Skip to content

Commit 864e4b5

Browse files
authored
ci: add shared CI workflows, Claude Code review, and git hooks (#1)
- CI workflow calling reusable workflow from agent-sh/.github - Claude Code automated PR review (owner/member/collaborator only, max 3 runs) - Claude Code @mentions support - Pre-push hook running tests before push
1 parent c1f67a6 commit 864e4b5

File tree

5 files changed

+205
-0
lines changed

5 files changed

+205
-0
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
ci:
11+
uses: agent-sh/.github/.github/workflows/ci-node.yml@main
12+
secrets: inherit
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Claude Code - Automated PR Review
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, ready_for_review, reopened]
6+
7+
jobs:
8+
claude-review:
9+
if: >-
10+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'),
11+
github.event.pull_request.author_association)
12+
13+
runs-on: ubuntu-latest
14+
15+
permissions:
16+
contents: write
17+
pull-requests: write
18+
issues: write
19+
actions: read
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Check run count
28+
id: check-runs
29+
env:
30+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31+
REPO: ${{ github.repository }}
32+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
33+
EVENT_ACTION: ${{ github.event.action }}
34+
PR_NUMBER: ${{ github.event.pull_request.number }}
35+
run: |
36+
WORKFLOW_FILE="claude-code-review.yml"
37+
38+
RUN_COUNT=$(gh api \
39+
-H "Accept: application/vnd.github+json" \
40+
-H "X-GitHub-Api-Version: 2022-11-28" \
41+
"repos/${REPO}/actions/workflows/${WORKFLOW_FILE}/runs?head_sha=${HEAD_SHA}&status=completed" \
42+
--jq '[.workflow_runs[] | select(.event == "pull_request")] | length' || echo "")
43+
44+
RUN_COUNT=${RUN_COUNT:-0}
45+
if ! echo "$RUN_COUNT" | grep -qE '^[0-9]+$'; then
46+
echo "[WARN] Invalid RUN_COUNT value ('$RUN_COUNT') from GitHub API. Defaulting to 0."
47+
RUN_COUNT=0
48+
fi
49+
50+
RUN_COUNT=$((RUN_COUNT + 1))
51+
52+
echo "This is run #$RUN_COUNT for this PR (event: $EVENT_ACTION)"
53+
54+
if [ "$RUN_COUNT" -le 3 ]; then
55+
echo "should_run=true" >> $GITHUB_OUTPUT
56+
echo "[OK] Will run Claude review (run $RUN_COUNT of 3)"
57+
else
58+
echo "should_run=false" >> $GITHUB_OUTPUT
59+
echo "[SKIP] Skipping Claude review (already ran 3 times)"
60+
fi
61+
62+
- name: Run Claude Code Review
63+
if: steps.check-runs.outputs.should_run == 'true'
64+
uses: anthropics/claude-code-action@f64219702d7454cf29fe32a74104be6ed43dc637 # v1
65+
with:
66+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
67+
github_token: ${{ secrets.GITHUB_TOKEN }}
68+
model: claude-opus-4-6
69+
use_commit_signing: true
70+
71+
- name: Comment on PR (skipped)
72+
if: steps.check-runs.outputs.should_run == 'false'
73+
env:
74+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75+
PR_NUMBER: ${{ github.event.pull_request.number }}
76+
run: |
77+
gh pr comment "$PR_NUMBER" --body "[INFO] Claude Code review was skipped as it has already run 3 times for this PR."

.github/workflows/claude.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Claude Code - @mentions
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
pull_request_review_comment:
7+
types: [created]
8+
pull_request_review:
9+
types: [submitted]
10+
issues:
11+
types: [opened]
12+
13+
jobs:
14+
claude-mention:
15+
if: |
16+
(
17+
github.event_name == 'issue_comment' &&
18+
contains(github.event.comment.body, '@claude') &&
19+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
20+
) ||
21+
(
22+
github.event_name == 'pull_request_review_comment' &&
23+
contains(github.event.comment.body, '@claude') &&
24+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
25+
) ||
26+
(
27+
github.event_name == 'pull_request_review' &&
28+
contains(github.event.review.body, '@claude') &&
29+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)
30+
) ||
31+
(
32+
github.event_name == 'issues' &&
33+
(contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
34+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.issue.author_association)
35+
)
36+
37+
runs-on: ubuntu-latest
38+
39+
permissions:
40+
contents: write
41+
pull-requests: write
42+
issues: write
43+
actions: read
44+
45+
steps:
46+
- name: Checkout repository
47+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
48+
with:
49+
fetch-depth: 0
50+
51+
- name: Run Claude Code
52+
uses: anthropics/claude-code-action@f64219702d7454cf29fe32a74104be6ed43dc637 # v1
53+
with:
54+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
55+
github_token: ${{ secrets.GITHUB_TOKEN }}
56+
model: claude-opus-4-6
57+
use_commit_signing: true

scripts/pre-push-node

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/sh
2+
# Pre-push hook for Node.js projects
3+
# Runs tests before allowing push
4+
5+
cd "$(git rev-parse --show-toplevel)" || exit 1
6+
7+
echo "[INFO] Running pre-push tests..."
8+
npm test
9+
exit $?

scripts/setup-hooks.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/sh
2+
# Setup git hooks for agent-sh repos
3+
# Detects project type and installs the appropriate pre-push hook
4+
#
5+
# Usage: sh scripts/setup-hooks.sh
6+
7+
set -e
8+
9+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11+
12+
# Verify we are inside a git repository
13+
if ! git -C "$REPO_ROOT" rev-parse --git-dir >/dev/null 2>&1; then
14+
echo "[ERROR] Not a git repository: $REPO_ROOT"
15+
exit 1
16+
fi
17+
18+
# Resolve hooks directory (handles worktrees where .git is a file)
19+
if [ -f "$REPO_ROOT/.git" ]; then
20+
COMMON_DIR="$(git -C "$REPO_ROOT" rev-parse --git-common-dir)"
21+
HOOKS_DIR="$COMMON_DIR/hooks"
22+
else
23+
HOOKS_DIR="$REPO_ROOT/.git/hooks"
24+
fi
25+
26+
mkdir -p "$HOOKS_DIR"
27+
28+
# Detect project type and install hook
29+
if [ -f "$REPO_ROOT/Cargo.toml" ]; then
30+
echo "[INFO] Detected Rust project"
31+
if [ ! -f "$SCRIPT_DIR/pre-push-rust" ]; then
32+
echo "[ERROR] Missing hook script: $SCRIPT_DIR/pre-push-rust"
33+
exit 1
34+
fi
35+
cp "$SCRIPT_DIR/pre-push-rust" "$HOOKS_DIR/pre-push"
36+
chmod +x "$HOOKS_DIR/pre-push"
37+
echo "[OK] Installed pre-push hook (Rust)"
38+
elif [ -f "$REPO_ROOT/package.json" ]; then
39+
echo "[INFO] Detected Node.js project"
40+
if [ ! -f "$SCRIPT_DIR/pre-push-node" ]; then
41+
echo "[ERROR] Missing hook script: $SCRIPT_DIR/pre-push-node"
42+
exit 1
43+
fi
44+
cp "$SCRIPT_DIR/pre-push-node" "$HOOKS_DIR/pre-push"
45+
chmod +x "$HOOKS_DIR/pre-push"
46+
echo "[OK] Installed pre-push hook (Node.js)"
47+
else
48+
echo "[WARN] No package.json or Cargo.toml found - skipping hook install"
49+
exit 0
50+
fi

0 commit comments

Comments
 (0)