From 59e4c9d418fd7e1524bdbf595774febd0645a7d2 Mon Sep 17 00:00:00 2001 From: Xiaoxia AI Date: Mon, 22 Jun 2026 18:36:56 +0800 Subject: [PATCH] ci(release): deploy production from runtime builder --- .gitea/workflows/deploy.yml | 75 ++++++++++++---------------- docs/GITEA-RUNTIME-BUILDER-RUNNER.md | 6 +-- tests/unit/test_release_scripts.py | 9 ++-- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 5ef487606..71761ae4e 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -148,57 +148,45 @@ jobs: deploy-production: name: Deploy Production - runs-on: ubuntu-latest - container: - image: docker:27-cli + runs-on: runtime-builder if: startsWith(github.ref, 'refs/tags/v') needs: build-production-runtime-images steps: - - name: Sync release artifact to production workspace + - name: Deploy production over SSH shell: sh + env: + PRODUCTION_SSH_HOST: ${{ secrets.PRODUCTION_SSH_HOST }} + PRODUCTION_SSH_USER: ${{ secrets.PRODUCTION_SSH_USER }} + PRODUCTION_SSH_KEY: ${{ secrets.PRODUCTION_SSH_KEY }} run: | set -eu - docker run --rm -e GITHUB_REF_NAME="${GITHUB_REF_NAME}" -v /:/host docker:27-cli sh -lc ' - set -eu - release_tar="/host/var/lib/xiaoxia-saas-production/release-${GITHUB_REF_NAME}.tar.gz" - test -f "$release_tar" - mkdir -p /host/var/lib/xiaoxia-saas-production - rm -rf /host/var/lib/xiaoxia-saas-production/repo - mkdir -p /host/var/lib/xiaoxia-saas-production/repo - tar -xzf "$release_tar" -C /host/var/lib/xiaoxia-saas-production/repo - test -f /host/var/lib/xiaoxia-saas-production/repo/apps/web/dist/index.html - ' - - - name: Verify production env file - shell: sh - run: | - set -eu - docker run --rm -v /:/host docker:27-cli sh -lc 'test -f /host/var/lib/xiaoxia-saas-production/.env' - - - name: Prepare production env - shell: sh - run: | - set -eu - docker run --rm -v /:/host docker:27-cli sh -lc 'cp /host/var/lib/xiaoxia-saas-production/.env /host/var/lib/xiaoxia-saas-production/repo/.env' - - - name: Deploy production stack - shell: sh - run: | - set -eu - docker run --rm \ - -e GITHUB_REF_NAME="${GITHUB_REF_NAME}" \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /:/host \ - docker:27-cli sh -lc ' - chmod +x /host/var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh && \ - RELEASE_VERSION="${GITHUB_REF_NAME}" /host/var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh - ' - - - name: Verify production health - shell: sh - run: | + production_host="${PRODUCTION_SSH_HOST:-47.98.113.167}" + production_user="${PRODUCTION_SSH_USER:-root}" + mkdir -p ~/.ssh + if [ -n "${PRODUCTION_SSH_KEY:-}" ]; then + key_path="$HOME/.ssh/id_ed25519" + printf '%s\n' "$PRODUCTION_SSH_KEY" > "$key_path" + chmod 600 "$key_path" + else + key_path="/root/.ssh/xiaoxia_runtime_builder" + test -f "$key_path" + fi + ssh-keyscan -H "$production_host" >> ~/.ssh/known_hosts + ssh -i "$key_path" "$production_user@$production_host" \ + "RELEASE_VERSION='${GITHUB_REF_NAME}' sh -s" <<'REMOTE_DEPLOY' set -eu + release_tar="/var/lib/xiaoxia-saas-production/release-${RELEASE_VERSION}.tar.gz" + test -f "$release_tar" + test -f "/var/lib/xiaoxia-saas-production/runtime-images-${RELEASE_VERSION}.tar" + mkdir -p /var/lib/xiaoxia-saas-production + rm -rf /var/lib/xiaoxia-saas-production/repo + mkdir -p /var/lib/xiaoxia-saas-production/repo + tar -xzf "$release_tar" -C /var/lib/xiaoxia-saas-production/repo + test -f /var/lib/xiaoxia-saas-production/repo/apps/web/dist/index.html + test -f /var/lib/xiaoxia-saas-production/.env + cp /var/lib/xiaoxia-saas-production/.env /var/lib/xiaoxia-saas-production/repo/.env + sh /var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh i=0 while [ "$i" -lt 30 ]; do if wget -qO- http://127.0.0.1:8001/health; then @@ -208,3 +196,4 @@ jobs: sleep 2 done exit 1 + REMOTE_DEPLOY diff --git a/docs/GITEA-RUNTIME-BUILDER-RUNNER.md b/docs/GITEA-RUNTIME-BUILDER-RUNNER.md index f4286925c..3acc49269 100644 --- a/docs/GITEA-RUNTIME-BUILDER-RUNNER.md +++ b/docs/GITEA-RUNTIME-BUILDER-RUNNER.md @@ -58,10 +58,10 @@ On `v*` tag push: 4. It builds `apps/web/dist` in a Node Docker container, not on production. 5. It packages `release-.tar.gz` with the prebuilt Web dist. 6. It uploads both `runtime-images-.tar` and `release-.tar.gz` to production. -7. `deploy-production` runs after the build job succeeds. -8. `deploy-production` extracts the prebuilt release artifact, loads `/var/lib/xiaoxia-saas-production/runtime-images-.tar`, and restarts API/Worker/Web. +7. `deploy-production` runs after the build job succeeds, on `runtime-builder`, and drives production over SSH. +8. `deploy-production` extracts the prebuilt release artifact on production, loads `/var/lib/xiaoxia-saas-production/runtime-images-.tar`, and restarts API/Worker/Web. -If either the release tar or runtime image tar is missing, production deploy must fail. +If either the release tar or runtime image tar is missing, production deploy must fail. The production deploy job intentionally also runs on `runtime-builder` because Gitea `needs` scheduling previously skipped the downstream `ubuntu-latest` deploy job after a successful cross-runner build. ## Preflight checks on runner diff --git a/tests/unit/test_release_scripts.py b/tests/unit/test_release_scripts.py index 54b5d0eec..3d9e84d70 100644 --- a/tests/unit/test_release_scripts.py +++ b/tests/unit/test_release_scripts.py @@ -17,7 +17,7 @@ def test_gitea_production_deploy_uses_production_ports(): assert "GITHUB_TOKEN: ${{ github.token }}" in workflow assert "WEB_PORT=3001" not in production_section assert "http://127.0.0.1:8001/health" in production_section - assert "RELEASE_VERSION=\"${GITHUB_REF_NAME}\"" in production_section + assert "RELEASE_VERSION='${GITHUB_REF_NAME}' sh -s" in production_section assert "http://127.0.0.1:8000/health" not in production_section @@ -173,9 +173,12 @@ def test_gitea_production_deploy_requires_runtime_builder_job(): assert "runtime-images-${GITHUB_REF_NAME}.tar" in build_section assert "release-${GITHUB_REF_NAME}.tar.gz" in build_section assert "needs: build-production-runtime-images" in production_section - assert "Sync release artifact to production workspace" in production_section - assert "release_tar=\"/host/var/lib/xiaoxia-saas-production/release-${GITHUB_REF_NAME}.tar.gz\"" in production_section + assert "runs-on: runtime-builder" in production_section + assert "Deploy production over SSH" in production_section + assert "release_tar=\"/var/lib/xiaoxia-saas-production/release-${RELEASE_VERSION}.tar.gz\"" in production_section + assert "runtime-images-${RELEASE_VERSION}.tar" in production_section assert "apps/web/dist/index.html" in production_section + assert "sh /var/lib/xiaoxia-saas-production/repo/infra/docker/deploy-production.sh" in production_section def test_build_host_runbook_requires_off_production_runtime_builds():