diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 26046b985..bb3761b70 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -99,7 +99,25 @@ jobs: chmod +x scripts/build_release_images.sh scripts/build_release_images.sh "${GITHUB_REF_NAME}" - - name: Upload runtime image artifact + - name: Build web artifact + shell: sh + run: | + set -eu + docker run --rm \ + -v "$PWD:/workspace" \ + -w /workspace/apps/web \ + docker.m.daocloud.io/library/node:20 \ + sh -lc 'npm ci && npm run build' + + - name: Package release source artifact + shell: sh + run: | + set -eu + mkdir -p dist/release-artifacts + tar --exclude=.git --exclude=apps/web/node_modules \ + -czf "dist/release-artifacts/xiaoxia-release-${GITHUB_REF_NAME}.tar.gz" . + + - name: Upload runtime image and release artifacts shell: sh env: PRODUCTION_SSH_HOST: ${{ secrets.PRODUCTION_SSH_HOST }} @@ -121,6 +139,8 @@ jobs: ssh-keyscan -H "$production_host" >> ~/.ssh/known_hosts scp -i "$key_path" "dist/release-images/xiaoxia-runtime-images-${GITHUB_REF_NAME}.tar" \ "$production_user@$production_host:/var/lib/xiaoxia-saas-production/runtime-images-${GITHUB_REF_NAME}.tar" + scp -i "$key_path" "dist/release-artifacts/xiaoxia-release-${GITHUB_REF_NAME}.tar.gz" \ + "$production_user@$production_host:/var/lib/xiaoxia-saas-production/release-${GITHUB_REF_NAME}.tar.gz" deploy-production: name: Deploy Production @@ -131,29 +151,20 @@ jobs: needs: build-production-runtime-images steps: - - name: Checkout code + - name: Sync release artifact to production workspace shell: sh run: | set -eu - archive_url="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" - wget --header="Authorization: token ${GITHUB_TOKEN}" -O /tmp/repo.tar.gz "$archive_url" - tar -xzf /tmp/repo.tar.gz --strip-components=1 -C . - rm -f /tmp/repo.tar.gz - - - name: Sync code to production workspace - shell: sh - run: | - set -eu - tar --exclude=.git -cf - . | docker run --rm -i \ - -v /:/host \ - docker:27-cli \ - sh -lc ' - set -eu - 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 -xf - -C /host/var/lib/xiaoxia-saas-production/repo - ' + 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 @@ -172,6 +183,7 @@ jobs: 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 ' diff --git a/docs/GITEA-RUNTIME-BUILDER-RUNNER.md b/docs/GITEA-RUNTIME-BUILDER-RUNNER.md index 06452c5d5..9fb70b52e 100644 --- a/docs/GITEA-RUNTIME-BUILDER-RUNNER.md +++ b/docs/GITEA-RUNTIME-BUILDER-RUNNER.md @@ -54,11 +54,13 @@ On `v*` tag push: 1. `build-production-runtime-images` runs on `runtime-builder`. 2. It executes `scripts/build_release_images.sh `. -3. It uploads `dist/release-images/xiaoxia-runtime-images-.tar` to production. -4. `deploy-production` runs after the build job succeeds. -5. `deploy-production` loads `/var/lib/xiaoxia-saas-production/runtime-images-.tar` and restarts API/Worker/Web. +3. It builds `apps/web/dist` in a Node Docker container, not on production. +4. It packages `release-.tar.gz` with the prebuilt Web dist. +5. It uploads both `runtime-images-.tar` and `release-.tar.gz` to production. +6. `deploy-production` runs after the build job succeeds. +7. `deploy-production` extracts the prebuilt release artifact, loads `/var/lib/xiaoxia-saas-production/runtime-images-.tar`, and restarts API/Worker/Web. -If the tar is missing, production deploy must fail. +If either the release tar or runtime image tar is missing, production deploy must fail. ## Preflight checks on runner diff --git a/tests/unit/test_release_scripts.py b/tests/unit/test_release_scripts.py index 140e47667..4c27fef06 100644 --- a/tests/unit/test_release_scripts.py +++ b/tests/unit/test_release_scripts.py @@ -161,12 +161,19 @@ def test_gitea_production_deploy_requires_runtime_builder_job(): assert "runs-on: runtime-builder" in build_section assert "scripts/build_release_images.sh \"${GITHUB_REF_NAME}\"" in build_section + assert "docker.m.daocloud.io/library/node:20" in build_section + assert "npm ci && npm run build" in build_section + assert "dist/release-artifacts/xiaoxia-release-${GITHUB_REF_NAME}.tar.gz" in build_section assert "PRODUCTION_SSH_HOST" in build_section assert "PRODUCTION_SSH_USER" in build_section assert "PRODUCTION_SSH_KEY" in build_section assert "/root/.ssh/xiaoxia_runtime_builder" in build_section 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 "apps/web/dist/index.html" in production_section def test_build_host_runbook_requires_off_production_runtime_builds(): @@ -189,6 +196,8 @@ def test_runtime_builder_runner_runbook_matches_workflow(): assert "/root/.ssh/xiaoxia_runtime_builder" in runbook assert "Do not install this runner on the current production host" in runbook assert "production deploy waits for the image job" in runbook + assert "release-.tar.gz" in runbook + assert "prebuilt Web dist" in runbook def test_deployment_docs_forbid_production_runtime_builds():