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 47402b236..40ce84d05 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,19 +122,16 @@ 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 - - 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 }} @@ -192,10 +184,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 != '' @@ -214,6 +206,125 @@ 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 + 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 @@ -227,19 +338,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 }} @@ -254,25 +361,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 }} @@ -290,6 +397,9 @@ jobs: project, npm-runtime, cli-asset-catalog, + cli-unit, + cli-coverage, + cli-e2e, ] runs-on: ubuntu-latest steps: @@ -304,22 +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 - [[ "$ASSET_CATALOG_RESULT" == "$expected" ]] + 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 b0e0484bb..a117509ad 100644 --- a/docs/operations/github-actions.md +++ b/docs/operations/github-actions.md @@ -21,6 +21,28 @@ 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. + +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 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..c42c0fa1b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -50,9 +50,11 @@ }, "packageManager": "pnpm@10.11.0", "sentryCi": { - "coverage": "coverage/lcov.info", + "build": false, + "e2e": false, "npmRuntime": "bundle", - "policy": "ci:policy" + "test": false, + "typecheck": "ci:typecheck" }, "scripts": { "tsx": "tsx --env-file-if-exists=.env.local --import ./script/require-shim.mjs", @@ -65,7 +67,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", @@ -93,6 +95,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 cc1396274..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,21 +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.policy, "ci:policy"); - assert.equal(cli.e2e, "test: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", () => { @@ -161,99 +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*\]/, - ); - 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/); - 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"/); - 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("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), - }, - }); - assert.equal( - result.status, - passes ? 0 : 1, - `selected=${selected}, native=${nativeResult}, projects=${count}`, - ); - } - }); - it("requires safe metadata and a single owner of each specialized role", () => { assert.throws( () =>