From 02b55dabb481a159112399e79c0d08742c81a66f Mon Sep 17 00:00:00 2001 From: Dan Zyto Date: Wed, 19 Aug 2026 12:51:12 +0200 Subject: [PATCH] ci: one build before the deploys, artifacts reused (Dan, #189 review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A push touching runner/packages/** installed and built the workspace twice, once inside each deploy job. Now a shared build job (gated on either deploy firing) builds the workspace once and the authoring app once (skipped when only the api deploys), and the deploy jobs ship the downloads: deploy-authoring needs no pnpm install at all — pinned-npx wrangler ships ./dist as Workers Assets — and deploy-api keeps install for its own wrangler but resolves demo-runtime from the artifact. A broken build still never reaches wrangler; it just fails once, earlier. --- .github/workflows/master.yml | 76 ++++++++++++++++++++++++++++++------ runner/AGENTS.md | 2 +- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 75b57bee7..091d6754e 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -2,8 +2,13 @@ name: Master # The single master-push pipeline: deploy first, verify production after. # -# changes ─┬─► deploy-authoring ─┐ -# └─► deploy-api ───────┴─► smoke (@smoke e2e against prod) +# changes ─► build ─┬─► deploy-authoring ─┐ +# └─► deploy-api ───────┴─► smoke (@smoke e2e against prod) +# +# One build, artifacts reused (Dan, #189 review): the workspace packages and +# the authoring app are built once, and the deploy jobs only download what +# they ship — before this, a push touching runner/packages/** installed and +# built the workspace twice, once per deploy job. # # No test gate before the deploys, deliberately (Dan, #184 review): the full # suite already ran on the PR, each deploy job runs its own `pnpm build` (a @@ -84,9 +89,10 @@ jobs: echo "authoring=$authoring" >> "$GITHUB_OUTPUT" echo "api=$api" >> "$GITHUB_OUTPUT" - deploy-authoring: + # One install, one workspace build — shared by both deploys via artifacts. + build: needs: [changes] - if: needs.changes.outputs.authoring == 'true' + if: needs.changes.outputs.authoring == 'true' || needs.changes.outputs.api == 'true' runs-on: ubuntu-latest defaults: run: @@ -106,19 +112,57 @@ jobs: - run: pnpm install --frozen-lockfile - # Build workspace packages, then the authoring app. VITE_API_BASE comes - # from apps/authoring/.env.production (committed) so it targets prod. - run: pnpm build - # SENTRY_* are only set here, on the deploying build. PR CI (ci.yml) - # gets no token, so PR builds neither emit source maps nor upload a - # release — see apps/authoring/vite.config.ts. - - run: pnpm --filter @handsontable/demo-authoring build + + - name: Upload the runtime build + uses: actions/upload-artifact@v4 + with: + name: runtime-dist + path: runner/packages/runtime/dist/ + retention-days: 1 + if-no-files-found: error + + # The authoring app builds here too — the deploy job then only ships it. + # VITE_API_BASE comes from apps/authoring/.env.production (committed) so + # it targets prod. SENTRY_* are only set on this deploying build; PR CI + # (ci.yml) gets no token, so PR builds neither emit source maps nor + # upload a release — see apps/authoring/vite.config.ts. + - name: Build authoring (skipped when only the api deploys) + if: needs.changes.outputs.authoring == 'true' + run: pnpm --filter @handsontable/demo-authoring build env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ vars.SENTRY_ORG }} SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }} GITHUB_SHA: ${{ github.sha }} + - name: Upload the authoring build + if: needs.changes.outputs.authoring == 'true' + uses: actions/upload-artifact@v4 + with: + name: authoring-dist + path: runner/apps/authoring/dist/ + retention-days: 1 + if-no-files-found: error + + deploy-authoring: + needs: [changes, build] + if: needs.changes.outputs.authoring == 'true' + runs-on: ubuntu-latest + defaults: + run: + working-directory: runner + steps: + - uses: actions/checkout@v4 + + - name: Download the authoring build + uses: actions/download-artifact@v4 + with: + name: authoring-dist + path: runner/apps/authoring/dist/ + + # No pnpm install: wrangler is pinned through npx and ships ./dist as + # Workers Assets — the checkout only supplies wrangler.jsonc. - name: Deploy authoring worker working-directory: runner/apps/authoring env: @@ -163,7 +207,7 @@ jobs: smoke: true deploy-api: - needs: [changes] + needs: [changes, build] if: needs.changes.outputs.api == 'true' runs-on: ubuntu-latest defaults: @@ -182,8 +226,16 @@ jobs: cache: pnpm cache-dependency-path: runner/pnpm-lock.yaml + # Install stays: `pnpm run deploy` runs the workspace's own wrangler and + # bundles the worker, which resolves @handsontable/demo-runtime from the + # artifact downloaded below instead of rebuilding it. - run: pnpm install --frozen-lockfile - - run: pnpm build + + - name: Download the runtime build + uses: actions/download-artifact@v4 + with: + name: runtime-dist + path: runner/packages/runtime/dist/ # Apply pending D1 schema changes to the remote DB before shipping code # that may depend on them. Migrations are idempotent (IF NOT EXISTS). diff --git a/runner/AGENTS.md b/runner/AGENTS.md index 07c4c5173..6ae01db65 100644 --- a/runner/AGENTS.md +++ b/runner/AGENTS.md @@ -170,7 +170,7 @@ Six workflows live in `.github/workflows/` at the repo root: | Workflow | Trigger | What it does | |----------|---------|--------------| | `ci.yml` | every PR (+ manual dispatch) | the CI DAG: presence + unit → build → authoring → e2e (in the pinned Playwright container). PRs are the only place the full suite runs — master does not repeat it. | -| `master.yml` | every push to `master` (or manual dispatch with per-target checkboxes) | deploy-first: path-gated `deploy-authoring`/`deploy-api` (each self-builds — a broken build never reaches wrangler), then one `@smoke` E2E run against prod. Merge-skew is covered by branch protection ("require branches to be up to date"), not by re-running the suite. | +| `master.yml` | every push to `master` (or manual dispatch with per-target checkboxes) | deploy-first: one shared `build` job (workspace + authoring, artifacts reused), then path-gated `deploy-authoring`/`deploy-api` ship the downloads — a broken build still never reaches wrangler — then one `@smoke` E2E run against prod. Merge-skew is covered by branch protection ("require branches to be up to date"), not by re-running the suite. | | `e2e-live.yml` | manual, weekly canary (Mon 05:00 UTC, prod + AI), or `workflow_call` with `smoke: true` from `master.yml` | everything ci.yml cannot run: live renders, container suites, the share viewer/round-trip, AI answer checks. Dispatch inputs: `base_url`, `ai`, `pkg_pr_new_ref` (DEV-2198). | | `e2e-starter-matrix.yml` | manual + monthly (1st, 03:00 UTC) | every starter × major through a live session; serialized against the global container cap. | | `import-docs.yml` | manual, or `repository_dispatch: docs-examples-sync` from the docs repo | re-imports the documentation-guide examples. |