diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e2e5bb47bc..2a6342aabd 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -14,7 +14,8 @@ permissions: concurrency: group: codeql-production-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + # Let a main-branch baseline finish; supersede stale pull-request scans only. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: analyze: @@ -52,7 +53,7 @@ jobs: threads: 2 ram: 4096 - name: Retain complete analysis evidence - if: always() + if: always() && !cancelled() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: codeql-production-${{ matrix.language }} diff --git a/docs/development/testing.md b/docs/development/testing.md index ea39eeb826..c473e01c33 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -407,6 +407,10 @@ relying on the new scope. While the variable is unset, the existing default setup continues scanning. Adding the configuration file alone does not change default setup in this user-owned repository. +Pull-request scans are cancelled when superseded. Active scans on `main` finish +so frequent pushes cannot prevent a complete baseline from being uploaded. +Cancelled runs skip the SARIF artifact upload when no report was produced. + Each analysis uses two threads and a 4096 MiB CodeQL memory budget; language jobs run one at a time. Complete SARIF reports are retained for seven days. For a scope comparison, use the same source SHA, CodeQL bundle, default query diff --git a/tests/boundary/filesystem/ciCodeqlScope.test.ts b/tests/boundary/filesystem/ciCodeqlScope.test.ts index 0374672db7..ddc15c85c3 100644 --- a/tests/boundary/filesystem/ciCodeqlScope.test.ts +++ b/tests/boundary/filesystem/ciCodeqlScope.test.ts @@ -85,6 +85,9 @@ it("uses the scope in a bounded workflow gated on administrator activation", asy const workflow = z .object({ permissions: z.object({ contents: z.literal("read") }), + concurrency: z.object({ + "cancel-in-progress": z.string(), + }), jobs: z.object({ analyze: z.object({ if: z.string(), @@ -108,6 +111,9 @@ it("uses the scope in a bounded workflow gated on administrator activation", asy .parse( parse(await readFile(new URL("workflows/codeql.yml", github), "utf8")), ); + expect(workflow.concurrency["cancel-in-progress"]).toBe( + "${{ github.event_name == 'pull_request' }}", + ); const job = workflow.jobs.analyze; expect(job.if).toBe("vars.REA_CODEQL_ADVANCED == 'true'"); expect(job.strategy.matrix.language).toEqual([