Skip to content

Commit 172fbdf

Browse files
Tony363claude
andcommitted
feat: add AI code review, issue triage, and release notes workflows
Add 5 AI-powered GitHub Actions workflows: - claude-review-phase1: comment-only PR review (~$1.50/PR) - claude-review-phase2: sensitive file detection + review (~$1.50/PR) - claude-review-phase3: draft fix PR, opt-in via 'ai-fix' label ($3-8/PR) - ai-issue-triage: auto-label issues with Claude (~$1.50/issue) - release-notes: AI-generated release notes (~$1.50/release) Security hardening per review feedback: - All actions pinned to commit SHA (not mutable tags) - Dependabot configured for weekly action updates - Phase 3 label-gated to 'ai-fix' (no triple-trigger cost) - GITHUB_TOKEN used everywhere (no PAT_TOKEN, prevents recursive loops) - Secret validation uses env-block pattern (no shell expansion) - Fork PRs detected and skipped cleanly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 87075b8 commit 172fbdf

File tree

8 files changed

+779
-0
lines changed

8 files changed

+779
-0
lines changed

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"

.github/test-events/pr-opened.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"action": "opened",
3+
"pull_request": {
4+
"number": 100,
5+
"title": "fix: handle missing GPU in dashboard-api",
6+
"body": "Fixes #999",
7+
"html_url": "https://github.com/test/test/pull/100",
8+
"head": {
9+
"ref": "fix/gpu-endpoint",
10+
"sha": "abc1234"
11+
},
12+
"base": {
13+
"ref": "main"
14+
},
15+
"user": {
16+
"login": "testuser"
17+
},
18+
"labels": [],
19+
"draft": false
20+
}
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"action": "created",
3+
"release": {
4+
"tag_name": "v0.1.0",
5+
"name": "v0.1.0",
6+
"body": "",
7+
"draft": false,
8+
"prerelease": false
9+
}
10+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: AI Issue Triage
2+
3+
# Auto-label and categorize new issues using Claude
4+
# Advisory mode only — adds labels, doesn't close or modify issues
5+
# Estimated cost: ~$1.50/issue
6+
7+
on:
8+
issues:
9+
types: [opened]
10+
11+
concurrency:
12+
group: issue-triage-${{ github.event.issue.number }}
13+
cancel-in-progress: false
14+
15+
jobs:
16+
triage:
17+
name: Triage Issue
18+
runs-on: ubuntu-latest
19+
if: >-
20+
github.event.issue.user.login != 'github-actions[bot]' &&
21+
github.event.issue.user.login != 'dependabot[bot]' &&
22+
github.event.issue.user.login != 'claude[bot]'
23+
permissions:
24+
issues: write
25+
contents: read
26+
27+
steps:
28+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
29+
with:
30+
sparse-checkout: |
31+
dream-server/
32+
CLAUDE.md
33+
sparse-checkout-cone-mode: true
34+
35+
- name: Validate required secrets
36+
env:
37+
HAS_API_KEY: ${{ secrets.ANTHROPIC_API_KEY != '' }}
38+
run: |
39+
if [ "$HAS_API_KEY" != "true" ]; then
40+
echo "::error::ANTHROPIC_API_KEY not configured"
41+
exit 1
42+
fi
43+
echo "Required secrets present"
44+
45+
- name: AI Triage
46+
uses: anthropics/claude-code-action@88c168b39e7e64da0286d812b6e9fbebb6708185 # v1
47+
with:
48+
github_token: ${{ secrets.GITHUB_TOKEN }}
49+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
50+
prompt: |
51+
You are an issue triage bot for the DreamServer project (fully local AI stack: LLM inference, chat, voice, agents, workflows, RAG, image generation, privacy tools).
52+
53+
Architecture context:
54+
- dream-server/installers/ = Bash installer libraries and phases
55+
- dream-server/dream-cli = main CLI tool (~45K lines Bash)
56+
- dream-server/extensions/services/dashboard-api/ = Python FastAPI backend
57+
- dream-server/extensions/services/dashboard/ = React/Vite frontend
58+
- dream-server/scripts/ = operational scripts
59+
- dream-server/config/ = backend configs (GPU tiers, models)
60+
- dream-server/tests/ = shell-based tests (BATS, contracts, smoke)
61+
- dream-server/extensions/services/ = 17 service extensions with manifests
62+
63+
Analyze this new issue and apply appropriate labels:
64+
65+
**Issue #${{ github.event.issue.number }}**
66+
**Title**: ${{ github.event.issue.title }}
67+
**Body**: ${{ github.event.issue.body }}
68+
69+
## Instructions
70+
71+
1. Read the issue title and body carefully
72+
2. Determine the appropriate labels from this list:
73+
- **Type labels** (pick one): `bug`, `enhancement`, `question`, `documentation`
74+
- **Component labels** (pick all that apply): `installer`, `cli`, `dashboard`, `dashboard-api`, `extensions`, `docker`, `scripts`, `tests`, `docs`, `ci-cd`
75+
- **Priority labels** (pick one): `priority:high`, `priority:medium`, `priority:low`
76+
3. Apply the labels using: `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2"`
77+
4. Do NOT close, assign, or comment on the issue — only add labels
78+
claude_args: >-
79+
--allowedTools
80+
"Bash(gh issue edit *),Read,Glob,Grep"
81+
--max-turns 5
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Claude Review - Phase 1 (Comment Only)
2+
3+
# Phase 1: Basic Claude Code Review with comment-only mode
4+
# Safe starting point — no PR creation, just review comments
5+
# Estimated cost: ~$1.50/PR | Latency: 2-5 minutes
6+
7+
on:
8+
pull_request:
9+
types: [opened, ready_for_review]
10+
issue_comment:
11+
types: [created]
12+
13+
jobs:
14+
claude-review:
15+
if: |
16+
github.actor != 'claude[bot]' &&
17+
github.actor != 'dependabot[bot]' &&
18+
github.actor != 'github-actions[bot]' &&
19+
(
20+
github.event_name == 'pull_request' ||
21+
(github.event_name == 'issue_comment' &&
22+
github.event.issue.pull_request &&
23+
contains(github.event.comment.body, '@claude-review'))
24+
)
25+
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 15
28+
29+
concurrency:
30+
group: claude-review-${{ github.event.pull_request.number || github.event.issue.number }}
31+
cancel-in-progress: true
32+
33+
steps:
34+
- name: Checkout code
35+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
36+
with:
37+
fetch-depth: 0
38+
39+
- name: Check PR size (cost control)
40+
id: pr_size
41+
run: |
42+
BASE_REF="${{ github.base_ref || github.event.repository.default_branch || 'main' }}"
43+
FILES_CHANGED=$(git diff --name-only origin/${BASE_REF}...HEAD | wc -l)
44+
LINES_CHANGED=$(git diff --stat origin/${BASE_REF}...HEAD | tail -1 | awk '{print $4+$6}')
45+
46+
echo "files_changed=$FILES_CHANGED" >> $GITHUB_OUTPUT
47+
echo "lines_changed=$LINES_CHANGED" >> $GITHUB_OUTPUT
48+
49+
if [ "$LINES_CHANGED" -gt 1000 ]; then
50+
echo "skip_review=true" >> $GITHUB_OUTPUT
51+
echo "::warning::PR too large for AI review (>1000 lines). Add 'force-review' label to override."
52+
else
53+
echo "skip_review=false" >> $GITHUB_OUTPUT
54+
fi
55+
56+
- name: Skip large PR notice
57+
if: steps.pr_size.outputs.skip_review == 'true' && !contains(github.event.pull_request.labels.*.name, 'force-review')
58+
run: |
59+
echo "::notice::Skipping review for large PR. Add 'force-review' label to override."
60+
exit 0
61+
62+
- name: Run Claude Code Review
63+
if: steps.pr_size.outputs.skip_review == 'false' || contains(github.event.pull_request.labels.*.name, 'force-review')
64+
uses: anthropics/claude-code-action@88c168b39e7e64da0286d812b6e9fbebb6708185 # v1
65+
with:
66+
claude_args: |
67+
code-review --comment \
68+
--model claude-opus-4-5-20251101 \
69+
--max-turns 20 \
70+
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git blame *),Read"
71+
72+
github_token: ${{ secrets.GITHUB_TOKEN }}
73+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
74+
75+
- name: Review metrics
76+
if: always()
77+
run: |
78+
echo "### Review Metrics" >> $GITHUB_STEP_SUMMARY
79+
echo "- Files changed: ${{ steps.pr_size.outputs.files_changed }}" >> $GITHUB_STEP_SUMMARY
80+
echo "- Lines changed: ${{ steps.pr_size.outputs.lines_changed }}" >> $GITHUB_STEP_SUMMARY
81+
echo "- Estimated cost: ~\$1.50" >> $GITHUB_STEP_SUMMARY
82+
echo "- Phase: 1 (Comment Only)" >> $GITHUB_STEP_SUMMARY
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
name: Claude Review - Phase 2 (Sensitive File Detection)
2+
3+
# Phase 2: Claude Review with enhanced detection for security-sensitive files
4+
# Flags high-stakes changes for extra human review attention
5+
# Estimated cost: ~$1.50/PR
6+
7+
on:
8+
pull_request:
9+
types: [opened, ready_for_review, synchronize]
10+
11+
jobs:
12+
detect-high-stakes:
13+
name: Detect High-Stakes Changes
14+
runs-on: ubuntu-latest
15+
outputs:
16+
is_high_stakes: ${{ steps.check.outputs.is_high_stakes }}
17+
sensitive_files: ${{ steps.check.outputs.sensitive_files }}
18+
reason: ${{ steps.check.outputs.reason }}
19+
is_fork: ${{ steps.fork-check.outputs.is_fork }}
20+
21+
steps:
22+
- name: Check if fork PR
23+
id: fork-check
24+
run: |
25+
if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
26+
echo "is_fork=true" >> $GITHUB_OUTPUT
27+
echo "::notice::Fork PR detected — Claude review requires repo secrets and will be skipped"
28+
else
29+
echo "is_fork=false" >> $GITHUB_OUTPUT
30+
fi
31+
32+
- name: Checkout code
33+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
34+
with:
35+
fetch-depth: 0
36+
37+
- name: Check for high-stakes changes
38+
id: check
39+
run: |
40+
HIGH_STAKES_PATTERNS=(
41+
"dream-server/installers/"
42+
"dream-server/dream-cli"
43+
"dream-server/config/"
44+
"dream-server/extensions/services/dashboard-api/security.py"
45+
".github/workflows/"
46+
".env"
47+
"docker-compose"
48+
)
49+
50+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref || github.event.repository.default_branch || 'main' }}...HEAD)
51+
52+
IS_HIGH_STAKES="false"
53+
SENSITIVE_FILES=""
54+
REASON=""
55+
56+
for pattern in "${HIGH_STAKES_PATTERNS[@]}"; do
57+
if echo "$CHANGED_FILES" | grep -i "$pattern" > /dev/null; then
58+
IS_HIGH_STAKES="true"
59+
SENSITIVE_FILES=$(echo "$CHANGED_FILES" | grep -i "$pattern" | head -5)
60+
REASON="Security-sensitive files detected: $pattern"
61+
break
62+
fi
63+
done
64+
65+
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'ai-consensus') }}" == "true" ]]; then
66+
IS_HIGH_STAKES="true"
67+
REASON="Manual review escalation requested via label"
68+
fi
69+
70+
echo "is_high_stakes=$IS_HIGH_STAKES" >> $GITHUB_OUTPUT
71+
echo "sensitive_files<<EOF" >> $GITHUB_OUTPUT
72+
echo "$SENSITIVE_FILES" >> $GITHUB_OUTPUT
73+
echo "EOF" >> $GITHUB_OUTPUT
74+
echo "reason=$REASON" >> $GITHUB_OUTPUT
75+
76+
if [ "$IS_HIGH_STAKES" == "true" ]; then
77+
echo "::notice::High-stakes changes detected — flagging for extra review"
78+
fi
79+
80+
claude-review:
81+
name: Claude Code Review
82+
needs: detect-high-stakes
83+
if: |
84+
needs.detect-high-stakes.outputs.is_fork != 'true' &&
85+
github.actor != 'claude[bot]' &&
86+
github.actor != 'dependabot[bot]' &&
87+
github.actor != 'github-actions[bot]'
88+
89+
runs-on: ubuntu-latest
90+
timeout-minutes: 15
91+
92+
concurrency:
93+
group: claude-review-p2-${{ github.event.pull_request.number }}
94+
cancel-in-progress: true
95+
96+
steps:
97+
- name: Checkout code
98+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
99+
with:
100+
fetch-depth: 0
101+
102+
- name: Run Claude Code Review
103+
id: review
104+
uses: anthropics/claude-code-action@88c168b39e7e64da0286d812b6e9fbebb6708185 # v1
105+
with:
106+
claude_args: |
107+
code-review \
108+
--model claude-opus-4-5-20251101 \
109+
--max-turns 20 \
110+
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git blame *),Read"
111+
112+
github_token: ${{ secrets.GITHUB_TOKEN }}
113+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
114+
115+
- name: Save review artifact (high-stakes)
116+
if: needs.detect-high-stakes.outputs.is_high_stakes == 'true'
117+
run: |
118+
mkdir -p /tmp/review-artifacts
119+
echo "high-stakes review completed" > /tmp/review-artifacts/claude-review.txt
120+
121+
- name: Upload review artifact
122+
if: needs.detect-high-stakes.outputs.is_high_stakes == 'true'
123+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
124+
with:
125+
name: claude-review-result
126+
path: /tmp/review-artifacts/
127+
128+
review-summary:
129+
name: Review Summary
130+
needs: [detect-high-stakes, claude-review]
131+
if: always() && needs.detect-high-stakes.outputs.is_fork != 'true'
132+
runs-on: ubuntu-latest
133+
permissions:
134+
pull-requests: write
135+
136+
steps:
137+
- name: Post high-stakes notice
138+
if: needs.detect-high-stakes.outputs.is_high_stakes == 'true'
139+
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
140+
with:
141+
github-token: ${{ secrets.GITHUB_TOKEN }}
142+
script: |
143+
const reason = `${{ needs.detect-high-stakes.outputs.reason }}`;
144+
const sensitiveFiles = `${{ needs.detect-high-stakes.outputs.sensitive_files }}`;
145+
146+
await github.rest.issues.createComment({
147+
owner: context.repo.owner,
148+
repo: context.repo.repo,
149+
issue_number: context.payload.pull_request.number,
150+
body: `## Sensitive Files Detected
151+
152+
**Trigger**: ${reason}
153+
154+
**Files flagged**:
155+
\`\`\`
156+
${sensitiveFiles}
157+
\`\`\`
158+
159+
Extra human review is recommended for this PR.
160+
161+
---
162+
**Phase**: 2 (Sensitive File Detection) | **Estimated Cost**: ~$1.50`
163+
});
164+
165+
- name: Generate summary
166+
run: |
167+
echo "### Review Complete" >> $GITHUB_STEP_SUMMARY
168+
echo "" >> $GITHUB_STEP_SUMMARY
169+
echo "- **Phase**: 2 (Sensitive File Detection)" >> $GITHUB_STEP_SUMMARY
170+
echo "- **High-Stakes**: ${{ needs.detect-high-stakes.outputs.is_high_stakes }}" >> $GITHUB_STEP_SUMMARY
171+
if [ "${{ needs.detect-high-stakes.outputs.is_high_stakes }}" == "true" ]; then
172+
echo "- **Reason**: ${{ needs.detect-high-stakes.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
173+
fi
174+
echo "- **Estimated Cost**: ~\$1.50" >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)