Deploy from CI with Infisical and ship Payload changes as migrations
Build and Deploy Next App / quality (push) Successful in 1m24s
Build and Deploy Next App / convex-deploy (push) Successful in 1m58s
Build and Deploy Next App / build-next (push) Successful in 5m32s

CI reads Infisical staging as the read-only convexmonorepo-ci machine identity, deploys the Convex functions, builds and pushes the image, and asks the VPS to redeploy. Payload schema changes now ship as migrations that production runs on startup; the baseline matches the production schema and is recorded as applied there. The local stack moves to its own ports and syncs the Convex runtime env, and the scripts pick up Infisical account switching, dev-kill and a cache-safe CI install.
This commit is contained in:
Gabriel Brown committed 2026-10-01 14:42:55 -04:00
1 parent f39376a887
commit 7d1884be41
23 files changed
+6235 -55

No files matched your search

+94 -19
View File
@@ -1,56 +1,131 @@
name: Build and Push Next App
name: Build and Deploy Next App
on:
push:
branches: [main]
branches:
- main
paths:
- 'apps/**'
- 'apps/next/**'
- 'packages/**'
- 'tools/**'
- 'scripts/**'
- 'docker/**'
- '.gitea/workflows/build-next.yml'
- '.infisical.json'
- 'package.json'
- 'bun.lock'
- 'turbo.json'
# One production deploy at a time; a newer push waits rather than racing.
concurrency:
group: build-next-production
cancel-in-progress: false
# Production env comes from the Infisical `staging` environment. Each job logs
# in as the read-only `convexmonorepo-ci` machine identity and writes the env to a
# temp file (scripts/ci-export-env.mjs), so no prod values live in Gitea.
jobs:
# Quality gate: workspace lint (sherif), eslint, typecheck and the unit,
# integration and component suites must pass before anything touches
# production. The local-stack e2e smoke check is skipped in CI.
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.10
- run: bun install --frozen-lockfile
- name: Lint, typecheck, and test
bun-version: 1.3.14
- name: Install dependencies
run: ./scripts/ci-install
- name: Lint and typecheck
env:
DOTENV_PROD: ${{ secrets.DOTENV_PROD }}
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
run: |
env_file="$(mktemp)"
trap 'rm -f "$env_file"' EXIT
printf '%s\n' "$DOTENV_PROD" > "$env_file"
node scripts/ci-export-env.mjs staging > "$env_file"
bunx dotenv -e "$env_file" -- env NODE_ENV=test SKIP_E2E=1 bun run ci:check
build-next:
# Pushes Convex functions and schema to prod before the image that depends on
# them is built. The env file must carry CONVEX_SELF_HOSTED_URL and
# CONVEX_SELF_HOSTED_ADMIN_KEY. Codegen is disabled: _generated is committed.
convex-deploy:
needs: [quality]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to container registry
run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.gbrown.org -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: Build image
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- name: Install dependencies
run: ./scripts/ci-install
- name: Deploy Convex functions
env:
DOTENV_PROD: ${{ secrets.DOTENV_PROD }}
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
run: |
env_file="$(mktemp)"
trap 'rm -f "$env_file"' EXIT
printf '%s\n' "$DOTENV_PROD" > "$env_file"
node scripts/ci-export-env.mjs staging > "$env_file"
cd packages/backend
bunx convex deploy --env-file "$env_file" --codegen disable --typecheck disable
build-next:
needs: [convex-deploy]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to container registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.gbrown.org -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: Build app using project script
env:
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
run: |
env_file="$(mktemp)"
trap 'rm -f "$env_file"' EXIT
node scripts/ci-export-env.mjs staging > "$env_file"
CI_ENV_FILE="$env_file" ./scripts/build-next-app staging
- name: Tag and push image
- name: Tag built image
run: |
docker tag convexmonorepo-next:latest git.gbrown.org/gib/convexmonorepo-next:${{ gitea.sha }}
docker tag convexmonorepo-next:latest git.gbrown.org/gib/convexmonorepo-next:latest
- name: Push image tags
run: |
docker push git.gbrown.org/gib/convexmonorepo-next:${{ gitea.sha }}
docker push git.gbrown.org/gib/convexmonorepo-next:latest
# Asks the VPS to pull the new image and restart podman-convexmonorepo.service
# through the forced-command deploy key (~/Server/AGENTS.md §15). A
# failure only warns: the image is pushed and the midnight
# podman-update.timer deploys it anyway.
- name: Trigger immediate deploy
run: |
install -m600 /dev/null "$HOME/.deploy_key"
printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > "$HOME/.deploy_key"
ssh -i "$HOME/.deploy_key" \
-o BatchMode=yes \
-o StrictHostKeyChecking=accept-new \
-o ConnectTimeout=15 \
[email protected] convexmonorepo \
|| echo "::warning::deploy trigger failed; nightly sweep will deploy instead"
rm -f "$HOME/.deploy_key"