Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2f925e8
fix(ci): add pr-review.yml to .prettierignore and use GH_PAT_WORKFLOW…
don-petry Oct 13, 2026
960d907
fix(ci): isolate GH_PAT_WORKFLOWS to push step to prevent credential …
don-petry Oct 13, 2026
2d50290
fix(ci): prevent branch name injection in git push with token
don-petry Oct 13, 2026
a2c9835
fix(ci): do not persist checkout credentials in autofix job
don-petry Oct 13, 2026
2478e66
fix(reviews): push autofix from a clean checkout job so PR hooks neve…
don-petry Oct 13, 2026
1976d10
fix(reviews): pass autofix patch via artifact instead of job output
don-petry Oct 13, 2026
770c369
fix(reviews): require GH_PAT_WORKFLOWS for autofix push
don-petry Oct 13, 2026
ddddcbc
Merge main (org renamed to combsmith) into fix/ci-autofix-token-and-p…
don-petry Oct 14, 2026
f352fc7
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
don-petry Oct 14, 2026
f26af8c
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
don-petry Oct 14, 2026
1849bde
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
don-petry Oct 14, 2026
8685d28
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
don-petry Oct 16, 2026
d7f4068
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
don-petry Oct 21, 2026
ede291d
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
rachel-petry Oct 23, 2026
9de7d48
fix(ci): restrict autofix patch to non-workflow tracked files and pre…
rachel-petry Oct 23, 2026
a87b9f5
fix(ci): harden autofix workflow-path guard with NUL-delimited checks
rachel-petry Oct 23, 2026
2757d3b
fix(ci): verify staged autofix tree before upload and guard against s…
rachel-petry Oct 24, 2026
51780d2
fix(ci): reduce autofix-push GITHUB_TOKEN permission to contents: read
rachel-petry Oct 24, 2026
ed8bea7
Merge branch 'main' into fix/ci-autofix-token-and-prettierignore
don-petry Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 79 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ jobs:
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: write
contents: read
outputs:
has_patch: ${{ steps.patch.outputs.has_patch }}
head_sha: ${{ steps.patch.outputs.head_sha }}

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v6
Expand All @@ -87,21 +90,87 @@ jobs:
npm run format
npm run lint -- --fix

- name: Commit and push auto-fixes
# No push token is available in this job: PR-controlled tooling (npm
# scripts, git hooks) runs here, so the fixes are exported as a patch and
# applied + pushed by the separate 'autofix-push' job on a clean checkout.
- name: Export auto-fixes as patch
id: patch
run: |
if git diff --quiet; then
echo "No formatting or lint fixes needed."
git add -u -- . ':(exclude).github/workflows'
git diff --cached --binary > "${RUNNER_TEMP}/autofix.patch"
git checkout -- .
echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
if [ -s "${RUNNER_TEMP}/autofix.patch" ]; then
echo "has_patch=true" >> "$GITHUB_OUTPUT"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "chore(ci): apply prettier/eslint auto-fixes"
git push
echo "has_patch=false" >> "$GITHUB_OUTPUT"
fi

- name: Verify no remaining issues after auto-fix
run: npm run check

# Passed as an artifact: job outputs are capped at ~1 MB.
- name: Upload auto-fix patch
if: steps.patch.outputs.has_patch == 'true'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: autofix-patch
path: ${{ runner.temp }}/autofix.patch
if-no-files-found: error

autofix-push:
needs: autofix
if: needs.autofix.outputs.has_patch == 'true'
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout code (clean, no PR-installed tooling)
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.ref }}
persist-credentials: false

- name: Download auto-fix patch
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: autofix-patch
path: ${{ runner.temp }}/autofix-patch

- name: Commit and push auto-fixes
env:
GH_TOKEN: ${{ secrets.GH_PAT_DON_PETRY || secrets.GH_PAT_WORKFLOWS }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
EXPECTED_SHA: ${{ needs.autofix.outputs.head_sha }}
run: |
if [ -z "${GH_TOKEN}" ]; then
echo "::error::GH_PAT_DON_PETRY (or legacy GH_PAT_WORKFLOWS) is required for autofix pushes."
exit 1
fi
if [ "$(git rev-parse HEAD)" != "${EXPECTED_SHA}" ]; then
echo "::notice::PR head moved from ${EXPECTED_SHA} to $(git rev-parse HEAD); skipping stale autofix push."
exit 0
fi
numstat_file="${RUNNER_TEMP}/autofix-numstat.bin"
if ! git apply --numstat -z "${RUNNER_TEMP}/autofix-patch/autofix.patch" > "${numstat_file}"; then
echo "::error::unable to inspect autofix patch; refusing to push."
exit 1
fi
if tr '\0' '\n' < "${numstat_file}" | grep -Eiq '^(([0-9]+|-)[[:space:]]+([0-9]+|-)[[:space:]]+)?(\./)?\.github/workflows/'; then
echo "::error::autofix patch touches workflow files; refusing to push."
exit 1
fi
git apply --index "${RUNNER_TEMP}/autofix-patch/autofix.patch"
if git diff --cached --name-only -z | tr '\0' '\n' | grep -Eiq '^(\./)?\.github/workflows/'; then
echo "::error::autofix patch touches workflow files; refusing to push."
exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -m "chore(ci): apply prettier/eslint auto-fixes"
git push -- "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" "HEAD:refs/heads/${HEAD_REF}"

secret-scan:
name: Secret scan (gitleaks)
runs-on: ubuntu-latest
Expand Down
9 changes: 4 additions & 5 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@
# reflow comments, re-introducing drift, so it must not reformat this file.
.github/workflows/persona-mention.yml
# pr-review is a standards-owned caller stub copied VERBATIM from
# combsmith/.github/standards/workflows/pr-review.yml. The canonical uses
# double-quoted YAML scalars and a two-space gap before the `# NOSONAR` markers;
# Prettier would rewrite both, re-introducing drift, and the CI autofix job
# cannot push workflow-file changes (no `workflows` permission), so it must not
# reformat this file.
# combsmith/.github/standards/workflows/pr-review.yml. Its exact bytes
# (including the two-space gap before the `# NOSONAR` marker) are enforced by the
# central byte-identity stub-drift guard. Prettier would collapse that gap and
# reflow comments, re-introducing drift, so it must not reformat this file.
.github/workflows/pr-review.yml
Loading