From 0af6b365cef983935413ec6a9fb1ca911ca3b65b Mon Sep 17 00:00:00 2001 From: betegon Date: Thu, 29 Oct 2026 18:36:30 +0100 Subject: [PATCH 01/11] ci(cli): run E2E tests in parallel --- .github/workflows/test.yml | 47 +++++++++++++++++++++++- docs/operations/github-actions.md | 6 ++++ packages/cli/package.json | 1 + scripts/ci-projects.test.mjs | 60 +++++++++++++++++++++++++++++-- 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 47402b236..14bda6903 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -214,6 +214,48 @@ jobs: git diff --exit-code [[ -z "$(git status --porcelain --untracked-files=all)" ]] + cli-e2e: + name: CLI E2E (Node 24) + needs: discover-projects + if: contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + BRAINTRUST_API_KEY: ci-test-key + GITHUB_TOKEN: ci-test-token + HONEYCOMB_API_KEY: ci-test-key + SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} + SENTRY_OAUTH_CLIENT_SECRET: ci-test-secret + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION_22 }} + cache: pnpm + - run: cp .env.example .env.local + - run: pnpm install --frozen-lockfile + - name: Build project dependencies + run: pnpm --filter 'sentry^...' --if-present run build + - name: Build standalone CLI + run: pnpm --filter sentry run build + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION_24 }} + cache: pnpm + - name: Run E2E tests on Node 24 + env: + SENTRY_CLI_BINARY: ${{ github.workspace }}/packages/cli/dist-bin/sentry-linux-x64 + run: | + test -x "$SENTRY_CLI_BINARY" + pnpm --filter sentry run test:e2e + - name: Check generated files + run: | + git diff --exit-code + [[ -z "$(git status --porcelain --untracked-files=all)" ]] + npm-runtime: name: NPM package (${{ matrix.name }}) needs: discover-projects @@ -290,6 +332,7 @@ jobs: project, npm-runtime, cli-asset-catalog, + cli-e2e, ] runs-on: ubuntu-latest steps: @@ -304,6 +347,7 @@ jobs: RUNTIME_COUNT: ${{ needs.discover-projects.outputs.runtime-count }} ASSET_CATALOG_RESULT: ${{ needs.cli-asset-catalog.result }} ASSET_CATALOG_SELECTED: ${{ contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') }} + CLI_E2E_RESULT: ${{ needs.cli-e2e.result }} run: | [[ "$DISCOVER_RESULT" == success ]] [[ "$INSTALL_RESULT" == success ]] @@ -322,4 +366,5 @@ jobs: if [[ "$ASSET_CATALOG_SELECTED" == false ]]; then expected=skipped fi - [[ "$ASSET_CATALOG_RESULT" == "$expected" ]] + test "$ASSET_CATALOG_RESULT" = "$expected" + test "$CLI_E2E_RESULT" = "$expected" diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index b0e0484bb..a7a8bd7c7 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -21,6 +21,12 @@ discovery, installation, repository quality, and every selected project. Package-specific exceptions live in `package.json#sentryCi`; the standalone smoke-test suite remains in its own workflow. +`CLI E2E (Node 24)` runs independently of the CLI project job. It builds the +standalone CLI on Node 22, then builds the npm bundle and tests both on Node 24. +Unit tests and coverage remain in the project job. The final `test` gate +requires E2E to pass whenever the CLI is selected, and requires it to be skipped +otherwise. + ### deploy.yml Runs after a successful `Test` push run on `main`. Checks out the tested commit diff --git a/packages/cli/package.json b/packages/cli/package.json index 9e5f41a5c..eee8ddf72 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,6 +51,7 @@ "packageManager": "pnpm@10.11.0", "sentryCi": { "coverage": "coverage/lcov.info", + "e2e": false, "npmRuntime": "bundle", "policy": "ci:policy" }, diff --git a/scripts/ci-projects.test.mjs b/scripts/ci-projects.test.mjs index cc1396274..a0dcfaf92 100644 --- a/scripts/ci-projects.test.mjs +++ b/scripts/ci-projects.test.mjs @@ -43,8 +43,10 @@ describe("workspace CI selection", () => { assert.match(docsScripts[docs.prepare], /sentry run generate:schema/); assert.match(docsScripts[docs.prepare], /sentry run generate:docs/); const cli = matrix.include.find(({ name }) => name === "sentry"); + assert.equal(cli.build, "build"); + assert.equal(cli.test, "test:unit"); assert.equal(cli.policy, "ci:policy"); - assert.equal(cli.e2e, "test:e2e"); + assert.equal(cli.e2e, ""); assert.equal(cli.coverage, "packages/cli/coverage/lcov.info"); assert.equal( matrix.include.find(({ name }) => name === "@sentry/mcp-core").junit, @@ -174,7 +176,7 @@ describe("workspace CI selection", () => { assert.doesNotMatch(workflow, /matrix\.name == 'sentry-cli-docs'/); assert.match( workflow, - /needs:\s*\[\s*discover-projects,\s*install,\s*quality,\s*project,\s*npm-runtime,\s*cli-asset-catalog,?\s*\]/, + /needs:\s*\[\s*discover-projects,\s*install,\s*quality,\s*project,\s*npm-runtime,\s*cli-asset-catalog,\s*cli-e2e,?\s*\]/, ); assert.match( workflow, @@ -202,6 +204,20 @@ describe("workspace CI selection", () => { assert.doesNotMatch(workflow.split("\njobs:", 1)[0], /paths(?:-ignore)?:/); }); + it("runs CLI E2E independently after project discovery", () => { + const workflow = readFileSync( + resolve(root, ".github/workflows/test.yml"), + "utf8", + ); + const job = workflow.split("\n cli-e2e:\n")[1]?.split(/\n [\w-]+:\n/)[0]; + assert.ok(job); + assert.match(job, /\n needs: discover-projects\n/); + assert.match( + job, + /if: contains\(fromJSON\(needs\.discover-projects\.outputs\.matrix\)\.include\.\*\.name, 'sentry'\)/, + ); + }); + it("requires native asset-catalog success when selected and a skip otherwise", () => { const workflow = readFileSync( resolve(root, ".github/workflows/test.yml"), @@ -244,6 +260,7 @@ describe("workspace CI selection", () => { RUNTIME_RESULT: count === "0" ? "skipped" : "success", ASSET_CATALOG_RESULT: nativeResult, ASSET_CATALOG_SELECTED: String(selected), + CLI_E2E_RESULT: selected ? "success" : "skipped", }, }); assert.equal( @@ -254,6 +271,45 @@ describe("workspace CI selection", () => { } }); + it("requires CLI E2E success when selected and a skip otherwise", () => { + const workflow = readFileSync( + resolve(root, ".github/workflows/test.yml"), + "utf8", + ); + const gate = workflow.split("\n test:\n")[1]; + assert.ok(gate); + assert.match(gate, /CLI_E2E_RESULT: \$\{\{ needs\.cli-e2e\.result \}\}/); + const script = gate.split(" run: |\n")[1]; + assert.ok(script); + + for (const selected of [true, false]) { + const expected = selected ? "success" : "skipped"; + for (const status of ["success", "failure", "cancelled", "skipped"]) { + const result = spawnSync("bash", ["-euc", script], { + encoding: "utf8", + env: { + ...process.env, + DISCOVER_RESULT: "success", + INSTALL_RESULT: "success", + QUALITY_RESULT: "success", + PROJECT_COUNT: "1", + PROJECT_RESULT: "success", + RUNTIME_COUNT: selected ? "1" : "0", + RUNTIME_RESULT: expected, + ASSET_CATALOG_RESULT: expected, + ASSET_CATALOG_SELECTED: String(selected), + CLI_E2E_RESULT: status, + }, + }); + assert.equal( + result.status, + status === expected ? 0 : 1, + `selected=${selected}, e2e=${status}`, + ); + } + } + }); + it("requires safe metadata and a single owner of each specialized role", () => { assert.throws( () => From 2c1cde73a6c97f4a828bc2031987a482c2eff9ee Mon Sep 17 00:00:00 2001 From: betegon Date: Thu, 29 Oct 2026 20:47:31 +0100 Subject: [PATCH 02/11] ci(cli): remove unused E2E environment setup --- .github/workflows/test.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14bda6903..8d3d50087 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -221,11 +221,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 env: - BRAINTRUST_API_KEY: ci-test-key - GITHUB_TOKEN: ci-test-token - HONEYCOMB_API_KEY: ci-test-key SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} - SENTRY_OAUTH_CLIENT_SECRET: ci-test-secret steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -235,7 +231,6 @@ jobs: with: node-version: ${{ env.NODE_VERSION_22 }} cache: pnpm - - run: cp .env.example .env.local - run: pnpm install --frozen-lockfile - name: Build project dependencies run: pnpm --filter 'sentry^...' --if-present run build From 756b806115166c38d08dee8bc2e92cdd9da96ad1 Mon Sep 17 00:00:00 2001 From: betegon Date: Fri, 30 Oct 2026 10:36:08 +0100 Subject: [PATCH 03/11] ci(cli): prepare E2E and unit inputs once --- .github/workflows/test.yml | 16 ++++++++++------ docs/operations/github-actions.md | 5 ++++- packages/cli/package.json | 10 ++++++++-- scripts/ci-projects.test.mjs | 6 ++++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d3d50087..a867fe29f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -223,29 +223,33 @@ jobs: env: SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: node-version: ${{ env.NODE_VERSION_22 }} cache: pnpm - run: pnpm install --frozen-lockfile - name: Build project dependencies run: pnpm --filter 'sentry^...' --if-present run build + - run: pnpm --filter sentry run ci:prepare - name: Build standalone CLI - run: pnpm --filter sentry run build - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + run: pnpm --filter sentry run tsx script/build.ts --single + - uses: actions/setup-node@v7 with: node-version: ${{ env.NODE_VERSION_24 }} - cache: pnpm + - name: Build npm bundle + run: pnpm --filter sentry run tsx script/bundle.ts - name: Run E2E tests on Node 24 env: SENTRY_CLI_BINARY: ${{ github.workspace }}/packages/cli/dist-bin/sentry-linux-x64 run: | test -x "$SENTRY_CLI_BINARY" - pnpm --filter sentry run test:e2e + test -s packages/cli/dist/bin.cjs + test -s packages/cli/dist/index.cjs + pnpm --filter sentry exec vitest run test/e2e - name: Check generated files run: | git diff --exit-code diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index a7a8bd7c7..896b8691b 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -23,7 +23,10 @@ smoke-test suite remains in its own workflow. `CLI E2E (Node 24)` runs independently of the CLI project job. It builds the standalone CLI on Node 22, then builds the npm bundle and tests both on Node 24. -Unit tests and coverage remain in the project job. The final `test` gate +Dependencies and generated sources are prepared once per job. The CLI project +job runs typechecks, unit tests, and coverage without rebuilding the standalone +or repeating generation. Public build and test commands still prepare their own +inputs for local use. The final `test` gate requires E2E to pass whenever the CLI is selected, and requires it to be skipped otherwise. diff --git a/packages/cli/package.json b/packages/cli/package.json index eee8ddf72..71fc1aa0b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -50,10 +50,13 @@ }, "packageManager": "pnpm@10.11.0", "sentryCi": { + "build": false, "coverage": "coverage/lcov.info", "e2e": false, "npmRuntime": "bundle", - "policy": "ci:policy" + "policy": "ci:policy", + "test": "ci:test:unit", + "typecheck": "ci:typecheck" }, "scripts": { "tsx": "tsx --env-file-if-exists=.env.local --import ./script/require-shim.mjs", @@ -66,7 +69,7 @@ "lint": "pnpm -w exec oxlint --report-unused-disable-directives packages/cli && pnpm -w exec oxfmt --check packages/cli", "lint:fix": "pnpm -w exec oxlint --fix packages/cli && pnpm -w exec oxfmt --write packages/cli", "test": "pnpm run test:unit", - "test:unit": "pnpm run generate:docs && pnpm run generate:sdk && vitest run test/lib test/commands test/types test/script --coverage", + "test:unit": "pnpm run generate:docs && pnpm run generate:sdk && pnpm run ci:test:unit", "test:changed": "pnpm run generate:docs && pnpm run generate:sdk && vitest run --changed", "test:e2e": "pnpm tsx script/prepare-e2e-bundle.ts && vitest run test/e2e", "test:init-eval": "vitest run test/init-eval --testTimeout 600000", @@ -94,6 +97,9 @@ "check:doc-examples": "pnpm tsx script/check-doc-examples.ts", "check:installer-docs": "pnpm tsx script/check-installer-docs.ts", "check:destructive": "pnpm tsx script/check-destructive-commands.ts", + "ci:prepare": "pnpm run generate:schema && pnpm run generate:docs && pnpm run generate:sdk", + "ci:typecheck": "tsc --noEmit", + "ci:test:unit": "vitest run test/lib test/commands test/types test/script --coverage", "ci:policy": "pnpm run check:patches && pnpm run check:deps && pnpm run check:errors && pnpm run check:fragments && pnpm run check:docs-sections && pnpm run check:env-coverage && pnpm run check:stale-refs && pnpm run check:doc-examples && pnpm run check:installer-docs && pnpm run check:destructive" }, "devDependencies": { diff --git a/scripts/ci-projects.test.mjs b/scripts/ci-projects.test.mjs index a0dcfaf92..45fe2b9ac 100644 --- a/scripts/ci-projects.test.mjs +++ b/scripts/ci-projects.test.mjs @@ -43,8 +43,10 @@ describe("workspace CI selection", () => { assert.match(docsScripts[docs.prepare], /sentry run generate:schema/); assert.match(docsScripts[docs.prepare], /sentry run generate:docs/); const cli = matrix.include.find(({ name }) => name === "sentry"); - assert.equal(cli.build, "build"); - assert.equal(cli.test, "test:unit"); + assert.equal(cli.prepare, "ci:prepare"); + assert.equal(cli.build, ""); + assert.equal(cli.typecheck, "ci:typecheck"); + assert.equal(cli.test, "ci:test:unit"); assert.equal(cli.policy, "ci:policy"); assert.equal(cli.e2e, ""); assert.equal(cli.coverage, "packages/cli/coverage/lcov.info"); From 1b42251feb06944d17cdd89e029c26dae43727d0 Mon Sep 17 00:00:00 2001 From: betegon Date: Fri, 30 Oct 2026 13:39:49 +0100 Subject: [PATCH 04/11] ci(cli): reuse npm build setup and share Node pins --- .github/node-versions/20 | 1 + .github/node-versions/22 | 1 + .github/node-versions/24 | 1 + .github/workflows/cli-build.yml | 11 ++---- .github/workflows/test.yml | 65 +++++++++++++------------------ docs/operations/github-actions.md | 5 +++ scripts/ci-projects.test.mjs | 11 ++++-- 7 files changed, 48 insertions(+), 47 deletions(-) create mode 100644 .github/node-versions/20 create mode 100644 .github/node-versions/22 create mode 100644 .github/node-versions/24 diff --git a/.github/node-versions/20 b/.github/node-versions/20 new file mode 100644 index 000000000..ccc4c6c7f --- /dev/null +++ b/.github/node-versions/20 @@ -0,0 +1 @@ +20.20.2 diff --git a/.github/node-versions/22 b/.github/node-versions/22 new file mode 100644 index 000000000..b8b23ca93 --- /dev/null +++ b/.github/node-versions/22 @@ -0,0 +1 @@ +22.23.4 diff --git a/.github/node-versions/24 b/.github/node-versions/24 new file mode 100644 index 000000000..ce62013c3 --- /dev/null +++ b/.github/node-versions/24 @@ -0,0 +1 @@ +24.22.1 diff --git a/.github/workflows/cli-build.yml b/.github/workflows/cli-build.yml index 33ffe585c..733de89fe 100644 --- a/.github/workflows/cli-build.yml +++ b/.github/workflows/cli-build.yml @@ -16,9 +16,6 @@ permissions: env: SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} - NODE_VERSION_20: "20.20.2" - NODE_VERSION_22: "22.23.4" - NODE_VERSION_24: "24.22.1" jobs: nightly-version: @@ -58,7 +55,7 @@ jobs: - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile @@ -267,7 +264,7 @@ jobs: - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile @@ -303,7 +300,7 @@ jobs: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: ${{ matrix.node == 24 && env.NODE_VERSION_24 || matrix.node == 20 && env.NODE_VERSION_20 || env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/${{ matrix.node }} - name: Download npm artifact uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -334,7 +331,7 @@ jobs: - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: - node-version: ${{ env.NODE_VERSION_24 }} + node-version-file: .github/node-versions/24 cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a867fe29f..004fbccb6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,11 +9,6 @@ on: permissions: contents: read -env: - NODE_VERSION_20: "20.20.2" - NODE_VERSION_22: "22.23.4" - NODE_VERSION_24: "24.22.1" - jobs: discover-projects: name: Discover projects @@ -24,14 +19,14 @@ jobs: runtime-matrix: ${{ steps.projects.outputs.runtime-matrix }} runtime-count: ${{ steps.projects.outputs.runtime-count }} steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false fetch-depth: 0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 - name: Build project matrix id: projects env: @@ -53,13 +48,13 @@ jobs: name: Install runs-on: ubuntu-latest steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - run: pnpm install --frozen-lockfile @@ -67,13 +62,13 @@ jobs: name: Repository quality runs-on: ubuntu-latest steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm run test:ci-projects @@ -96,13 +91,13 @@ jobs: SENTRY_CLI_NO_TELEMETRY: "1" FOSSILIZE_SIGN: "n" steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm --filter sentry run build -- --target darwin-arm64 @@ -127,13 +122,13 @@ jobs: SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} SENTRY_OAUTH_CLIENT_SECRET: ci-test-secret steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - run: cp .env.example .env.local - run: pnpm install --frozen-lockfile @@ -192,10 +187,10 @@ jobs: PROJECT_NAME: ${{ matrix.name }} CHECK_SCRIPT: ${{ matrix.policy }} run: pnpm --filter "$PROJECT_NAME" run "$CHECK_SCRIPT" - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 if: matrix.e2e != '' with: - node-version: ${{ env.NODE_VERSION_24 }} + node-version-file: .github/node-versions/24 cache: pnpm - name: Run E2E tests on Node 24 if: matrix.e2e != '' @@ -229,7 +224,7 @@ jobs: - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - run: pnpm install --frozen-lockfile - name: Build project dependencies @@ -239,7 +234,7 @@ jobs: run: pnpm --filter sentry run tsx script/build.ts --single - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_24 }} + node-version-file: .github/node-versions/24 - name: Build npm bundle run: pnpm --filter sentry run tsx script/bundle.ts - name: Run E2E tests on Node 24 @@ -268,19 +263,15 @@ jobs: SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} SENTRY_CLI_NO_TELEMETRY: "1" steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 with: persist-credentials: false - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 cache: pnpm - run: pnpm install --frozen-lockfile - - name: Build package dependencies - env: - PROJECT_NAME: ${{ matrix.name }} - run: pnpm --filter "${PROJECT_NAME}^..." --if-present run build - name: Build and pack once on Node 22 env: BUILD_SCRIPT: ${{ matrix.npmRuntime }} @@ -295,25 +286,25 @@ jobs: test -s "${packages[0]}" [[ "$(stat -c %s "${packages[0]}")" -le 52428800 ]] mv "${packages[0]}" "$RUNNER_TEMP/npm-package/package.tgz" - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_20 }} + node-version-file: .github/node-versions/20 - name: Validate packaged runtime on Node 20 env: RUNTIME_BIN: ${{ matrix.runtimeBin }} RUNTIME_PACKAGE: ${{ matrix.name }} run: bash scripts/validate-npm-runtime.sh - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_22 }} + node-version-file: .github/node-versions/22 - name: Validate packaged runtime on Node 22 env: RUNTIME_BIN: ${{ matrix.runtimeBin }} RUNTIME_PACKAGE: ${{ matrix.name }} run: bash scripts/validate-npm-runtime.sh - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: - node-version: ${{ env.NODE_VERSION_24 }} + node-version-file: .github/node-versions/24 - name: Validate packaged runtime on Node 24 env: RUNTIME_BIN: ${{ matrix.runtimeBin }} diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index 896b8691b..bf43f198a 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -21,6 +21,11 @@ discovery, installation, repository quality, and every selected project. Package-specific exceptions live in `package.json#sentryCi`; the standalone smoke-test suite remains in its own workflow. +`test.yml` and `cli-build.yml` read their exact Node 20, 22, and 24 versions +from `.github/node-versions/`. The npm runtime job calls the self-contained +`sentryCi.npmRuntime` build command once, then validates the same package on +all three Node versions. + `CLI E2E (Node 24)` runs independently of the CLI project job. It builds the standalone CLI on Node 22, then builds the npm bundle and tests both on Node 24. Dependencies and generated sources are prepared once per job. The CLI project diff --git a/scripts/ci-projects.test.mjs b/scripts/ci-projects.test.mjs index 45fe2b9ac..1f9e971d7 100644 --- a/scripts/ci-projects.test.mjs +++ b/scripts/ci-projects.test.mjs @@ -196,9 +196,14 @@ describe("workspace CI selection", () => { assert.match(workflow, /Validate packaged runtime on Node 20/); assert.match(workflow, /Validate packaged runtime on Node 22/); assert.match(workflow, /Validate packaged runtime on Node 24/); - assert.match(workflow, /NODE_VERSION_20: "20\.20\.2"/); - assert.match(workflow, /NODE_VERSION_22: "22\.23\.4"/); - assert.match(workflow, /NODE_VERSION_24: "24\.22\.1"/); + for (const major of [20, 22, 24]) { + const versionFile = `.github/node-versions/${major}`; + assert.ok(workflow.includes(`node-version-file: ${versionFile}`)); + assert.match( + readFileSync(resolve(root, versionFile), "utf8").trim(), + new RegExp(`^${major}\\.\\d+\\.\\d+$`), + ); + } assert.match(workflow, /RUNTIME_RESULT.*needs\.npm-runtime\.result/); assert.match(workflow, /name: Verify JUnit/); assert.match(workflow, /name: Verify coverage/); From c2e83c059f9c60f4f638f2831373cea561fe7311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Beteg=C3=B3n?= Date: Mon, 2 Nov 2026 07:16:12 +0100 Subject: [PATCH 05/11] ci(cli): shard unit tests and merge coverage (#1727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runs the CLI unit tests in three parallel shards and merges their Vitest blob reports into one coverage report, instead of running the whole suite inside the `sentry` project job. Stacked on #1726. | Job | #1726 (`1b42251`, unsharded) | This PR (`1608137`) | | --- | --- | --- | | `Test` workflow | 10m18s | 5m48s | | `sentry` project job | 9m57s | 1m57s | | CLI unit tests | inside `sentry` | 2m24s / 3m48s / 3m33s (3 shards) | | CLI coverage merge | inside `sentry` | 0m25s | | CLI E2E (Node 24) | 4m30s | 3m30s | Runs: [#1726](https://github.com/getsentry/toolkit/actions/runs/41057051286), [this PR](https://github.com/getsentry/toolkit/actions/runs/41126676840). One run each; workflow time is first job start to last job done. The last job to finish was `CLI asset catalog (macOS arm64)`. Shards install only the CLI and its workspace dependencies: 1,508 → 774 packages (`pnpm install` vs `pnpm --filter 'sentry...' install`, both `--frozen-lockfile`), with install scripts kept. Sharing preparation through artifacts was no faster in a [one-off benchmark](https://github.com/getsentry/toolkit/actions/runs/41088577518) (4m41s vs 4m37s), so each job prepares its own inputs. Same tests and coverage scope; CodeQL is unchanged. Drops the `ci-projects` tests that only asserted `test.yml`'s own text or ran its bash from Node.
Recompute job times from any run ```sh gh run view -R getsentry/toolkit --json jobs --jq '.jobs[] | select(.name | test("^sentry$|^CLI")) | ((.completedAt | fromdate) - (.startedAt | fromdate)) as $s | "\($s / 60 | floor)m\($s % 60)s\t\(.name)"' ```
--- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude Opus 5.5 --- .github/workflows/test.yml | 97 +++++++++++++++-- docs/operations/github-actions.md | 24 +++-- packages/cli/package.json | 4 +- scripts/ci-projects.test.mjs | 174 +----------------------------- 4 files changed, 107 insertions(+), 192 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 004fbccb6..7ba31316f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -132,9 +132,6 @@ jobs: cache: pnpm - run: cp .env.example .env.local - run: pnpm install --frozen-lockfile - - name: Install zsh for CLI completion regression tests - if: matrix.name == 'sentry' - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends zsh - name: Build project dependencies env: PROJECT_NAME: ${{ matrix.name }} @@ -209,6 +206,84 @@ jobs: git diff --exit-code [[ -z "$(git status --porcelain --untracked-files=all)" ]] + cli-unit: + name: CLI unit tests (${{ matrix.shard }}/${{ strategy.job-total }}) + needs: discover-projects + if: contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + shard-count: ${{ strategy.job-total }} + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3] + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 + - uses: actions/setup-node@v7 + with: + node-version-file: .github/node-versions/22 + cache: pnpm + - run: pnpm --filter 'sentry...' install --frozen-lockfile + - name: Install zsh for CLI completion regression tests + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends zsh + - name: Build project dependencies + run: pnpm --filter 'sentry^...' --if-present run build + - run: pnpm --filter sentry run ci:prepare + - name: Test CLI shard + env: + SHARD: ${{ matrix.shard }} + SHARD_COUNT: ${{ strategy.job-total }} + run: pnpm --filter sentry run ci:test:unit --shard="$SHARD/$SHARD_COUNT" --reporter=default --reporter=blob --outputFile.blob="coverage/shard-$SHARD.json" + - uses: actions/upload-artifact@v7 + with: + name: cli-unit-coverage-${{ matrix.shard }} + path: packages/cli/coverage/shard-${{ matrix.shard }}.json + if-no-files-found: error + overwrite: true + retention-days: 7 + - name: Check generated files + run: | + git diff --exit-code + [[ -z "$(git status --porcelain --untracked-files=all)" ]] + + cli-coverage: + name: CLI coverage + needs: [discover-projects, cli-unit] + if: contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 + - uses: actions/setup-node@v7 + with: + node-version-file: .github/node-versions/22 + cache: pnpm + - run: pnpm --filter sentry install --frozen-lockfile --ignore-scripts + - uses: actions/download-artifact@v8 + with: + pattern: cli-unit-coverage-* + path: ${{ runner.temp }}/cli-unit-reports + merge-multiple: true + - name: Require every shard report + env: + SHARD_COUNT: ${{ needs.cli-unit.outputs.shard-count }} + run: | + test "$SHARD_COUNT" -gt 0 + for ((shard = 1; shard <= SHARD_COUNT; shard++)); do + test -s "$RUNNER_TEMP/cli-unit-reports/shard-$shard.json" + done + - name: Merge CLI coverage + run: pnpm --filter sentry exec vitest --merge-reports="$RUNNER_TEMP/cli-unit-reports" --coverage + - name: Verify coverage + run: test -s packages/cli/coverage/lcov.info + cli-e2e: name: CLI E2E (Node 24) needs: discover-projects @@ -322,6 +397,8 @@ jobs: project, npm-runtime, cli-asset-catalog, + cli-unit, + cli-coverage, cli-e2e, ] runs-on: ubuntu-latest @@ -337,24 +414,28 @@ jobs: RUNTIME_COUNT: ${{ needs.discover-projects.outputs.runtime-count }} ASSET_CATALOG_RESULT: ${{ needs.cli-asset-catalog.result }} ASSET_CATALOG_SELECTED: ${{ contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') }} + CLI_UNIT_RESULT: ${{ needs.cli-unit.result }} + CLI_COVERAGE_RESULT: ${{ needs.cli-coverage.result }} CLI_E2E_RESULT: ${{ needs.cli-e2e.result }} run: | - [[ "$DISCOVER_RESULT" == success ]] - [[ "$INSTALL_RESULT" == success ]] - [[ "$QUALITY_RESULT" == success ]] + test "$DISCOVER_RESULT" = success + test "$INSTALL_RESULT" = success + test "$QUALITY_RESULT" = success expected=success if [[ "$PROJECT_COUNT" == 0 ]]; then expected=skipped fi - [[ "$PROJECT_RESULT" == "$expected" ]] + test "$PROJECT_RESULT" = "$expected" expected=success if [[ "$RUNTIME_COUNT" == 0 ]]; then expected=skipped fi - [[ "$RUNTIME_RESULT" == "$expected" ]] + test "$RUNTIME_RESULT" = "$expected" expected=success if [[ "$ASSET_CATALOG_SELECTED" == false ]]; then expected=skipped fi test "$ASSET_CATALOG_RESULT" = "$expected" + test "$CLI_UNIT_RESULT" = "$expected" + test "$CLI_COVERAGE_RESULT" = "$expected" test "$CLI_E2E_RESULT" = "$expected" diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index bf43f198a..a117509ad 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -26,14 +26,22 @@ from `.github/node-versions/`. The npm runtime job calls the self-contained `sentryCi.npmRuntime` build command once, then validates the same package on all three Node versions. -`CLI E2E (Node 24)` runs independently of the CLI project job. It builds the -standalone CLI on Node 22, then builds the npm bundle and tests both on Node 24. -Dependencies and generated sources are prepared once per job. The CLI project -job runs typechecks, unit tests, and coverage without rebuilding the standalone -or repeating generation. Public build and test commands still prepare their own -inputs for local use. The final `test` gate -requires E2E to pass whenever the CLI is selected, and requires it to be skipped -otherwise. +When the CLI is selected, its lint, typecheck, and policy checks run in the +project job. Unit tests run on Node 22 in three parallel shards, with the same +test directories and coverage as `pnpm --filter sentry test:unit`. Shards install +only the CLI and its workspace dependencies, retaining install scripts. Each +uploads a Vitest blob report; `CLI coverage` requires a report from every shard +before merging them into `packages/cli/coverage/lcov.info`. +The merge job installs the CLI dependency set without install scripts; it does +not build packages or rerun tests. + +`CLI E2E (Node 24)` runs independently of the unit tests and project checks. It +builds the standalone CLI on Node 22 and the npm bundle on Node 24, then tests +both on Node 24. The project, unit-test, and E2E jobs each build their workspace +dependencies and run `ci:prepare` once before using the prepared sources. Public +build and test commands still prepare their own inputs for local use. The final +`test` gate requires the unit shards, coverage merge, and E2E job whenever the +CLI is selected, and requires them to be skipped otherwise. ### deploy.yml diff --git a/packages/cli/package.json b/packages/cli/package.json index 71fc1aa0b..c42c0fa1b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,11 +51,9 @@ "packageManager": "pnpm@10.11.0", "sentryCi": { "build": false, - "coverage": "coverage/lcov.info", "e2e": false, "npmRuntime": "bundle", - "policy": "ci:policy", - "test": "ci:test:unit", + "test": false, "typecheck": "ci:typecheck" }, "scripts": { diff --git a/scripts/ci-projects.test.mjs b/scripts/ci-projects.test.mjs index 1f9e971d7..39e7a8fd3 100644 --- a/scripts/ci-projects.test.mjs +++ b/scripts/ci-projects.test.mjs @@ -1,6 +1,5 @@ import assert from "node:assert/strict"; -import { execFileSync, spawnSync } from "node:child_process"; -import { readFileSync } from "node:fs"; +import { execFileSync } from "node:child_process"; import { resolve } from "node:path"; import { describe, it } from "node:test"; @@ -35,25 +34,6 @@ describe("workspace CI selection", () => { assert.ok(names.includes("@sentry/mcp-core")); assert.ok(!names.includes("@sentry/mcp-smoke-tests")); assert.equal(new Set(names).size, names.length); - const docs = matrix.include.find(({ name }) => name === "sentry-cli-docs"); - assert.equal(docs.prepare, "ci:prepare"); - const docsScripts = JSON.parse( - readFileSync(resolve(root, "apps/cli-docs/package.json"), "utf8"), - ).scripts; - assert.match(docsScripts[docs.prepare], /sentry run generate:schema/); - assert.match(docsScripts[docs.prepare], /sentry run generate:docs/); - const cli = matrix.include.find(({ name }) => name === "sentry"); - assert.equal(cli.prepare, "ci:prepare"); - assert.equal(cli.build, ""); - assert.equal(cli.typecheck, "ci:typecheck"); - assert.equal(cli.test, "ci:test:unit"); - assert.equal(cli.policy, "ci:policy"); - assert.equal(cli.e2e, ""); - assert.equal(cli.coverage, "packages/cli/coverage/lcov.info"); - assert.equal( - matrix.include.find(({ name }) => name === "@sentry/mcp-core").junit, - "packages/mcp-core/tests.junit.xml", - ); }); it("runs only affected projects and their transitive consumers", () => { @@ -165,158 +145,6 @@ describe("workspace CI selection", () => { } }); - it("keeps a stable CI status and one dynamic job per package", () => { - const workflow = readFileSync( - resolve(root, ".github/workflows/test.yml"), - "utf8", - ); - assert.match(workflow, /merge_group:/); - assert.match(workflow, /node scripts\/ci-projects\.mjs --event/); - assert.match(workflow, /name: \$\{\{ matrix\.name \}\}/); - assert.match(workflow, /if: matrix\.prepare != ''/); - assert.match(workflow, /CHECK_SCRIPT: \$\{\{ matrix\.prepare \}\}/); - assert.doesNotMatch(workflow, /matrix\.name == 'sentry-cli-docs'/); - assert.match( - workflow, - /needs:\s*\[\s*discover-projects,\s*install,\s*quality,\s*project,\s*npm-runtime,\s*cli-asset-catalog,\s*cli-e2e,?\s*\]/, - ); - assert.match( - workflow, - /runtime-matrix: \$\{\{ steps\.projects\.outputs\.runtime-matrix \}\}/, - ); - assert.match( - workflow, - /matrix: \$\{\{ fromJSON\(needs\.discover-projects\.outputs\.runtime-matrix\) \}\}/, - ); - assert.match( - workflow, - /if: needs\.discover-projects\.outputs\.runtime-count != '0'/, - ); - assert.match(workflow, /Build and pack once on Node 22/); - assert.match(workflow, /Validate packaged runtime on Node 20/); - assert.match(workflow, /Validate packaged runtime on Node 22/); - assert.match(workflow, /Validate packaged runtime on Node 24/); - for (const major of [20, 22, 24]) { - const versionFile = `.github/node-versions/${major}`; - assert.ok(workflow.includes(`node-version-file: ${versionFile}`)); - assert.match( - readFileSync(resolve(root, versionFile), "utf8").trim(), - new RegExp(`^${major}\\.\\d+\\.\\d+$`), - ); - } - assert.match(workflow, /RUNTIME_RESULT.*needs\.npm-runtime\.result/); - assert.match(workflow, /name: Verify JUnit/); - assert.match(workflow, /name: Verify coverage/); - assert.match(workflow, /if: always\(\)/); - assert.doesNotMatch(workflow.split("\njobs:", 1)[0], /paths(?:-ignore)?:/); - }); - - it("runs CLI E2E independently after project discovery", () => { - const workflow = readFileSync( - resolve(root, ".github/workflows/test.yml"), - "utf8", - ); - const job = workflow.split("\n cli-e2e:\n")[1]?.split(/\n [\w-]+:\n/)[0]; - assert.ok(job); - assert.match(job, /\n needs: discover-projects\n/); - assert.match( - job, - /if: contains\(fromJSON\(needs\.discover-projects\.outputs\.matrix\)\.include\.\*\.name, 'sentry'\)/, - ); - }); - - it("requires native asset-catalog success when selected and a skip otherwise", () => { - const workflow = readFileSync( - resolve(root, ".github/workflows/test.yml"), - "utf8", - ); - const gate = workflow.split("\n test:\n")[1]; - assert.ok(gate); - assert.match( - gate, - /ASSET_CATALOG_RESULT: \$\{\{ needs\.cli-asset-catalog\.result \}\}/, - ); - assert.match( - gate, - /ASSET_CATALOG_SELECTED: \$\{\{ contains\(fromJSON\(needs\.discover-projects\.outputs\.matrix\)\.include\.\*\.name, 'sentry'\) \}\}/, - ); - const script = gate.split(" run: |\n")[1]; - assert.ok(script); - - for (const [selected, nativeResult, count, passes] of [ - [true, "success", "1", true], - [true, "failure", "1", false], - [true, "cancelled", "1", false], - [true, "skipped", "1", false], - [false, "skipped", "1", true], - [false, "success", "1", false], - [false, "failure", "1", false], - [false, "cancelled", "1", false], - [false, "skipped", "0", true], - ]) { - const result = spawnSync("bash", ["-euc", script], { - encoding: "utf8", - env: { - ...process.env, - DISCOVER_RESULT: "success", - INSTALL_RESULT: "success", - QUALITY_RESULT: "success", - PROJECT_COUNT: count, - PROJECT_RESULT: count === "0" ? "skipped" : "success", - RUNTIME_COUNT: count, - RUNTIME_RESULT: count === "0" ? "skipped" : "success", - ASSET_CATALOG_RESULT: nativeResult, - ASSET_CATALOG_SELECTED: String(selected), - CLI_E2E_RESULT: selected ? "success" : "skipped", - }, - }); - assert.equal( - result.status, - passes ? 0 : 1, - `selected=${selected}, native=${nativeResult}, projects=${count}`, - ); - } - }); - - it("requires CLI E2E success when selected and a skip otherwise", () => { - const workflow = readFileSync( - resolve(root, ".github/workflows/test.yml"), - "utf8", - ); - const gate = workflow.split("\n test:\n")[1]; - assert.ok(gate); - assert.match(gate, /CLI_E2E_RESULT: \$\{\{ needs\.cli-e2e\.result \}\}/); - const script = gate.split(" run: |\n")[1]; - assert.ok(script); - - for (const selected of [true, false]) { - const expected = selected ? "success" : "skipped"; - for (const status of ["success", "failure", "cancelled", "skipped"]) { - const result = spawnSync("bash", ["-euc", script], { - encoding: "utf8", - env: { - ...process.env, - DISCOVER_RESULT: "success", - INSTALL_RESULT: "success", - QUALITY_RESULT: "success", - PROJECT_COUNT: "1", - PROJECT_RESULT: "success", - RUNTIME_COUNT: selected ? "1" : "0", - RUNTIME_RESULT: expected, - ASSET_CATALOG_RESULT: expected, - ASSET_CATALOG_SELECTED: String(selected), - CLI_E2E_RESULT: status, - }, - }); - assert.equal( - result.status, - status === expected ? 0 : 1, - `selected=${selected}, e2e=${status}`, - ); - } - } - }); - it("requires safe metadata and a single owner of each specialized role", () => { assert.throws( () => From 0eb2b50db66e57f7ff0bae03a702cb556dcb00ed Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 2 Nov 2026 10:40:09 +0000 Subject: [PATCH 06/11] ci(cli): cap the E2E job at 10 minutes The job takes 3m28s-4m45s, the same range the unit shards cap at 10 minutes. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7ba31316f..40ce84d05 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -289,7 +289,7 @@ jobs: needs: discover-projects if: contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 env: SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} steps: From 4dbe4f1a603825ba61cc2e67e1d6a07e214d0ca8 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 2 Nov 2026 18:54:53 +0000 Subject: [PATCH 07/11] ci(cli): run E2E in Build against the nightly build artifacts Build now also runs on pull requests, building only linux-x64 there. A new E2E job downloads the Linux binary, the npm package and the generated sources from that run and tests them without building, so pull requests, nightlies and releases test the artifacts Build ships. E2E replaces the Linux smoke test, and the CLI E2E job leaves test.yml. Build status fails unless the builds, the npm package tests and E2E pass. Every actions/ step in cli-build.yml uses its latest major tag. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/cli-build.yml | 134 ++++++++++++++++++++++-------- .github/workflows/test.yml | 44 ---------- docs/cli/README.md | 6 +- docs/operations/github-actions.md | 30 +++++-- scripts/workflow-policy.test.mjs | 4 + 5 files changed, 131 insertions(+), 87 deletions(-) diff --git a/.github/workflows/cli-build.yml b/.github/workflows/cli-build.yml index b6e25f656..d10981d5c 100644 --- a/.github/workflows/cli-build.yml +++ b/.github/workflows/cli-build.yml @@ -5,11 +5,12 @@ on: branches: - main - release/cli/** + pull_request: workflow_dispatch: concurrency: group: cli-build-${{ github.ref }} - cancel-in-progress: false + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: contents: read @@ -25,7 +26,7 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 - name: Compute version from the commit timestamp id: version run: node scripts/cli-nightly-version.mjs @@ -39,21 +40,12 @@ jobs: strategy: fail-fast: false matrix: - include: - - target: darwin-arm64 - os: macos-latest - - target: darwin-x64 - os: ubuntu-latest - - target: linux-x64 - os: ubuntu-latest - - target: linux-arm64 - os: ubuntu-latest - - target: windows-x64 - os: ubuntu-latest + # Pull requests build the Linux binary the E2E tests run against. + include: ${{ fromJSON(github.event_name == 'pull_request' && '[{"target":"linux-x64","os":"ubuntu-latest"}]' || '[{"target":"darwin-arm64","os":"macos-latest"},{"target":"darwin-x64","os":"ubuntu-latest"},{"target":"linux-x64","os":"ubuntu-latest"},{"target":"linux-arm64","os":"ubuntu-latest"},{"target":"windows-x64","os":"ubuntu-latest"}]') }} steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: node-version-file: .github/node-versions/22 cache: pnpm @@ -118,7 +110,7 @@ jobs: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: pnpm --filter sentry run build -- --target ${{ matrix.target }} - name: Smoke test - if: matrix.target == 'linux-x64' || matrix.target == 'darwin-arm64' + if: matrix.target == 'darwin-arm64' env: SENTRY_AUTH_TOKEN: "" SENTRY_TOKEN: "" @@ -140,14 +132,25 @@ jobs: shell: bash run: rcodesign verify packages/cli/dist-bin/sentry-darwin-arm64 - name: Upload binary artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@v7 with: name: sentry-${{ matrix.target }} path: | packages/cli/dist-bin/sentry-* !packages/cli/dist-bin/*.gz + - name: Upload generated CLI sources + if: matrix.target == 'linux-x64' + uses: actions/upload-artifact@v7 + with: + name: cli-generated + path: | + packages/cli/src/generated/ + packages/cli/src/sdk.generated.ts + packages/cli/src/sdk.generated.d.cts + if-no-files-found: error + retention-days: 7 - name: Upload compressed artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@v7 with: name: sentry-${{ matrix.target }}-gz path: packages/cli/dist-bin/*.gz @@ -166,7 +169,7 @@ jobs: from-version: ${{ steps.generate.outputs.from-version }} steps: - name: Download binaries and compressed artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@v8 with: pattern: sentry-*-* path: new-binaries @@ -185,7 +188,7 @@ jobs: patches-dir: patches - name: Upload patches if: steps.generate.outputs.has-patches == 'true' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@v7 with: name: sentry-patches path: patches/*.patch @@ -204,13 +207,13 @@ jobs: packages: write steps: - name: Download compressed binaries - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@v8 with: pattern: sentry-*-gz path: artifacts merge-multiple: true - name: Download binaries for patch integrity annotations - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@v8 with: pattern: sentry-*-* path: binaries @@ -218,7 +221,7 @@ jobs: - name: Download optional delta patches id: patches continue-on-error: true - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@v8 with: name: sentry-patches path: patches @@ -260,9 +263,9 @@ jobs: timeout-minutes: 20 environment: ${{ github.ref == 'refs/heads/main' && 'production' || startsWith(github.ref, 'refs/heads/release/cli/') && 'cli-release' || '' }} steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: node-version-file: .github/node-versions/22 cache: pnpm @@ -282,7 +285,7 @@ jobs: working-directory: packages/cli run: npm pack - name: Upload npm artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@v7 with: name: npm-package path: packages/cli/*.tgz @@ -295,7 +298,7 @@ jobs: working-directory: packages/mcp-server run: npm pack - name: Upload mcp-server npm artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@v7 with: name: mcp-server-npm-package path: packages/mcp-server/*.tgz @@ -310,12 +313,12 @@ jobs: matrix: node: [20, 22, 24] steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version-file: .github/node-versions/${{ matrix.node }} - name: Download npm artifact - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@v8 with: name: npm-package path: packages/cli @@ -330,8 +333,53 @@ jobs: npm install --ignore-scripts --prefix "$RUNNER_TEMP/npm-smoke" "$GITHUB_WORKSPACE/$package_file" node "$RUNNER_TEMP/npm-smoke/node_modules/sentry/dist/bin.cjs" --help + test-e2e: + name: E2E Tests (Node 24) + needs: [build-binary, build-npm] + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 + - uses: actions/setup-node@v7 + with: + node-version-file: .github/node-versions/24 + cache: pnpm + - run: pnpm --filter 'sentry...' install --frozen-lockfile + # The tests import CLI sources that read the generated files. + - name: Download generated CLI sources + uses: actions/download-artifact@v8 + with: + name: cli-generated + path: packages/cli/src + - name: Download Linux binary + uses: actions/download-artifact@v8 + with: + name: sentry-linux-x64 + path: packages/cli/dist-bin + - name: Download npm package + uses: actions/download-artifact@v8 + with: + name: npm-package + path: ${{ runner.temp }}/npm-package + - name: Unpack the binary and npm bundle + run: | + chmod +x packages/cli/dist-bin/sentry-linux-x64 + tar -xzf "$RUNNER_TEMP"/npm-package/sentry-*.tgz -C packages/cli --strip-components=1 package/dist + - name: Run E2E tests + env: + SENTRY_CLI_BINARY: ${{ github.workspace }}/packages/cli/dist-bin/sentry-linux-x64 + run: | + test -x "$SENTRY_CLI_BINARY" + test -s packages/cli/dist/bin.cjs + test -s packages/cli/dist/index.cjs + pnpm --filter sentry exec vitest run test/e2e + build-docs: name: Build Docs + if: github.event_name != 'pull_request' needs: build-binary runs-on: ubuntu-latest timeout-minutes: 30 @@ -340,9 +388,9 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} PUBLIC_SENTRY_ENVIRONMENT: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/cli/')) && 'production' || 'development' }} steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@v7 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@v7 with: node-version-file: .github/node-versions/24 cache: pnpm @@ -360,7 +408,7 @@ jobs: - name: Build docs run: pnpm --filter sentry-cli-docs run build - name: Download Linux binary - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@v8 with: name: sentry-linux-x64 path: dist-bin @@ -387,7 +435,25 @@ jobs: printf '%s\n' '{"version":3}' > "$output_dir/.vercel/output/config.json" (cd "$output_dir" && zip -qr "$GITHUB_WORKSPACE/vercel.zip" .vercel) - name: Upload docs artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@v7 with: name: vercel path: vercel.zip + + build-status: + name: Build status + if: always() + needs: [build-binary, build-npm, test-npm, test-e2e] + runs-on: ubuntu-latest + permissions: {} + steps: + - name: Require the CLI build and E2E tests + env: + RESULTS: ${{ join(needs.*.result, ' ') }} + run: | + for result in $RESULTS; do + if [[ "$result" != success ]]; then + echo "::error::A required Build job finished with: $result" + exit 1 + fi + done diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 40ce84d05..613573107 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -284,47 +284,6 @@ jobs: - name: Verify coverage run: test -s packages/cli/coverage/lcov.info - cli-e2e: - name: CLI E2E (Node 24) - needs: discover-projects - if: contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') - runs-on: ubuntu-latest - timeout-minutes: 10 - env: - SENTRY_CLIENT_ID: ${{ vars.SENTRY_CLIENT_ID }} - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - - uses: actions/setup-node@v7 - with: - node-version-file: .github/node-versions/22 - cache: pnpm - - run: pnpm install --frozen-lockfile - - name: Build project dependencies - run: pnpm --filter 'sentry^...' --if-present run build - - run: pnpm --filter sentry run ci:prepare - - name: Build standalone CLI - run: pnpm --filter sentry run tsx script/build.ts --single - - uses: actions/setup-node@v7 - with: - node-version-file: .github/node-versions/24 - - name: Build npm bundle - run: pnpm --filter sentry run tsx script/bundle.ts - - name: Run E2E tests on Node 24 - env: - SENTRY_CLI_BINARY: ${{ github.workspace }}/packages/cli/dist-bin/sentry-linux-x64 - run: | - test -x "$SENTRY_CLI_BINARY" - test -s packages/cli/dist/bin.cjs - test -s packages/cli/dist/index.cjs - pnpm --filter sentry exec vitest run test/e2e - - name: Check generated files - run: | - git diff --exit-code - [[ -z "$(git status --porcelain --untracked-files=all)" ]] - npm-runtime: name: NPM package (${{ matrix.name }}) needs: discover-projects @@ -399,7 +358,6 @@ jobs: cli-asset-catalog, cli-unit, cli-coverage, - cli-e2e, ] runs-on: ubuntu-latest steps: @@ -416,7 +374,6 @@ jobs: ASSET_CATALOG_SELECTED: ${{ contains(fromJSON(needs.discover-projects.outputs.matrix).include.*.name, 'sentry') }} CLI_UNIT_RESULT: ${{ needs.cli-unit.result }} CLI_COVERAGE_RESULT: ${{ needs.cli-coverage.result }} - CLI_E2E_RESULT: ${{ needs.cli-e2e.result }} run: | test "$DISCOVER_RESULT" = success test "$INSTALL_RESULT" = success @@ -438,4 +395,3 @@ jobs: test "$ASSET_CATALOG_RESULT" = "$expected" test "$CLI_UNIT_RESULT" = "$expected" test "$CLI_COVERAGE_RESULT" = "$expected" - test "$CLI_E2E_RESULT" = "$expected" diff --git a/docs/cli/README.md b/docs/cli/README.md index 2cdffa6f9..4249c7403 100644 --- a/docs/cli/README.md +++ b/docs/cli/README.md @@ -14,8 +14,10 @@ CLI releases build it from that workspace. Start here for repository guides: [command conventions](../../packages/cli/CONTRIBUTING.md) — implementation details and command design. - [CLI build workflow](../../.github/workflows/cli-build.yml) and - [release configuration](../../.craft.yml) — binaries, npm packaging, and - release-gated website artifacts. + [release configuration](../../.craft.yml) — binaries, npm packaging, + release-gated website artifacts, and the E2E tests that run against those + artifacts on pull requests, `main`, and release branches. A failing E2E run on + `main` fails the Build run, which the nightly watchdog below reports. - [CLI nightly watchdog](../../.github/workflows/cli-nightly-watchdog.yml) — hourly and after each main Build, checks for stuck or failed Build runs and a GHCR `nightly` that lags main. It keeps one open alert issue while unhealthy, diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index 188d42427..628a1b804 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -36,13 +36,26 @@ before merging them into `packages/cli/coverage/lcov.info`. The merge job installs the CLI dependency set without install scripts; it does not build packages or rerun tests. -`CLI E2E (Node 24)` runs independently of the unit tests and project checks. It -builds the standalone CLI on Node 22 and the npm bundle on Node 24, then tests -both on Node 24. The project, unit-test, and E2E jobs each build their workspace -dependencies and run `ci:prepare` once before using the prepared sources. Public -build and test commands still prepare their own inputs for local use. The final -`test` gate requires the unit shards, coverage merge, and E2E job whenever the -CLI is selected, and requires them to be skipped otherwise. +The project and unit-test jobs each build their workspace dependencies and run +`ci:prepare` once before using the prepared sources. Public build and test +commands still prepare their own inputs for local use. The final `test` gate +requires the unit shards and coverage merge whenever the CLI is selected, and +requires them to be skipped otherwise. The CLI E2E tests run in +[cli-build.yml](#cli-buildyml), against the artifacts that workflow builds. + +### cli-build.yml + +Runs on pull requests, pushes to `main` and `release/cli/**`, and manual +dispatch. It builds the CLI artifacts that ship: `Build Binary` builds every +target on `main` and release branches, and only `linux-x64` on pull requests. +`Build npm Package` packs the npm package. `E2E Tests (Node 24)` downloads the +Linux binary, the npm package, and the generated sources from those jobs and +runs `test/e2e` against them without building anything. `Build status` fails +unless the builds, the npm package tests, and the E2E tests passed. + +Nightly delta patches and the GHCR nightly publish only on pushes to `main`. +Releases take this workflow's artifacts (see `.craft.yml`), so a release ships +the binaries and package that its E2E run tested. ### deploy.yml @@ -164,6 +177,9 @@ project: A new workflow fits only when the matrix job cannot run the check: another operating system, a deployment, secrets, or pull request comments and labels. +The CLI's E2E tests are the exception: they must run against the binaries and +npm package that `cli-build.yml` builds for nightlies and releases, so +`packages/cli` sets `sentryCi.e2e` to `false` and that workflow runs them. `scripts/workflow-policy.test.mjs` fails for a workflow that runs on pull requests unless its list names that workflow and the reason. diff --git a/scripts/workflow-policy.test.mjs b/scripts/workflow-policy.test.mjs index c592b7513..854c35b4d 100644 --- a/scripts/workflow-policy.test.mjs +++ b/scripts/workflow-policy.test.mjs @@ -8,6 +8,10 @@ import { describe, it } from "node:test"; // per-project matrix. See docs/operations/github-actions.md#package-ci-checks. const PULL_REQUEST_WORKFLOWS = new Map([ ["test.yml", "Builds and runs the per-project matrix."], + [ + "cli-build.yml", + "Builds the CLI release artifacts and runs the CLI E2E tests against them.", + ], ["eval.yml", "Runs AI evaluations against model providers."], [ "install-scripts.yml", From 029c6db47cd0300bdb67fe743752fa5b073d1197 Mon Sep 17 00:00:00 2001 From: betegon Date: Mon, 2 Nov 2026 19:04:16 +0000 Subject: [PATCH 08/11] ci(cli): give the Build status job a timeout Every cli-build.yml job needs timeout-minutes, which cli-nightly-watchdog.test.mjs checks. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/cli-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cli-build.yml b/.github/workflows/cli-build.yml index d10981d5c..84cd6ac7f 100644 --- a/.github/workflows/cli-build.yml +++ b/.github/workflows/cli-build.yml @@ -445,6 +445,7 @@ jobs: if: always() needs: [build-binary, build-npm, test-npm, test-e2e] runs-on: ubuntu-latest + timeout-minutes: 5 permissions: {} steps: - name: Require the CLI build and E2E tests From dafb78cf6ef3570037ff4cb3c663e89df1a9e724 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 3 Nov 2026 09:24:05 +0000 Subject: [PATCH 09/11] ci(test): drop the CLI binary path from the shared E2E step The CLI no longer runs E2E in the project job, and @sentry/logger, the only project that does, doesn't read SENTRY_CLI_BINARY. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 613573107..3c34d83a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -194,7 +194,6 @@ jobs: env: PROJECT_NAME: ${{ matrix.name }} CHECK_SCRIPT: ${{ matrix.e2e }} - SENTRY_CLI_BINARY: ${{ github.workspace }}/packages/cli/dist-bin/sentry-linux-x64 run: pnpm --filter "$PROJECT_NAME" run "$CHECK_SCRIPT" - name: Verify coverage if: matrix.coverage != '' From 7f804fb1122ee8e55bd8a8e5182398ca793608db Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 3 Nov 2026 09:26:34 +0000 Subject: [PATCH 10/11] ci(cli): validate the npm package once, from the Build artifact With Build on pull requests, every CLI pull request bundled the npm package twice: Test's npm runtime job validated its own build on Node 20, 22 and 24, and Build bundled it again for E2E, where Test npm Package only ran --help. Test npm Package now runs validate-npm-runtime.sh against Build's tarball on all three versions, and the CLI leaves sentryCi.npmRuntime unset, so Test no longer builds it. The --help smoke test in Build npm Package goes too, since Test npm Package and E2E cover it. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/cli-build.yml | 20 ++++++-------------- docs/operations/github-actions.md | 11 +++++++---- packages/cli/package.json | 1 - 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/.github/workflows/cli-build.yml b/.github/workflows/cli-build.yml index 84cd6ac7f..92c020a14 100644 --- a/.github/workflows/cli-build.yml +++ b/.github/workflows/cli-build.yml @@ -275,12 +275,6 @@ jobs: env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} run: pnpm --filter sentry run bundle - - name: Smoke test - env: - SENTRY_AUTH_TOKEN: "" - SENTRY_TOKEN: "" - SENTRY_CONFIG_DIR: ${{ runner.temp }}/.sentry-smoke - run: node packages/cli/dist/bin.cjs --help - name: Pack working-directory: packages/cli run: npm pack @@ -321,17 +315,15 @@ jobs: uses: actions/download-artifact@v8 with: name: npm-package - path: packages/cli + path: ${{ runner.temp }}/npm-package - name: Test packaged CLI env: - SENTRY_AUTH_TOKEN: "" - SENTRY_TOKEN: "" - SENTRY_CONFIG_DIR: ${{ runner.temp }}/.sentry-smoke + RUNTIME_BIN: sentry + RUNTIME_PACKAGE: sentry run: | - package_file=$(find packages/cli -maxdepth 1 -name 'sentry-*.tgz' -print -quit) - test -n "$package_file" - npm install --ignore-scripts --prefix "$RUNNER_TEMP/npm-smoke" "$GITHUB_WORKSPACE/$package_file" - node "$RUNNER_TEMP/npm-smoke/node_modules/sentry/dist/bin.cjs" --help + packages=("$RUNNER_TEMP"/npm-package/sentry-*.tgz) + [[ "${#packages[@]}" == 1 ]] + RUNTIME_ARCHIVE="${packages[0]}" bash scripts/validate-npm-runtime.sh test-e2e: name: E2E Tests (Node 24) diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index 628a1b804..2297d8739 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -48,7 +48,9 @@ requires them to be skipped otherwise. The CLI E2E tests run in Runs on pull requests, pushes to `main` and `release/cli/**`, and manual dispatch. It builds the CLI artifacts that ship: `Build Binary` builds every target on `main` and release branches, and only `linux-x64` on pull requests. -`Build npm Package` packs the npm package. `E2E Tests (Node 24)` downloads the +`Build npm Package` packs the npm package, and `Test npm Package` validates that +package on Node 20, 22, and 24 with `scripts/validate-npm-runtime.sh`. +`E2E Tests (Node 24)` downloads the Linux binary, the npm package, and the generated sources from those jobs and runs `test/e2e` against them without building anything. `Build status` fails unless the builds, the npm package tests, and the E2E tests passed. @@ -177,9 +179,10 @@ project: A new workflow fits only when the matrix job cannot run the check: another operating system, a deployment, secrets, or pull request comments and labels. -The CLI's E2E tests are the exception: they must run against the binaries and -npm package that `cli-build.yml` builds for nightlies and releases, so -`packages/cli` sets `sentryCi.e2e` to `false` and that workflow runs them. +The CLI's E2E tests and npm package checks are the exception: they must run +against the binaries and npm package that `cli-build.yml` builds for nightlies +and releases, so `packages/cli` sets `sentryCi.e2e` to `false`, leaves +`sentryCi.npmRuntime` unset, and that workflow runs them. `scripts/workflow-policy.test.mjs` fails for a workflow that runs on pull requests unless its list names that workflow and the reason. diff --git a/packages/cli/package.json b/packages/cli/package.json index b09577aa0..4f766c19c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -53,7 +53,6 @@ "sentryCi": { "build": false, "e2e": false, - "npmRuntime": "bundle", "test": false, "typecheck": "ci:typecheck" }, From d3ded9c8889dc73c046b6bed34673c4176f46840 Mon Sep 17 00:00:00 2001 From: betegon Date: Tue, 3 Nov 2026 09:27:13 +0000 Subject: [PATCH 11/11] ci(cli): keep pull request builds from uploading sourcemaps SENTRY_AUTH_TOKEN is a repository secret, so Build's pull request runs received it and uploaded the binary's and npm bundle's sourcemaps to Sentry on every push. They also made the .gz release copies and uploaded a 34 MB sentry-linux-x64-gz artifact that nothing reads. Pull request builds now get neither the token nor RELEASE_BUILD and skip the compressed upload. The binary and the packed npm files are the same; only the upload and the .gz copies are skipped. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/cli-build.yml | 8 +++++--- docs/operations/github-actions.md | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cli-build.yml b/.github/workflows/cli-build.yml index 92c020a14..fce48a79e 100644 --- a/.github/workflows/cli-build.yml +++ b/.github/workflows/cli-build.yml @@ -103,8 +103,9 @@ jobs: fi - name: Build env: - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - RELEASE_BUILD: "1" + # Pull request builds skip the sourcemap upload and the .gz copies. + SENTRY_AUTH_TOKEN: ${{ github.event_name != 'pull_request' && secrets.SENTRY_AUTH_TOKEN || '' }} + RELEASE_BUILD: ${{ github.event_name != 'pull_request' && '1' || '' }} FOSSILIZE_SIGN: ${{ (github.ref_name == 'main' || startsWith(github.ref_name, 'release/cli/')) && 'y' || 'n' }} APPLE_CERT_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} @@ -150,6 +151,7 @@ jobs: if-no-files-found: error retention-days: 7 - name: Upload compressed artifact + if: github.event_name != 'pull_request' uses: actions/upload-artifact@v7 with: name: sentry-${{ matrix.target }}-gz @@ -273,7 +275,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Bundle env: - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_AUTH_TOKEN: ${{ github.event_name != 'pull_request' && secrets.SENTRY_AUTH_TOKEN || '' }} run: pnpm --filter sentry run bundle - name: Pack working-directory: packages/cli diff --git a/docs/operations/github-actions.md b/docs/operations/github-actions.md index 2297d8739..4a58b6995 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -48,7 +48,8 @@ requires them to be skipped otherwise. The CLI E2E tests run in Runs on pull requests, pushes to `main` and `release/cli/**`, and manual dispatch. It builds the CLI artifacts that ship: `Build Binary` builds every target on `main` and release branches, and only `linux-x64` on pull requests. -`Build npm Package` packs the npm package, and `Test npm Package` validates that +Pull request builds get no `SENTRY_AUTH_TOKEN`, so they upload no sourcemaps, +and they skip the compressed release copies. `Build npm Package` packs the npm package, and `Test npm Package` validates that package on Node 20, 22, and 24 with `scripts/validate-npm-runtime.sh`. `E2E Tests (Node 24)` downloads the Linux binary, the npm package, and the generated sources from those jobs and