From b058c5861fd0fea165d2bce78a38d30d48db5c32 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 27 May 2026 20:22:03 -0400 Subject: [PATCH] ci(#319): shallow checkout for docs/changeset lint workflows (#402) Lint workflows used fetch-depth:0 (full clone); switched to depth 50 + explicit base-ref fetch so the three-dot diff (origin/${base}...HEAD) has its merge-base; fails closed if merge-base is deeper than 50. Added policy test asserting fetch-depth:50 on both workflows. Fixes #319. Co-authored-by: Claude Opus 4.7 (1M context) --- .github/workflows/changeset-required.yml | 6 +- .github/workflows/docs-required.yml | 6 +- tests/policy-lint-shallow-checkout.test.cjs | 64 +++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/policy-lint-shallow-checkout.test.cjs diff --git a/.github/workflows/changeset-required.yml b/.github/workflows/changeset-required.yml index 52c91ff9d..18d524b42 100644 --- a/.github/workflows/changeset-required.yml +++ b/.github/workflows/changeset-required.yml @@ -18,7 +18,11 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 50 + - name: Fetch base ref for diff + run: git fetch --depth=50 origin "${BASE_REF}:refs/remotes/origin/${BASE_REF}" + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} - uses: actions/setup-node@v4 with: node-version: '24' diff --git a/.github/workflows/docs-required.yml b/.github/workflows/docs-required.yml index 8287403c6..5e10fd788 100644 --- a/.github/workflows/docs-required.yml +++ b/.github/workflows/docs-required.yml @@ -18,7 +18,11 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 50 + - name: Fetch base ref for diff + run: git fetch --depth=50 origin "${BASE_REF}:refs/remotes/origin/${BASE_REF}" + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} - uses: actions/setup-node@v4 with: node-version: '24' diff --git a/tests/policy-lint-shallow-checkout.test.cjs b/tests/policy-lint-shallow-checkout.test.cjs new file mode 100644 index 000000000..82a96b985 --- /dev/null +++ b/tests/policy-lint-shallow-checkout.test.cjs @@ -0,0 +1,64 @@ +'use strict'; + +/** + * Policy test: docs-required.yml and changeset-required.yml must use + * fetch-depth: 50 (NOT fetch-depth: 0) in their checkout steps. + * + * Rationale: both workflows run a lint script that performs a three-dot git diff + * (`git diff --name-only origin/${base}...HEAD`). A full-history clone + * (fetch-depth: 0) is wasteful — depth 50 covers >99% of PRs and is far faster. + * The explicit base-ref fetch step ensures the merge-base is present for the + * three-dot diff. The workflow FAILS CLOSED (lint errors) if the merge-base is + * deeper than 50, which is intentional. + * + * Note: security-scan.yml legitimately uses fetch-depth: 0 and is NOT covered + * by this test (see tests/security-scan.test.cjs). + */ + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const PROJECT_ROOT = path.join(__dirname, '..'); + +const WORKFLOWS = { + 'docs-required.yml': path.join(PROJECT_ROOT, '.github', 'workflows', 'docs-required.yml'), + 'changeset-required.yml': path.join( + PROJECT_ROOT, + '.github', + 'workflows', + 'changeset-required.yml', + ), +}; + +for (const [name, workflowPath] of Object.entries(WORKFLOWS)) { + describe(`${name} shallow-checkout policy`, () => { + let content; + + test('workflow file exists', () => { + assert.ok(fs.existsSync(workflowPath), `Missing workflow: ${workflowPath}`); + content = fs.readFileSync(workflowPath, 'utf-8'); + }); + + test('checkout uses fetch-depth: 50 (not 0)', () => { + if (!content) content = fs.readFileSync(workflowPath, 'utf-8'); + assert.ok( + content.includes('fetch-depth: 50'), + `${name}: checkout must use fetch-depth: 50 (got full-history clone with fetch-depth: 0 or missing)`, + ); + assert.ok( + !content.includes('fetch-depth: 0'), + `${name}: fetch-depth: 0 (full-history clone) must be replaced with fetch-depth: 50`, + ); + }); + + test('has explicit base-ref fetch step for three-dot diff merge-base', () => { + if (!content) content = fs.readFileSync(workflowPath, 'utf-8'); + assert.ok( + content.includes('Fetch base ref for diff'), + `${name}: must have an explicit "Fetch base ref for diff" step so the three-dot diff has its merge-base`, + ); + }); + }); +}