diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e821a..7ef22a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [3.16.0] - 2026-07-05 + +### Added + +- **The scaffolded PR workflow prunes its own report branch, out of the box.** + `styleproof-init`'s `.github/workflows/styleproof.yml` now also runs on + `pull_request: closed`: when a PR closes, a `prune` job removes that PR's `pr-/` + folder from the report branch (`styleproof-reports`), so the branch no longer grows + without bound as PRs come and go — no adopter has to remember to garbage-collect it. + The report job is unchanged, only guarded to skip the close event. Covered by the + `styleproof-init` workflow test. + ## [3.15.0] - 2026-07-05 ### Changed diff --git a/action.yml b/action.yml index f30bf29..c45a491 100644 --- a/action.yml +++ b/action.yml @@ -16,7 +16,7 @@ inputs: description: 'Directory with restored or freshly captured PR-head maps to compare (.json.gz + .png).' required: true report-branch: - description: 'Orphan branch that stores reports (created on first run). One pr-/ folder per PR; never pruned.' + description: 'Orphan branch that stores reports (created on first run). One pr-/ folder per PR; the styleproof-init workflow prunes each on PR close.' default: 'styleproof-reports' github-token: description: 'Token used to push the report branch and post the PR comment.' diff --git a/bin/styleproof-init.mjs b/bin/styleproof-init.mjs index e2fa311..98a6f91 100755 --- a/bin/styleproof-init.mjs +++ b/bin/styleproof-init.mjs @@ -345,7 +345,9 @@ const CI_WORKFLOW = `name: StyleProof # report without a browser; # - on cache miss, CI recaptures both sides in one pinned environment so the # comparison stays valid. -on: pull_request +on: + pull_request: + types: [opened, synchronize, reopened, closed] permissions: contents: write @@ -355,6 +357,8 @@ permissions: jobs: styleproof: + # Report on open/update; the prune job below handles close. + if: github.event.action != 'closed' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -408,6 +412,41 @@ ${PM.setup} baseline-dir: __stylemaps__/base fresh-dir: __stylemaps__/head require-approval: true + + prune: + # PR closed: drop its pr-/ folder from the report branch so the branch + # never grows without bound. Keep BRANCH in sync with the report-branch + # input above (default: styleproof-reports). + if: github.event.action == 'closed' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Prune this PR's report folder + shell: bash + env: + GH_TOKEN: \${{ github.token }} + BRANCH: styleproof-reports + PR: \${{ github.event.pull_request.number }} + run: | + set -euo pipefail + REMOTE="https://x-access-token:\${GH_TOKEN}@github.com/\${{ github.repository }}.git" + if ! git ls-remote --exit-code "$REMOTE" "refs/heads/$BRANCH" >/dev/null 2>&1; then + echo "No $BRANCH branch yet — nothing to prune."; exit 0 + fi + TMP="$(mktemp -d)" + # Blobless clone keeps this fast; a very large report branch may prefer + # a --no-checkout plumbing rewrite instead. + git clone --filter=blob:none --single-branch --branch "$BRANCH" "$REMOTE" "$TMP" + cd "$TMP" + if [ ! -d "pr-$PR" ]; then + echo "No pr-$PR/ folder — nothing to prune."; exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git rm -r --quiet "pr-$PR" + git commit -m "chore(styleproof): prune report for closed PR #$PR" + git push origin "$BRANCH" `; function writeFileSafe(file, contents, { force: f } = {}) { diff --git a/package.json b/package.json index c64a884..b03b364 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "styleproof", - "version": "3.15.0", + "version": "3.16.0", "description": "Catch every CSS change before it ships — review PRs and certify refactors by the browser's computed styles, not pixels. Works with any styling system.", "keywords": [ "playwright", diff --git a/test/init.test.mjs b/test/init.test.mjs index 649e05f..7929344 100644 --- a/test/init.test.mjs +++ b/test/init.test.mjs @@ -132,6 +132,13 @@ for (const manager of [ const workflow = readFile(root, '.github/workflows/styleproof.yml'); for (const pattern of manager.workflow) assert.match(workflow, pattern); for (const pattern of manager.absent ?? []) assert.doesNotMatch(workflow, pattern); + + // Report branch self-prunes on PR close (out of the box) — manager-independent. + assert.match(workflow, /types: \[opened, synchronize, reopened, closed\]/); + assert.match(workflow, /if: github\.event\.action != 'closed'/); // report skips close + assert.match(workflow, /^\s{2}prune:/m); + assert.match(workflow, /if: github\.event\.action == 'closed'/); + assert.match(workflow, /git rm -r --quiet "pr-\$PR"/); } finally { rmTmp(root); }