Skip to content
Closed
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
1 change: 1 addition & 0 deletions .github/workflows/ci-failure-analyst-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,5 @@ jobs:
claude --print \
--permission-mode bypassPermissions \
--allowedTools Bash \
--fallback-model opus \
-p "$PROMPT"
2 changes: 1 addition & 1 deletion .github/workflows/daily-pr-review-health.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
node-version: '20'

- name: Cache claude-code CLI
uses: actions/cache@v5.0.5
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.npm-global
key: claude-code-${{ env.CLAUDE_CODE_VERSION }}-${{ runner.os }}
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/dev-lead-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ jobs:
GITHUB_EVENT_NAME: ${{ github.event_name }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
run: bash .dev-lead/scripts/dev-lead-intent.sh

Expand Down Expand Up @@ -112,6 +113,7 @@ jobs:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GH_PAT_WORKFLOWS: ${{ secrets.GH_PAT_WORKFLOWS }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GH_PAT: ${{ secrets.GH_PAT }}
run: bash .dev-lead/scripts/dev-lead-preflight.sh

Expand Down Expand Up @@ -164,6 +166,7 @@ jobs:
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-ci.sh
Expand All @@ -179,6 +182,7 @@ jobs:
TRIGGERING_REVIEWER: ${{ env.INTENT_ACTOR }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh
Expand All @@ -193,6 +197,7 @@ jobs:
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh
Expand All @@ -207,6 +212,7 @@ jobs:
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh
Expand All @@ -221,6 +227,7 @@ jobs:
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh
Expand All @@ -234,6 +241,7 @@ jobs:
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-issue.sh
Expand All @@ -247,6 +255,7 @@ jobs:
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/dev-lead.yml
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,17 @@ jobs:
# ── enable-auto-merge ─────────────────────────────────────────────────
# Triggered when a trusted bot approves a PR. Skips engine install and
# pre-flight — just checks reviewDecision and enables auto-merge if APPROVED.
#
# Gate on the step output of `intent` (not env.INTENT_TYPE): a step's own
# env block is in scope when evaluating its `if:`, so the literal
# `INTENT_TYPE: enable-auto-merge` we set below would make env.INTENT_TYPE
# always equal 'enable-auto-merge' at gate-evaluation time, defeating the
# check. Step outputs are not shadowed. Also require a non-empty PR number
# to mirror the guard already present in dev-lead-reusable.yml.
- name: Enable auto-merge on bot approval
if: env.INTENT_TYPE == 'enable-auto-merge'
if: >-
steps.intent.outputs.intent_type == 'enable-auto-merge'
&& env.INTENT_PR_NUMBER != ''
env:
INTENT_TYPE: enable-auto-merge
PR_NUMBER: ${{ env.INTENT_PR_NUMBER }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/issue-triage-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
persist-credentials: false

- name: Install claude CLI
uses: anthropics/claude-code-action/setup@20c8abf165d5f85ab3fc970db9498436377dc9d1 # main
uses: anthropics/claude-code-action/setup@787c5a0ce96a9a6cfb050ea0c8f4c05f2447c251 # main
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

Expand Down
68 changes: 61 additions & 7 deletions .github/workflows/pr-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ jobs:
${{
inputs.pr_url
|| github.event.client_payload.pr_url
|| (github.event_name == 'pull_request_review' && github.event.pull_request.html_url)
|| (github.event_name == 'pull_request' && github.event.pull_request.html_url)
|| github.event.pull_request.html_url
|| ''
}}
# JSON array of PRs associated with a check_suite event. Empty for all
Expand Down Expand Up @@ -186,10 +185,31 @@ jobs:
path: ~/.npm-global
key: claude-code-${{ env.CLAUDE_CODE_VERSION }}-${{ runner.os }}

- name: Verify auth scopes
run: |
set -euo pipefail
if auth_status="$(gh auth status 2>&1)"; then
:
else
auth_rc=$?
echo "$auth_status"
echo "::error::gh auth status failed"
exit "$auth_rc"
fi
echo "$auth_status"
scopes_line="$(printf '%s\n' "$auth_status" | grep 'Token scopes:' || true)"
normalized_scopes="$(printf '%s' "$scopes_line" | sed "s/[',]/ /g")"
required_scopes=(repo read:org)
for required_scope in "${required_scopes[@]}"; do
if ! grep -qE "(^|[[:space:]])${required_scope}([[:space:]]|$)" <<< "$normalized_scopes"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept org-scope supersets

When the bot PAT is granted write:org or admin:org instead of the narrower read:org, this exact-token check fails the workflow even though GitHub treats those org scopes as higher-permission variants and normalizes implicitly included scopes (see GitHub's OAuth scope docs: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps#normalized-scopes). Allow the accepted supersets for read:org so an over-scoped but otherwise functional token does not stop every PR-review run during the new auth verification step.

Useful? React with 👍 / 👎.

echo "::error::GH_TOKEN is missing required scope: ${required_scope}"
exit 1
fi
done

- name: Install review engine CLIs
run: |
set -euo pipefail
gh auth status

# Use a per-user prefix so actions/cache (above) can persist the
# install across runs without needing root. Add the bin dir to PATH
Expand All @@ -199,19 +219,38 @@ jobs:
echo "$HOME/.npm-global/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.npm-global/bin:$PATH"

npm_install_retry() {
# npm_install_retry <package> [attempts=3] [delay_seconds=15]
local package="$1"
local attempts="${2:-3}"
local delay="${3:-15}"
local attempt=1
while [ "$attempt" -le "$attempts" ]; do
if npm install -g "$package"; then
return 0
fi
if [ "$attempt" -lt "$attempts" ]; then
echo "::warning::npm install failed for $package (attempt $attempt/$attempts); retrying in ${delay}s"
sleep "$delay"
fi
attempt=$((attempt + 1))
done
return 1
}

install_claude() {
if command -v claude >/dev/null 2>&1; then
echo "claude-code cache hit: $(claude --version)"
return 0
fi
npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"
npm_install_retry "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"
}

case "$REVIEW_ENGINE" in
claude)
install_claude
# Best-effort install of fallbacks for duck/fallback support
npm install -g @google/gemini-cli || true
npm_install_retry "@google/gemini-cli" || true
if ! command -v gemini >/dev/null 2>&1; then
echo "::warning::gemini CLI unavailable — Gemini rate-limit fallback will be unavailable"
fi
Expand All @@ -220,7 +259,7 @@ jobs:
fi
;;
gemini)
npm install -g @google/gemini-cli
npm_install_retry "@google/gemini-cli"
install_claude || true
;;
copilot)
Expand All @@ -229,7 +268,7 @@ jobs:
gh extension install github/gh-copilot || true
fi
install_claude || true
npm install -g @google/gemini-cli || true
npm_install_retry "@google/gemini-cli" || true
if ! command -v gemini >/dev/null 2>&1; then
echo "::warning::gemini CLI unavailable — Gemini duck review will be skipped"
fi
Expand Down Expand Up @@ -267,6 +306,21 @@ jobs:
- name: Review each PR (cascade)
run: bash scripts/review-batch.sh

- name: Upload debug log on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# Collect both current cascade logs and legacy/supplemental log paths.
with:
name: pr-review-debug-${{ github.run_number }}-${{ github.run_attempt }}
path: |
prs.txt
/tmp/cascade/**
/tmp/review-*.log
${{ runner.temp }}/cascade/**
${{ runner.temp }}/review-*.log
if-no-files-found: ignore
retention-days: 14

- name: Upload token usage log
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/repair-pr-approvals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Repair PR approvals
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Run copilot_chat tests
run: bash tests/test_copilot_chat.sh
Expand Down
4 changes: 4 additions & 0 deletions scripts/dev-lead-fix-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set -euo pipefail
# Optional: MAX_FAIL_ATTEMPTS — consecutive engine failures before exhaustion (default: 2)

source "$(dirname "$0")/engine.sh"
source "$(dirname "$0")/lib/git-identity.sh"

PR_NUMBER="${PR_NUMBER:-}"
HEAD_SHA="${HEAD_SHA:-}"
Expand Down Expand Up @@ -254,6 +255,9 @@ main() {
fi

if $has_uncommitted; then
# GitHub-hosted runners have no default git identity; configure it
# before committing or commit fails with "fatal: empty ident name".
setup_git_identity
git add -A
git commit -m "fix(ci): auto-fix for $(echo "$CHECKS_JSON" | jq -r '.[0].name // "CI failure"') [skip ci-relay]"
fi
Expand Down
13 changes: 1 addition & 12 deletions scripts/dev-lead-fix-issue.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -euo pipefail
# Optional: PROMPTS_DIR (defaults to prompts/dev-lead relative to CWD)

source "$(dirname "$0")/engine.sh"
source "$(dirname "$0")/lib/git-identity.sh"

ISSUE_NUMBER="${ISSUE_NUMBER:-}"
REPO="${REPO:-${GITHUB_REPOSITORY:-}}"
Expand All @@ -17,18 +18,6 @@ check_existing_pr() {
[ "$existing" -gt 0 ]
}

setup_git_identity() {
local bot="${BOT_USER:-donpetry-bot}"
local bot_id
bot_id=$(gh api "users/${bot}" --jq '.id' 2>/dev/null || echo "")
if [ -n "$bot_id" ]; then
git config user.email "${bot_id}+${bot}@users.noreply.github.com"
else
git config user.email "${bot}@users.noreply.github.com"
fi
git config user.name "$bot"
}

main() {
if [ -z "$ISSUE_NUMBER" ]; then
echo "::error::ISSUE_NUMBER is required"
Expand Down
14 changes: 11 additions & 3 deletions scripts/dev-lead-fix-reviews.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -euo pipefail
# Optional: PROMPTS_DIR (defaults to prompts/dev-lead relative to CWD)

source "$(dirname "$0")/engine.sh"
source "$(dirname "$0")/lib/git-identity.sh"

INTENT_TYPE="${INTENT_TYPE:-fix-reviews}"
PR_NUMBER="${PR_NUMBER:-}"
Expand All @@ -29,6 +30,7 @@ fi
# Checkout the PR branch for modification (Requirement 1)
if [ "${DEV_LEAD_DRY_RUN:-false}" = "false" ] && [ -n "${PR_NUMBER:-}" ]; then
gh pr checkout "$PR_NUMBER" --repo "$REPO"
setup_git_identity
fi

build_and_run() {
Expand Down Expand Up @@ -370,7 +372,13 @@ commit_and_push() {
fi
else
if $has_uncommitted; then
# GitHub-hosted runners have no default git identity; configure it
# before committing or commit fails with "fatal: empty ident name".
setup_git_identity
git add -A
# Ensure git identity is set — actions/checkout only sets local config for the
# repo it checks out (.github-private), not for target repos cloned separately.
setup_git_identity
# Explicit exit on failure: set -e is suspended when commit_and_push is called from
# an if-statement condition, so git commit failures would be silently swallowed
# otherwise. Using exit (not return) ensures CI fails visibly instead of posting a
Expand Down Expand Up @@ -534,9 +542,9 @@ case "$INTENT_TYPE" in
post_reviews_terminal "fix-reviews" "applied" "Changes committed and pushed."
else
notify_coderabbit_resolve
resolve_actor_outdated_threads "fix-reviews"
post_no_changes "fix-reviews"
fi
resolve_actor_outdated_threads "fix-reviews"
try_enable_auto_merge
fi
exit "$rc"
Expand All @@ -553,9 +561,9 @@ case "$INTENT_TYPE" in
post_reviews_terminal "fix-bot-comment" "applied" "Changes committed and pushed."
else
notify_coderabbit_resolve
resolve_actor_outdated_threads "fix-bot-comment"
post_no_changes "fix-bot-comment"
fi
resolve_actor_outdated_threads "fix-bot-comment"
try_enable_auto_merge
fi
exit "$rc"
Expand Down Expand Up @@ -605,9 +613,9 @@ case "$INTENT_TYPE" in
post_reviews_terminal "review-changes" "applied" "Changes committed and pushed."
else
notify_coderabbit_resolve
resolve_actor_outdated_threads "review-changes"
post_reviews_terminal "review-changes" "no-changes" "No changes were needed for this PR."
fi
resolve_actor_outdated_threads "review-changes"
try_enable_auto_merge
fi
exit "$rc"
Expand Down
Loading