From 0e7f61f39fdb549a05f7536d7308936a4e376023 Mon Sep 17 00:00:00 2001 From: hexin-codex-bot Date: Sun, 4 Oct 2026 21:35:32 +0800 Subject: [PATCH] ci: restore persistent production deployment --- .gitea/workflows/ci.yml | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .gitea/workflows/ci.yml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..e94ba08 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,70 @@ +name: Verify and deploy production + +concurrency: + group: rednote-tool-studio-production + cancel-in-progress: false + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +jobs: + verify-and-deploy: + runs-on: ubuntu-latest + container: node:24-bookworm-slim + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: npm ci + + - name: Type check + run: npm run typecheck + + - name: Unit tests + run: npm test + + - name: Production build + run: npm run build + + - name: Deploy persistent production container + if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' + env: + STUDIO_USERNAME: studio + STUDIO_PASSWORD: ${{ secrets.STUDIO_PASSWORD }} + DEPLOY_PORT: ${{ vars.DEPLOY_PORT }} + DEPLOY_DOCKERFILE: Dockerfile.deploy + GITHUB_SHA: ${{ gitea.sha }} + run: | + if [ -z "$STUDIO_PASSWORD" ]; then + echo "STUDIO_PASSWORD repository secret is required for production deployment" + exit 1 + fi + node scripts/deploy.mjs + + - name: Verify persistent deployment settings + if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main' + run: | + node --input-type=module <<'JS' + import Docker from "dockerode"; + + const docker = new Docker({ socketPath: "/var/run/docker.sock" }); + const container = docker.getContainer("rednote-tool-studio"); + const state = await container.inspect(); + const restartPolicy = state.HostConfig.RestartPolicy?.Name; + const health = state.State.Health?.Status; + const dataMount = state.Mounts?.find((mount) => mount.Destination === "/app/data/projects"); + + if (!state.State.Running) throw new Error("Production container is not running"); + if (health !== "healthy") throw new Error(`Production container health is ${health || "unavailable"}`); + if (restartPolicy !== "unless-stopped") throw new Error(`Unexpected restart policy: ${restartPolicy || "none"}`); + if (dataMount?.Type !== "volume" || dataMount.Name !== "rednote-tool-studio-data") { + throw new Error("Persistent project data volume is not mounted correctly"); + } + + console.log(`Persistent deployment verified: health=${health}, restart=${restartPolicy}, volume=${dataMount.Name}`); + JS