Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
110 changes: 110 additions & 0 deletions .github/workflows/pr-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: PR Pipeline

on:
pull_request:
types: [opened, synchronize, reopened]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]

jobs:
shellcheck:
name: ShellCheck
# Only run for the repo owner — block random fork PRs from consuming CI
if: github.event_name == 'pull_request' && github.actor == 'injectedfusion'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get changed shell scripts
id: changed
env:
BASE_REF: ${{ github.base_ref }}
run: |
files=$(git diff --name-only --diff-filter=ACMR "origin/$BASE_REF"...HEAD -- '*.sh' | tr '\n' ' ')
echo "files=$files" >> "$GITHUB_OUTPUT"
if [ -n "$files" ]; then
echo "has_files=true" >> "$GITHUB_OUTPUT"
fi

- name: Install shellcheck
if: steps.changed.outputs.has_files == 'true'
run: sudo apt-get install -y shellcheck

- name: Run shellcheck on changed scripts
if: steps.changed.outputs.has_files == 'true'
env:
CHANGED_FILES: ${{ steps.changed.outputs.files }}
run: |
exit_code=0
for f in $CHANGED_FILES; do
[ -f "$f" ] || continue
echo "::group::Checking $f"
shellcheck -S warning "$f" 2>&1
result=$?
echo "::endgroup::"
if [ $result -ne 0 ]; then
echo "::error file=$f::shellcheck found issues"
exit_code=1
fi
done
exit $exit_code

# --- Claude Review (waits for CI on PRs, runs directly on @claude mentions) ---

review:
needs: [shellcheck]
# Only the repo owner can trigger reviews — prevents billing abuse on public repo
if: |
always() && github.actor == 'injectedfusion' &&
(
(github.event_name == 'pull_request' &&
needs.shellcheck.result != 'failure') ||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment')
)
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1

- uses: anthropics/claude-code-action@v1
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true
prompt: |
REPO: ${{ env.REPO }}
PR NUMBER: ${{ env.PR_NUMBER }}

You are the sole code reviewer for this repository. Your review decision
determines whether this PR merges to the main branch.

This repo contains reusable pre-commit hooks (bash scripts + .pre-commit-hooks.yaml).
Review this PR focusing on:
- Shell script correctness and safety (quoting, error handling, set -euo pipefail)
- Security (no credential leaks, no unsafe eval/exec patterns)
- Hook configuration validity (.pre-commit-hooks.yaml fields)
- Documentation accuracy (comments match actual behavior)

After your review:
- Use inline comments for specific code issues.
- Post a single PR comment with your overall summary.
- If the PR is acceptable: approve it with `gh pr review --approve` and
then merge it with `gh pr merge --squash --auto`.
- If the PR has issues that must be fixed: request changes with
`gh pr review --request-changes` and do NOT merge.

claude_args: |
--model claude-haiku-4-5-20251001 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr review:*),Bash(gh pr merge:*)"
13 changes: 13 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,16 @@
language: script
types: [yaml]
stages: [pre-commit]

- id: require-signed-commits
name: require signed commits
description: >
Fails if commit.gpgsign is not true or user.signingkey is not set.
Enforces GPG/SSH commit signing discipline, especially important in
agentic AI workflows where unsigned commits can slip through
non-interactive shells.
entry: hooks/require-signed-commits.sh
language: script
always_run: true
pass_filenames: false
stages: [pre-commit]
39 changes: 39 additions & 0 deletions hooks/require-signed-commits.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# require-signed-commits — fail if the commit being created will be unsigned.
# Prevents unsigned commits from entering the repo, enforcing GPG/SSH signing
# discipline especially important in agentic AI workflows.
#
# Fails unless commit.gpgsign is explicitly set to true and user.signingkey is configured.

set -euo pipefail

# Check if signing is configured
gpgsign="$(git config --get commit.gpgsign 2>/dev/null || echo 'false')"

if [[ "$gpgsign" != "true" ]]; then
echo "✗ Unsigned commit blocked: commit.gpgsign is not set to true"
echo ""
echo "To enable commit signing:"
echo " git config --global commit.gpgsign true"
echo " git config --global user.signingkey <your-key>"
echo ""
echo "If using 1Password SSH agent:"
echo " git config --global gpg.format ssh"
echo " git config --global user.signingkey <your-public-key>"
echo ""
echo "To bypass (not recommended): git commit --no-verify"
exit 1
fi

# Check a signing key is set
signingkey="$(git config --get user.signingkey 2>/dev/null || echo '')"

if [[ -z "$signingkey" ]]; then
echo "✗ Unsigned commit blocked: commit.gpgsign=true but user.signingkey is not set"
echo ""
echo "Set your signing key:"
echo " git config --global user.signingkey <your-key>"
exit 1
fi

exit 0
Loading