diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml index 9e0987fbd5..2da0c5c5b4 100644 --- a/.github/workflows/mutation-testing.yml +++ b/.github/workflows/mutation-testing.yml @@ -36,13 +36,6 @@ jobs: fetch-depth: 0 persist-credentials: false - - name: Fetch pull request base - if: github.event_name == 'pull_request' - env: - BASE_REPOSITORY_URL: ${{ github.server_url }}/${{ github.repository }}.git - BASE_SHA: ${{ github.event.pull_request.base.sha }} - run: git fetch --no-tags "$BASE_REPOSITORY_URL" "$BASE_SHA" - - name: Setup Node.js and pnpm if: github.event_name == 'pull_request' uses: ./.github/actions/setup-node-pnpm @@ -56,9 +49,10 @@ jobs: - name: Enforce executable-line scope and run advisory mutation testing if: github.event_name == 'pull_request' env: - BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.sha }} - run: node scripts/stryker-diff.mjs ci --base "$BASE_SHA" --head "$HEAD_SHA" + run: | + BASE_SHA="$(git rev-parse "$HEAD_SHA^1")" + node scripts/stryker-diff.mjs ci --base "$BASE_SHA" --head "$HEAD_SHA" - name: Upload mutation reports id: mutation_report diff --git a/scripts/stryker-diff.test.mjs b/scripts/stryker-diff.test.mjs index 403cf62d89..deaba510a9 100644 --- a/scripts/stryker-diff.test.mjs +++ b/scripts/stryker-diff.test.mjs @@ -57,6 +57,8 @@ describe("mutation testing workflow", () => { assert.ok(!workflow.includes("ref: ${{ github.event.pull_request.head.sha }}")) assert.ok(workflow.includes("HEAD_SHA: ${{ github.sha }}")) assert.ok(!workflow.includes("HEAD_SHA: ${{ github.event.pull_request.head.sha }}")) + assert.ok(workflow.includes('BASE_SHA="$(git rev-parse "$HEAD_SHA^1")"')) + assert.ok(!workflow.includes("github.event.pull_request.base.sha")) assert.ok(workflow.includes("steps.mutation_report.outputs.artifact-url")) assert.ok(workflow.includes("open the package's mutation.html file")) assert.ok(workflow.includes("Enforce executable-line scope and run advisory mutation testing")) @@ -444,6 +446,49 @@ describe("selectFromGit", () => { fs.rmSync(repo, { recursive: true, force: true }) } }) + + it("does not charge intervening base-branch changes to the pull request", () => { + const repo = fs.mkdtempSync(path.join(os.tmpdir(), "stryker-stale-base-")) + const runGit = (...args) => execFileSync("git", args, { cwd: repo, encoding: "utf8" }).trim() + + try { + runGit("init", "--initial-branch=main") + runGit("config", "user.name", "Mutation Test") + runGit("config", "user.email", "mutation@example.com") + fs.mkdirSync(path.join(repo, "packages/core/src"), { recursive: true }) + fs.writeFileSync(path.join(repo, "packages/core/src/pr.ts"), "export const pr = false\n") + fs.writeFileSync(path.join(repo, "packages/core/src/base.ts"), "export const base = false\n") + runGit("add", ".") + runGit("commit", "-m", "initial") + const staleBaseSha = runGit("rev-parse", "HEAD") + + runGit("checkout", "-b", "feature") + fs.writeFileSync(path.join(repo, "packages/core/src/pr.ts"), "export const pr = true\n") + runGit("commit", "-am", "change pull request") + + runGit("checkout", "main") + fs.writeFileSync(path.join(repo, "packages/core/src/base.ts"), "export const base = true\n") + runGit("commit", "-am", "advance base branch") + const currentBaseSha = runGit("rev-parse", "HEAD") + runGit("merge", "--no-ff", "feature", "-m", "synthetic pull request merge") + const mergeSha = runGit("rev-parse", "HEAD") + const mergeResultBaseSha = runGit("rev-parse", `${mergeSha}^1`) + assert.equal(mergeResultBaseSha, currentBaseSha) + + assert.deepEqual( + selectFromGit(repo, staleBaseSha, mergeSha).packages[0].files.map(({ path: filePath }) => filePath), + ["packages/core/src/base.ts", "packages/core/src/pr.ts"], + ) + assert.deepEqual( + selectFromGit(repo, mergeResultBaseSha, mergeSha).packages[0].files.map( + ({ path: filePath }) => filePath, + ), + ["packages/core/src/pr.ts"], + ) + } finally { + fs.rmSync(repo, { recursive: true, force: true }) + } + }) }) describe("mutation exclusions", () => {