From 64c02ea5a6f91a7a27a61406b0486c3febf29307 Mon Sep 17 00:00:00 2001 From: Duy /zuey/ Date: Sun, 31 May 2026 21:47:28 +0700 Subject: [PATCH] ci: speed up zuey beta deploy (#127) * ci: speed up zuey beta deploy * ci: mark issue 88 plan complete --- .github/workflows/dev-beta-release.yaml | 149 ++++++++++++++++-- docs/deployment-guide.md | 4 +- docs/project-changelog.md | 14 ++ .../phase-01-scout-and-design.md | 42 +++++ .../phase-02-refactor-dev-beta-workflow.md | 61 +++++++ .../phase-03-validate-workflow.md | 48 ++++++ .../phase-04-ship-beta-pr.md | 47 ++++++ .../plan.md | 49 ++++++ scripts/ci/dev-beta-release-workflow.test.mjs | 61 +++++++ 9 files changed, 460 insertions(+), 15 deletions(-) create mode 100644 plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-01-scout-and-design.md create mode 100644 plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-02-refactor-dev-beta-workflow.md create mode 100644 plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-03-validate-workflow.md create mode 100644 plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-04-ship-beta-pr.md create mode 100644 plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/plan.md create mode 100644 scripts/ci/dev-beta-release-workflow.test.mjs diff --git a/.github/workflows/dev-beta-release.yaml b/.github/workflows/dev-beta-release.yaml index a981a8e5..c2d107f8 100644 --- a/.github/workflows/dev-beta-release.yaml +++ b/.github/workflows/dev-beta-release.yaml @@ -8,10 +8,6 @@ on: permissions: contents: read -concurrency: - group: dev-beta-release-${{ github.ref }} - cancel-in-progress: false - env: GHCR_IMAGE: ghcr.io/${{ github.repository }} DOCKERHUB_IMAGE: digitop/goclaw @@ -80,6 +76,9 @@ jobs: needs: [go, web] if: github.ref == 'refs/heads/dev' runs-on: ubuntu-latest + concurrency: + group: dev-beta-version-${{ github.ref }} + cancel-in-progress: false permissions: contents: write outputs: @@ -91,8 +90,8 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Release versioning tests - run: node --test scripts/ci/semantic-beta-version.test.mjs + - name: Release workflow tests + run: node --test scripts/ci/semantic-beta-version.test.mjs scripts/ci/dev-beta-release-workflow.test.mjs - name: Fetch upstream release tags run: git fetch --force --tags https://github.com/nextlevelbuilder/goclaw.git "refs/tags/v*:refs/tags/v*" - name: Compute semantic beta version @@ -118,7 +117,49 @@ jobs: name: release-notes path: ${{ steps.version.outputs.notes_path }} - build_binaries: + build_zuey_binary: + needs: beta_version + if: needs.beta_version.outputs.released == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.beta_version.outputs.tag }} + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache-dependency-path: go.sum + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Build web UI + run: | + corepack enable && corepack prepare pnpm@10.28.2 --activate + cd ui/web && pnpm install --frozen-lockfile && pnpm build && cd ../.. + mkdir -p internal/webui/dist + cp -r ui/web/dist/* internal/webui/dist/ + + - name: Build binary + env: + GOOS: linux + GOARCH: amd64 + VERSION: ${{ needs.beta_version.outputs.tag }} + run: | + CGO_ENABLED=0 go build -tags embedui \ + -ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \ + -o goclaw . + tar -czf "goclaw-${VERSION}-linux-amd64.tar.gz" goclaw migrations/ skills/ + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: binary-linux-amd64 + path: goclaw-*.tar.gz + + build_remaining_binaries: needs: beta_version if: needs.beta_version.outputs.released == 'true' runs-on: ubuntu-latest @@ -126,8 +167,6 @@ jobs: fail-fast: false matrix: include: - - goos: linux - goarch: amd64 - goos: linux goarch: arm64 steps: @@ -169,18 +208,17 @@ jobs: path: goclaw-*.tar.gz publish_release: - needs: [beta_version, build_binaries, promote_beta_aliases] + needs: [beta_version, build_zuey_binary] if: needs.beta_version.outputs.released == 'true' runs-on: ubuntu-latest permissions: contents: write steps: - - name: Download all artifacts + - name: Download zuey release artifact uses: actions/download-artifact@v4 with: - pattern: binary-* + name: binary-linux-amd64 path: artifacts - merge-multiple: true - name: Download release notes uses: actions/download-artifact@v4 @@ -208,6 +246,29 @@ jobs: fi gh release upload "$TAG" artifacts/* --clobber + complete_release_artifacts: + needs: [beta_version, publish_release, build_zuey_binary, build_remaining_binaries] + if: needs.beta_version.outputs.released == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download all binary artifacts + uses: actions/download-artifact@v4 + with: + pattern: binary-* + path: artifacts + merge-multiple: true + + - name: Refresh prerelease assets + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + TAG: ${{ needs.beta_version.outputs.tag }} + run: | + (cd artifacts && sha256sum goclaw-*.tar.gz > CHECKSUMS.sha256) + gh release upload "$TAG" artifacts/* --clobber + docker_images: needs: beta_version if: needs.beta_version.outputs.released == 'true' @@ -295,6 +356,9 @@ jobs: needs: [beta_version, docker_images] if: needs.beta_version.outputs.released == 'true' runs-on: ubuntu-latest + concurrency: + group: dev-beta-promote-aliases + cancel-in-progress: false permissions: contents: read packages: write @@ -302,9 +366,34 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} steps: + - name: Check latest beta tag + id: beta_freshness + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ needs.beta_version.outputs.tag }} + run: | + latest_beta="$( + gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/tags/v" --paginate \ + --jq '.[].ref | select(test("^refs/tags/v[0-9]+\\.[0-9]+\\.[0-9]+-beta\\.[0-9]+$")) | sub("^refs/tags/"; "")' \ + | sort -V \ + | tail -n 1 + )" + if [[ -z "$latest_beta" ]]; then + echo "::error::No beta tags found; refusing to promote aliases" + exit 1 + fi + if [[ "$latest_beta" != "$TAG" ]]; then + echo "::notice::Skipping stale beta alias promotion for ${TAG}; latest beta is ${latest_beta}" + echo "current=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "current=true" >> "$GITHUB_OUTPUT" + - uses: docker/setup-buildx-action@v3 + if: steps.beta_freshness.outputs.current == 'true' - name: Log in to GHCR + if: steps.beta_freshness.outputs.current == 'true' uses: docker/login-action@v3 with: registry: ghcr.io @@ -312,13 +401,14 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Log in to Docker Hub - if: env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' + if: steps.beta_freshness.outputs.current == 'true' && env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' uses: docker/login-action@v3 with: username: ${{ env.DOCKERHUB_USERNAME }} password: ${{ env.DOCKERHUB_TOKEN }} - name: Promote beta aliases + if: steps.beta_freshness.outputs.current == 'true' env: TAG: ${{ needs.beta_version.outputs.tag }} run: | @@ -336,6 +426,9 @@ jobs: if: needs.beta_version.outputs.released == 'true' && github.repository == 'digitopvn/goclaw' && github.repository != 'nextlevelbuilder/goclaw' runs-on: ubuntu-latest timeout-minutes: 20 + concurrency: + group: dev-beta-zuey-deploy + cancel-in-progress: false permissions: contents: read env: @@ -348,7 +441,30 @@ jobs: ZUEY_SSH_PORT: ${{ vars.ZUEY_SSH_PORT || '2233' }} ZUEY_SSH_USER: ${{ vars.ZUEY_SSH_USER || 'zuey' }} steps: + - name: Check latest beta tag + id: beta_freshness + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + latest_beta="$( + gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/tags/v" --paginate \ + --jq '.[].ref | select(test("^refs/tags/v[0-9]+\\.[0-9]+\\.[0-9]+-beta\\.[0-9]+$")) | sub("^refs/tags/"; "")' \ + | sort -V \ + | tail -n 1 + )" + if [[ -z "$latest_beta" ]]; then + echo "::error::No beta tags found; refusing to deploy" + exit 1 + fi + if [[ "$latest_beta" != "$TAG" ]]; then + echo "::notice::Skipping stale zuey deploy for ${TAG}; latest beta is ${latest_beta}" + echo "current=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "current=true" >> "$GITHUB_OUTPUT" + - name: Validate deploy configuration + if: steps.beta_freshness.outputs.current == 'true' run: | missing=0 for name in GOCLAW_DEPLOY_URL GOCLAW_GATEWAY_TOKEN GOCLAW_UPGRADE_TOKEN TAG; do @@ -360,11 +476,13 @@ jobs: exit "$missing" - name: Checkout repository (for VPS script sync) + if: steps.beta_freshness.outputs.current == 'true' uses: actions/checkout@v4 with: ref: ${{ needs.beta_version.outputs.tag }} - name: Sync zuey ops scripts to VPS + if: steps.beta_freshness.outputs.current == 'true' env: ZUEY_SSH_PRIVATE_KEY_B64: ${{ secrets.ZUEY_SSH_PRIVATE_KEY_B64 }} ZUEY_SUDO_PASS: ${{ secrets.ZUEY_SUDO_PASS }} @@ -441,6 +559,7 @@ jobs: shred -u ~/.ssh/id_zuey 2>/dev/null || rm -f ~/.ssh/id_zuey - name: Trigger zuey gateway upgrade + if: steps.beta_freshness.outputs.current == 'true' run: | base_url="${GOCLAW_DEPLOY_URL%/}" body="$(mktemp)" @@ -462,6 +581,7 @@ jobs: cat "$body" - name: Wait for zuey gateway upgrade + if: steps.beta_freshness.outputs.current == 'true' run: | base_url="${GOCLAW_DEPLOY_URL%/}" for attempt in {1..90}; do @@ -496,6 +616,7 @@ jobs: exit 1 - name: Verify public health + if: steps.beta_freshness.outputs.current == 'true' run: | base_url="${GOCLAW_DEPLOY_URL%/}" health_json="$(curl -fsS --retry 5 --retry-delay 3 "${base_url}/health")" diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md index ecfacde3..df34ccdb 100644 --- a/docs/deployment-guide.md +++ b/docs/deployment-guide.md @@ -190,7 +190,9 @@ The remote trigger endpoint fails closed unless `GOCLAW_UPGRADE_TRIGGER_TOKEN` i ### Automatic Beta Deploy From `dev` -Pushing or merging into `dev` runs `.github/workflows/dev-beta-release.yaml`. After Go/Web checks pass, the workflow creates the next semantic beta tag, publishes the prerelease assets, promotes beta Docker aliases, then deploys that exact beta tag to the zuey VPS through the gateway upgrade endpoint. +Pushing or merging into `dev` runs `.github/workflows/dev-beta-release.yaml`. After Go/Web checks pass, the workflow creates the next semantic beta tag, publishes the linux amd64 prerelease asset and checksum, then deploys that exact beta tag to the zuey VPS through the gateway upgrade endpoint. + +Linux arm64 release assets, full checksums, multi-arch Docker images, and beta Docker aliases continue after the fast zuey deploy path. Those completion jobs are still required to pass; the workflow remains failed if later artifact completion breaks. Zuey deploy and Docker beta alias promotion both skip stale beta tags so older runs cannot roll back a newer beta. Required GitHub Actions configuration: diff --git a/docs/project-changelog.md b/docs/project-changelog.md index 616a38f8..6cb78162 100644 --- a/docs/project-changelog.md +++ b/docs/project-changelog.md @@ -6,6 +6,20 @@ Significant changes, features, and fixes in reverse chronological order. ## 2026-05-31 +### CI/CD: fast zuey beta deploy (issue #88) + +**Changes** + +- Split the dev beta release workflow so zuey deploy starts after the linux amd64 prerelease asset is published, instead of waiting for Docker multi-arch builds and beta alias promotion. +- Added stale beta tag guards for zuey deploy and Docker beta alias promotion so older runs cannot roll back a newer beta. +- Kept linux arm64 binaries, refreshed checksums, multi-arch Docker images, and beta aliases as required post-deploy completion jobs. + +**Tests** + +- Added a workflow-structure test covering the fast deploy graph, artifact completion graph, and stale-tag guards. + +--- + ### Provider fallback content-policy recovery **Fixes** diff --git a/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-01-scout-and-design.md b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-01-scout-and-design.md new file mode 100644 index 00000000..046e7512 --- /dev/null +++ b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-01-scout-and-design.md @@ -0,0 +1,42 @@ +--- +phase: 1 +title: Scout and design +status: completed +priority: P1 +effort: 30m +dependencies: [] +--- + +# Phase 1: Scout and design + +## Overview + +Verify current workflow graph, zuey asset contract, and overlapping plans before editing CI. + +## Requirements + +- Functional: document the exact current blockers for zuey deploy. +- Non-functional: plan must be grounded in live workflow code and recent run timing. + +## Architecture + +Current path: `go/web -> beta_version -> build_binaries + docker_images -> promote_beta_aliases -> publish_release -> deploy_zuey_beta`. + +Fast path target: `go/web -> beta_version -> build_zuey_binary -> publish_release -> deploy_zuey_beta`. + +## Implementation Steps + +1. Read `README.md`, `CLAUDE.md`, `.github/workflows/dev-beta-release.yaml`, and `scripts/zuey/goclaw-upgrade-release.sh`. +2. Check issue #88 and recent workflow job timings with `gh`. +3. Check unfinished plans for overlapping CI release work. +4. Verify zuey only requires linux amd64 release tarball and checksum/digest. + +## Success Criteria + +- [ ] Workflow dependency bottleneck identified with file/line refs. +- [ ] Zuey asset requirement identified with file/line refs. +- [ ] No overlapping active plan blocks this work. + +## Risk Assessment + +Risk: publishing partial prerelease could confuse consumers. Mitigation: follow-up job refreshes release assets/checksums and workflow stays failed if completion breaks. diff --git a/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-02-refactor-dev-beta-workflow.md b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-02-refactor-dev-beta-workflow.md new file mode 100644 index 00000000..536821d4 --- /dev/null +++ b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-02-refactor-dev-beta-workflow.md @@ -0,0 +1,61 @@ +--- +phase: 2 +title: Refactor dev beta workflow +status: completed +priority: P1 +effort: 1h +dependencies: + - 1 +--- + +# Phase 2: Refactor dev beta workflow + +## Overview + +Refactor `.github/workflows/dev-beta-release.yaml` so the release needed by zuey is published before slow Docker multi-arch jobs finish. + +## Requirements + +- Functional: split amd64 release publishing from full artifact completion. +- Non-functional: keep all current artifact families and registry publishing. + +## Architecture + +Use two binary jobs: +- `build_zuey_binary`: linux amd64 only, feeds the first prerelease publish. +- `build_remaining_binaries`: linux arm64, feeds final release artifact completion. + +Keep Docker jobs parallel after `beta_version`. Keep `promote_beta_aliases` after Docker. Add a release completion job that re-uploads all binary assets and full `CHECKSUMS.sha256`. + +Move from workflow-level concurrency to job-level concurrency only where mutation ordering matters. Add stale beta tag guards before zuey deploy and beta alias promotion. + +## Related Code Files + +- Modify: `.github/workflows/dev-beta-release.yaml` +- Modify: `docs/deployment-guide.md` +- Modify: `docs/project-changelog.md` +- Create: `scripts/ci/dev-beta-release-workflow.test.mjs` +- Delete: none + +## Implementation Steps + +1. Rename/split existing `build_binaries` into amd64 fast job and remaining arch job. +2. Change `publish_release.needs` to `[beta_version, build_zuey_binary]`. +3. Download only `binary-linux-amd64` in `publish_release`. +4. Add `complete_release_artifacts` needing `[beta_version, publish_release, build_zuey_binary, build_remaining_binaries]`. +5. Keep `docker_images` and `promote_beta_aliases` unchanged except not gating deploy. +6. Keep `deploy_zuey_beta.needs` as `[beta_version, publish_release]`. +7. Remove top-level workflow concurrency; add job-level concurrency and stale-tag guard for deploy/alias mutation jobs. +8. Update deployment docs and changelog for the new order. + +## Success Criteria + +- [ ] `deploy_zuey_beta` critical path excludes Docker jobs. +- [ ] Full release upload still includes amd64 + arm64 tarballs and refreshed checksum. +- [ ] Docker beta aliases still promote after multi-arch images. +- [ ] Stale beta deploy/alias promotion is skipped. +- [ ] No secret, branch, or repository guard is weakened. + +## Risk Assessment + +Risk: two jobs uploading to same release can race. Mitigation: `complete_release_artifacts` depends on `publish_release` and uses `gh release upload --clobber` after the fast publish. diff --git a/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-03-validate-workflow.md b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-03-validate-workflow.md new file mode 100644 index 00000000..daa06fae --- /dev/null +++ b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-03-validate-workflow.md @@ -0,0 +1,48 @@ +--- +phase: 3 +title: Validate workflow +status: completed +priority: P1 +effort: 30m +dependencies: + - 2 +--- + +# Phase 3: Validate workflow + +## Overview + +Validate YAML syntax, dependency graph intent, and changed workflow blast radius. + +## Requirements + +- Functional: prove workflow file is syntactically valid and dependency graph matches acceptance. +- Non-functional: avoid running heavy Docker builds locally. + +## Architecture + +Local validation is static because GitHub-hosted runner context/secrets are required for full execution. + +## Related Code Files + +- Read: `.github/workflows/dev-beta-release.yaml` +- Read: `docs/deployment-guide.md` + +## Implementation Steps + +1. Parse workflow YAML with Ruby `YAML.safe_load` or available equivalent. +2. Inspect changed job `needs` and artifact names. +3. Add and run workflow-structure test for fast path, completion path, and stale guard. +4. Run lightweight repo checks that do not require services when feasible. +5. Ask tester/reviewer agents to validate regression risks. + +## Success Criteria + +- [ ] YAML parser accepts workflow. +- [ ] Static grep confirms deploy depends only on fast release publish. +- [ ] Node workflow-structure test passes. +- [ ] Review finds no blocker for release/deploy contract. + +## Risk Assessment + +Risk: static validation misses GitHub expression mistakes. Mitigation: keep expressions simple and reuse existing artifact/action patterns. diff --git a/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-04-ship-beta-pr.md b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-04-ship-beta-pr.md new file mode 100644 index 00000000..21e45a6d --- /dev/null +++ b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/phase-04-ship-beta-pr.md @@ -0,0 +1,47 @@ +--- +phase: 4 +title: Ship beta PR +status: completed +priority: P2 +effort: 30m +dependencies: + - 3 +--- + +# Phase 4: Ship beta PR + +## Overview + +Ship the workflow refactor to `dev` via PR and preserve beta release observability. + +## Requirements + +- Functional: commit, push branch, create PR into `dev`, link issue #88. +- Non-functional: do not merge automatically; beta workflow runs after PR merge to `dev`. + +## Architecture + +Use normal PR shipping. After merge, the first `dev` push is the real validation for zuey time-to-deploy and full artifact completion. + +## Related Code Files + +- Commit: `.github/workflows/dev-beta-release.yaml` +- Commit: `plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/*` + +## Implementation Steps + +1. Run final `git diff --check` and workflow validation. +2. Commit with conventional message. +3. Push `codex/issue-88-fast-zuey-beta-deploy-ci`. +4. Create PR to `dev`, mention `Fixes #88`. +5. Report post-merge watch command for first real beta run. + +## Success Criteria + +- [ ] Branch pushed to origin. +- [ ] PR opened against `dev`. +- [ ] PR body explains fast path and artifact completion path. + +## Risk Assessment + +Risk: `dev` workflow behavior cannot be fully proven before merge. Mitigation: PR includes expected job-order evidence and first-run watch steps. diff --git a/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/plan.md b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/plan.md new file mode 100644 index 00000000..176cc0ec --- /dev/null +++ b/plans/260531-2101-issue-88-fast-zuey-beta-deploy-ci/plan.md @@ -0,0 +1,49 @@ +--- +title: Issue 88 fast zuey beta deploy CI +description: >- + Refactor dev beta release workflow so zuey deploy starts after the linux amd64 + release asset is published, while arm64 and Docker artifacts still complete + afterward. +status: completed +priority: P2 +branch: codex/issue-88-fast-zuey-beta-deploy-ci +tags: [] +blockedBy: [] +blocks: [] +created: '2026-05-31T14:01:31.321Z' +createdBy: 'ck:plan' +source: skill +--- + +# Issue 88 fast zuey beta deploy CI + +## Overview + +Issue #88 reports 30-40m dev beta CI. Recent run `26713221199` spent ~24m in Docker multi-arch while zuey deploy waited behind Docker promotion. Zuey deploy only needs the GitHub Release linux amd64 tarball, verified by `scripts/zuey/goclaw-upgrade-release.sh`. + +Goal: optimize time-to-zuey deployed. Do not weaken Go/Web gates. Do not drop arm64, Docker `latest/full`, beta aliases, or checksums; only move them off the zuey critical path. + +## Phases + +| Phase | Name | Status | +|-------|------|--------| +| 1 | [Scout and design](./phase-01-scout-and-design.md) | Completed | +| 2 | [Refactor dev beta workflow](./phase-02-refactor-dev-beta-workflow.md) | Completed | +| 3 | [Validate workflow](./phase-03-validate-workflow.md) | Completed | +| 4 | [Ship beta PR](./phase-04-ship-beta-pr.md) | Completed | + +## Dependencies + +- Base branch: `origin/dev` +- Primary files: `.github/workflows/dev-beta-release.yaml`, this plan +- GitHub issue: `digitopvn/goclaw#88` + +## Acceptance Criteria + +- `deploy_zuey_beta` no longer waits for `docker_images` or `promote_beta_aliases`. +- Workflow-level concurrency no longer serializes a new dev push behind slow artifact completion from the previous push. +- Zuey deploy and Docker beta alias promotion skip stale beta tags instead of rolling back to an older tag. +- The prerelease is published with `goclaw-${TAG}-linux-amd64.tar.gz` and `CHECKSUMS.sha256` before deploy. +- Linux arm64 binary, multi-arch Docker `latest/full`, and beta aliases still run after `beta_version` and keep workflow failure visible if broken. +- Workflow YAML parses and preserves repo guard `github.repository == 'digitopvn/goclaw'`. +- PR targets `dev` and links issue #88. diff --git a/scripts/ci/dev-beta-release-workflow.test.mjs b/scripts/ci/dev-beta-release-workflow.test.mjs new file mode 100644 index 00000000..0f0b2e79 --- /dev/null +++ b/scripts/ci/dev-beta-release-workflow.test.mjs @@ -0,0 +1,61 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(here, "../.."); +const workflow = readFileSync(join(repoRoot, ".github/workflows/dev-beta-release.yaml"), "utf8"); + +function jobBlock(name) { + const lines = workflow.split("\n"); + const start = lines.findIndex((line) => line === ` ${name}:`); + assert.notEqual(start, -1, `missing job ${name}`); + + let end = lines.length; + for (let index = start + 1; index < lines.length; index += 1) { + if (/^ [A-Za-z0-9_]+:$/.test(lines[index])) { + end = index; + break; + } + } + return lines.slice(start + 1, end).join("\n"); +} + +test("dev beta workflow does not serialize fast deploy behind full artifact completion", () => { + assert.doesNotMatch(workflow, /^concurrency:\n/m); + + const publishRelease = jobBlock("publish_release"); + assert.match(publishRelease, /needs: \[beta_version, build_zuey_binary\]/); + assert.doesNotMatch(publishRelease, /promote_beta_aliases|docker_images|build_remaining_binaries/); + + const deployZuey = jobBlock("deploy_zuey_beta"); + assert.match(deployZuey, /needs: \[beta_version, publish_release\]/); + assert.doesNotMatch(deployZuey, /promote_beta_aliases|docker_images|complete_release_artifacts/); +}); + +test("dev beta workflow publishes zuey amd64 asset before completing all release assets", () => { + const buildZuey = jobBlock("build_zuey_binary"); + assert.match(buildZuey, /GOARCH: amd64/); + assert.match(buildZuey, /name: binary-linux-amd64/); + + const remainingBinaries = jobBlock("build_remaining_binaries"); + assert.match(remainingBinaries, /goarch: arm64/); + assert.match(remainingBinaries, /name: binary-\$\{\{ matrix\.goos \}\}-\$\{\{ matrix\.goarch \}\}/); + + const completion = jobBlock("complete_release_artifacts"); + assert.match(completion, /needs: \[beta_version, publish_release, build_zuey_binary, build_remaining_binaries\]/); + assert.match(completion, /pattern: binary-\*/); + assert.match(completion, /sha256sum goclaw-\*\.tar\.gz > CHECKSUMS\.sha256/); +}); + +test("dev beta workflow skips stale deploy and alias mutations", () => { + for (const name of ["deploy_zuey_beta", "promote_beta_aliases"]) { + const block = jobBlock(name); + assert.match(block, /id: beta_freshness/); + assert.match(block, /latest_beta/); + assert.match(block, /Skipping stale/); + assert.match(block, /steps\.beta_freshness\.outputs\.current == 'true'/); + } +});