Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM mcr.microsoft.com/devcontainers/javascript-node:22

# pnpm is pre-installed in the devcontainer image; ensure correct version
RUN corepack enable && corepack prepare pnpm@latest --activate

# mise for runtime management
RUN curl https://mise.run | sh && \
echo 'eval "$(~/.local/bin/mise activate bash)"' >> /home/node/.bashrc

# System dependencies for Synapse (uses Docker) and node-gyp
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
46 changes: 46 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "Boxel PR Review",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/boxel",

"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"dockerDashComposeVersion": "v2"
},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.11"
},
"ghcr.io/devcontainers/features/github-cli:1": {}
},

"forwardPorts": [4201, 4206, 8008],
"portsAttributes": {
"4201": { "label": "Realm Server", "onAutoForward": "silent" },
"4206": { "label": "Icons Server", "onAutoForward": "silent" },
"8008": { "label": "Matrix/Synapse", "onAutoForward": "silent" }
},

"postCreateCommand": "bash .devcontainer/setup.sh",
"postStartCommand": "bash .devcontainer/start-services.sh",

"customizations": {
"vscode": {
"extensions": [
"cardstack.boxel-tools",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"typed-ember.glint-vscode"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
}
}
},

"hostRequirements": {
"cpus": 4,
"memory": "8gb",
"storage": "64gb"
}
}
33 changes: 33 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspaces/boxel:cached
command: sleep infinity
environment:
- PGHOST=postgres
- PGPORT=5432
- PGUSER=postgres
- PGDATABASE=boxel
- NODE_NO_WARNINGS=1
depends_on:
postgres:
condition: service_healthy

postgres:
image: postgres:16.3
restart: unless-stopped
environment:
POSTGRES_HOST_AUTH_METHOD: trust
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 5s
timeout: 3s
retries: 5

volumes:
pgdata:
18 changes: 18 additions & 0 deletions .devcontainer/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
# One-time setup after the container is created.
# Runs during Codespace build (or prebuild) — keep it idempotent.
# The host app is NOT built here; it's deployed via GitHub Actions.
set -euo pipefail

echo "==> Installing dependencies..."
pnpm install --frozen-lockfile

echo "==> Running database migrations..."
cd packages/postgres
PGHOST="${PGHOST:-postgres}" PGPORT="${PGPORT:-5432}" pnpm migrate up
cd /workspaces/boxel

echo "==> Setting up skills realm..."
pnpm --dir=packages/skills-realm skills:setup

echo "==> Setup complete. Backend services will start automatically."
126 changes: 126 additions & 0 deletions .devcontainer/start-services.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/bin/bash
# Start backend services for PR review in Codespaces.
# The host app is NOT built here — a GitHub Actions workflow builds and
# deploys it to S3 with URLs pointing back at this Codespace.
set -euo pipefail

cd /workspaces/boxel

CODESPACE_NAME="${CODESPACE_NAME:?CODESPACE_NAME must be set}"
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN="${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN:-app.github.dev}"

# Derived Codespace URLs for forwarded ports
export REALM_SERVER_URL="https://${CODESPACE_NAME}-4201.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
export MATRIX_URL="https://${CODESPACE_NAME}-8008.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
export ICONS_URL="https://${CODESPACE_NAME}-4206.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"

# Common env vars for realm-server processes
export NODE_ENV=development
export NODE_NO_WARNINGS=1
export PGHOST="${PGHOST:-postgres}"
export PGPORT="${PGPORT:-5432}"
export PGDATABASE="${PGDATABASE:-boxel}"
export LOG_LEVELS='*=info'
export REALM_SERVER_SECRET_SEED="mum's the word"
export REALM_SECRET_SEED="shhh! it's a secret"
export REALM_SERVER_MATRIX_USERNAME=realm_server
export ENABLE_FILE_WATCHER=true

# ── Postgres is already running via docker-compose ──

# ── Make forwarded ports public so the S3 preview can reach them ──
echo "==> Making forwarded ports public..."
gh codespace ports visibility 4201:public 4206:public 8008:public -c "$CODESPACE_NAME" 2>/dev/null || true

# ── Matrix/Synapse ──
echo "==> Starting Matrix/Synapse..."
(cd packages/matrix && MATRIX_URL=http://localhost:8008 pnpm assert-synapse-running) &
SYNAPSE_PID=$!

# ── SMTP (MailHog) ──
echo "==> Starting SMTP server..."
(cd packages/matrix && pnpm assert-smtp-running) &

# ── Icons server ──
echo "==> Starting icons server..."
pnpm --dir=packages/realm-server run start:icons &

# ── Prerender service ──
echo "==> Starting prerender services..."
pnpm --dir=packages/realm-server run start:prerender-dev &
pnpm --dir=packages/realm-server run start:prerender-manager-dev &

# ── Worker ──
echo "==> Starting worker..."
pnpm --dir=packages/realm-server run start:worker-development &

# Wait for Synapse before starting the realm server
wait $SYNAPSE_PID || true

# ── Realm server ──
echo "==> Starting realm server..."
SKIP_EXPERIMENTS=true \
SKIP_BOXEL_HOMEPAGE=true \
SKIP_SUBMISSION=true \
MATRIX_URL=http://localhost:8008 \
pnpm --dir=packages/realm-server ts-node \
--transpileOnly main \
--port=4201 \
--matrixURL=http://localhost:8008 \
--realmsRootPath=./realms/codespaces \
--prerendererUrl=http://localhost:4221 \
--migrateDB \
--workerManagerPort=4213 \
\
--path='../base' \
--username='base_realm' \
--fromUrl='https://cardstack.com/base/' \
--toUrl="${REALM_SERVER_URL}/base/" \
\
--path='../catalog-realm' \
--username='catalog_realm' \
--fromUrl='@cardstack/catalog/' \
--toUrl="${REALM_SERVER_URL}/catalog/" \
\
--path='../skills-realm/contents' \
--username='skills_realm' \
--fromUrl="${REALM_SERVER_URL}/skills/" \
--toUrl="${REALM_SERVER_URL}/skills/" \
\
--path='../catalog-new/contents' \
--username='catalog_new_realm' \
--fromUrl="${REALM_SERVER_URL}/catalog-new/" \
--toUrl="${REALM_SERVER_URL}/catalog-new/" &
REALM_PID=$!

# ── Wait for realm server readiness ──
echo "==> Waiting for realm server to be ready..."
timeout 300 bash -c \
'until curl -sf "http://localhost:4201/_readiness-check?acceptHeader=application%2Fvnd.api%2Bjson" >/dev/null 2>&1; do sleep 2; done' \
|| echo "Warning: realm server readiness check timed out after 5 minutes"

# ── Trigger host preview build via GitHub Actions ──
echo "==> Triggering host preview build pointed at this Codespace..."
BRANCH_NAME="$(git rev-parse --abbrev-ref HEAD)"
gh workflow run codespaces-preview.yml \
--ref "$BRANCH_NAME" \
-f codespace_name="$CODESPACE_NAME" \
-f realm_server_url="$REALM_SERVER_URL" \
-f matrix_url="$MATRIX_URL" \
-f icons_url="$ICONS_URL" \
|| echo "Warning: could not trigger preview build. Run manually with: gh workflow run codespaces-preview.yml"

echo ""
echo "============================================"
echo " Backend services running!"
echo ""
echo " Realm server: ${REALM_SERVER_URL}"
echo " Matrix: ${MATRIX_URL}"
echo " Icons: ${ICONS_URL}"
echo ""
echo " Host preview build triggered — check the"
echo " PR for a preview link once it completes."
echo "============================================"

# Keep the script alive
wait
106 changes: 106 additions & 0 deletions .github/workflows/codespaces-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Codespaces Preview

on:
workflow_dispatch:
inputs:
codespace_name:
description: "Codespace name (used to derive forwarded port URLs)"
required: true
type: string
realm_server_url:
description: "Codespace realm server URL (e.g. https://<name>-4201.app.github.dev)"
required: true
type: string
matrix_url:
description: "Codespace Matrix URL (e.g. https://<name>-8008.app.github.dev)"
required: true
type: string
icons_url:
description: "Codespace icons URL (e.g. https://<name>-4206.app.github.dev)"
required: true
type: string

permissions:
contents: read
issues: write
checks: write
pull-requests: write
id-token: write
statuses: write

jobs:
deploy-codespaces-preview:
name: Build and deploy host preview for Codespaces
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2

- uses: ./.github/actions/init

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 # 4.1.0
with:
role-to-assume: arn:aws:iam::680542703984:role/boxel-host
aws-region: us-east-1

- name: Set environment variables for Codespaces backend
shell: bash
run: |
# Point the host build at the Codespace's backend services
echo "REALM_SERVER_DOMAIN=${{ inputs.realm_server_url }}/" >> $GITHUB_ENV
echo "RESOLVED_BASE_REALM_URL=${{ inputs.realm_server_url }}/base/" >> $GITHUB_ENV
echo "RESOLVED_CATALOG_REALM_URL=${{ inputs.realm_server_url }}/catalog/" >> $GITHUB_ENV
echo "RESOLVED_NEW_CATALOG_REALM_URL=${{ inputs.realm_server_url }}/catalog-new/" >> $GITHUB_ENV
echo "RESOLVED_SKILLS_REALM_URL=${{ inputs.realm_server_url }}/skills/" >> $GITHUB_ENV
echo "MATRIX_URL=${{ inputs.matrix_url }}" >> $GITHUB_ENV
echo "MATRIX_SERVER_NAME=localhost" >> $GITHUB_ENV
echo "ICONS_URL=${{ inputs.icons_url }}" >> $GITHUB_ENV

- name: Set PR branch name for S3 prefix
shell: bash
run: |
RAW_BRANCH="${GITHUB_REF_NAME}"
echo "PR_BRANCH_NAME=$(echo "${RAW_BRANCH}" | tr _ - | tr '[:upper:]' '[:lower:]' | sed -e 's/-$//' | sed -e 's/[^a-z0-9\-]//g' | cut -c1-60)" >> $GITHUB_ENV

- name: Build and deploy preview
shell: bash
env:
S3_PREVIEW_BUCKET_NAME: boxel-host-preview.stack.cards
AWS_S3_BUCKET: boxel-host-preview.stack.cards
AWS_REGION: us-east-1
AWS_CLOUDFRONT_DISTRIBUTION: EU4RGLH4EOCHJ
run: pnpm deploy:boxel-host:preview-staging

- name: Store preview URL
shell: bash
run: echo "PREVIEW_HOST=https://${PR_BRANCH_NAME}.boxel-host-preview.stack.cards/" >> $GITHUB_ENV

- name: Post status check with preview link
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
HEAD_SHA: ${{ github.sha }}
run: |
curl \
-X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$REPOSITORY/statuses/$HEAD_SHA" \
-d '{"context":"Preview boxel-host codespaces","description":"Host preview connected to Codespace backend","target_url":"'"$PREVIEW_HOST"'","state":"success"}'

- name: Find associated PR and comment
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_NUMBER=$(gh pr list --head "${GITHUB_REF_NAME}" --json number --jq '.[0].number' 2>/dev/null || true)
if [ -n "$PR_NUMBER" ]; then
gh pr comment "$PR_NUMBER" --body "### Codespaces Preview

- [Host preview (connected to Codespace)]($PREVIEW_HOST)
- Realm server: ${{ inputs.realm_server_url }}
- Matrix: ${{ inputs.matrix_url }}

> This preview build is connected to the Codespace \`${{ inputs.codespace_name }}\`. The preview will stop working when the Codespace is stopped."
fi
10 changes: 10 additions & 0 deletions packages/host/config/codespaces.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# These values are overridden at build time by the codespaces-preview workflow.
# They serve as documentation of what's needed.
REALM_SERVER_DOMAIN=http://localhost:4201/
RESOLVED_BASE_REALM_URL=http://localhost:4201/base/
RESOLVED_CATALOG_REALM_URL=http://localhost:4201/catalog/
RESOLVED_NEW_CATALOG_REALM_URL=http://localhost:4201/catalog-new/
RESOLVED_SKILLS_REALM_URL=http://localhost:4201/skills/
MATRIX_URL=http://localhost:8008
MATRIX_SERVER_NAME=localhost
ICONS_URL=http://localhost:4206
Loading