Unverified 提交 51fc07e9 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Use GitHub App tokens in Claude workflows (#2936)

## Summary - switch the listed Claude automation workflows from PAT/default token auth to GitHub App installation tokens - use fork-scoped app tokens for PR responder and rebase push paths, plus base-repo app tokens for labels/comments/API calls - rename the Actions variable and secret references to DYAD_GITHUB_APP_ID and DYAD_GITHUB_APP_PRIVATE_KEY ## Test plan - npm run fmt - npm run lint:fix - npm run ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2936" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end -->
上级 a6a8cda9
......@@ -22,17 +22,26 @@ jobs:
permissions:
issues: write
steps:
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-actions: read
permission-issues: write
- name: Checkout repository
uses: actions/checkout@v5
- name: Check workflow health
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.DEFLAKE_E2E_PAT_GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
CLAUDE_CODE_MAX_OUTPUT_TOKENS: 48000
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.DEFLAKE_E2E_PAT_GITHUB_TOKEN }}
github_token: ${{ steps.app-token.outputs.token }}
claude_args: --model claude-sonnet-4-5-20250929
prompt: |
/dyad:check-workflows ${{ inputs.hours || '24' }}
......
......@@ -21,6 +21,17 @@ jobs:
- ARM64
permissions: {}
steps:
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-actions: write
permission-contents: write
permission-issues: write
permission-pull-requests: write
- name: Checkout repository
uses: actions/checkout@v5
with:
......@@ -38,7 +49,7 @@ jobs:
- name: Install node modules
run: npm ci --no-audit --no-fund --progress=false
env:
GITHUB_TOKEN: ${{ secrets.DEFLAKE_E2E_PAT_GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
......@@ -63,11 +74,11 @@ jobs:
- name: Deflake E2E tests
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.DEFLAKE_E2E_PAT_GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
CLAUDE_CODE_MAX_OUTPUT_TOKENS: 48000
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.DEFLAKE_E2E_PAT_GITHUB_TOKEN }}
github_token: ${{ steps.app-token.outputs.token }}
claude_args: --model claude-opus-4-6
prompt: |
/dyad:deflake-e2e-recent-commits ${{ inputs.commit_count || '10' }}
......
......@@ -30,6 +30,14 @@ jobs:
contents: read
pull-requests: write
steps:
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-pull-requests: write
- name: Checkout repository
uses: actions/checkout@v5
with:
......@@ -48,7 +56,7 @@ jobs:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# See: https://github.com/anthropics/claude-code-action/blob/v1/docs/security.md
github_token: ${{ secrets.GITHUB_TOKEN }} # bypass OIDC
github_token: ${{ steps.app-token.outputs.token }}
allowed_non_write_users: "princeaden1,wwwillchen-bot,dyadbot" # remember, we already filter above.
# Disable progress tracking (try to save tokens)
......
......@@ -17,10 +17,20 @@ jobs:
contents: write
pull-requests: write
steps:
- name: Create GitHub App token for base repo
id: base-app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- name: Check PR author
id: check-author
uses: actions/github-script@v7
with:
github-token: ${{ steps.base-app-token.outputs.token }}
script: |
const pr = context.payload.pull_request;
const allowedUsers = ['wwwillchen', 'wwwillchen-bot', 'dyadbot', 'azizmejri1', 'princeaden1'];
......@@ -32,6 +42,17 @@ jobs:
core.setOutput('should_continue', 'true');
core.setOutput('pr_number', pr.number);
- name: Create GitHub App token for PR head repo
if: steps.check-author.outputs.should_continue == 'true'
id: head-app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
owner: ${{ github.event.pull_request.head.repo.owner.login }}
repositories: ${{ github.event.pull_request.head.repo.name }}
permission-contents: write
- name: Checkout repository
if: steps.check-author.outputs.should_continue == 'true'
uses: actions/checkout@v4
......@@ -52,7 +73,7 @@ jobs:
echo " upstream -> ${{ github.repository }} (base repo)"
git remote -v
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Configure push remote for fork PRs
if: steps.check-author.outputs.should_continue == 'true'
......@@ -62,28 +83,28 @@ jobs:
# Setting pushurl separately ensures git push still targets the fork,
# because git uses pushurl over url when both are configured.
#
# We use PR_CONTENTS_RW_GITHUB_TOKEN (a PAT) instead of GITHUB_TOKEN so that
# We use a GitHub App token instead of GITHUB_TOKEN so that
# the push creates real PR events (synchronize) that trigger downstream workflows
# like CI. Pushes made with GITHUB_TOKEN are silently ignored by GitHub to prevent
# infinite loops.
git remote set-url --push origin "https://x-access-token:${PR_CONTENTS_RW_GITHUB_TOKEN}@github.com/${{ github.event.pull_request.head.repo.full_name }}.git"
git remote set-url --push origin "https://x-access-token:${PR_HEAD_APP_TOKEN}@github.com/${{ github.event.pull_request.head.repo.full_name }}.git"
echo "Configured pushurl to ${{ github.event.pull_request.head.repo.full_name }}"
env:
PR_CONTENTS_RW_GITHUB_TOKEN: ${{ secrets.PR_CONTENTS_RW_GITHUB_TOKEN }}
PR_HEAD_APP_TOKEN: ${{ steps.head-app-token.outputs.token }}
- name: Remove cc:rebase label and add cc:rebasing
if: steps.check-author.outputs.should_continue == 'true'
run: |
gh pr edit ${{ steps.check-author.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:rebase" --add-label "cc:rebasing"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Run PR Rebase
if: steps.check-author.outputs.should_continue == 'true'
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
github_token: ${{ steps.base-app-token.outputs.token }}
prompt: |
You are orchestrating the /dyad:pr-rebase skill. Your job is to run it end-to-end as a sub-agent with NO shortcuts.
......@@ -101,11 +122,11 @@ jobs:
run: |
gh pr edit ${{ steps.check-author.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:rebasing"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Update labels on failure
if: steps.check-author.outputs.should_continue == 'true' && failure()
run: |
gh pr edit ${{ steps.check-author.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:rebasing" --add-label "cc:rebase-failed"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
......@@ -22,6 +22,14 @@ jobs:
contents: read
issues: write
steps:
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-issues: write
- name: Checkout repository
uses: actions/checkout@v4
with:
......@@ -33,7 +41,7 @@ jobs:
CLAUDE_CODE_MAX_OUTPUT_TOKENS: 48000
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
github_token: ${{ steps.app-token.outputs.token }}
direct: true
allowed_tools: "Read,Glob,Grep,Bash(git log:*),Bash(gh issue create:*),Bash(gh issue list:*),Bash(gh issue close:*),Bash(gh label:*)"
claude_args: --model claude-opus-4-6
......
......@@ -10,10 +10,18 @@ jobs:
issues: write
contents: read
steps:
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-issues: write
- uses: actions/checkout@v4
- uses: anthropics/claude-code-base-action@beta
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
with:
# anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
......
......@@ -13,10 +13,18 @@ jobs:
issues: write
contents: read
steps:
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-issues: write
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
......@@ -24,7 +32,7 @@ jobs:
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# See: https://github.com/anthropics/claude-code-action/blob/v1/docs/security.md
github_token: ${{ secrets.GITHUB_TOKEN }} # bypass OIDC
github_token: ${{ steps.app-token.outputs.token }}
allowed_non_write_users: "*"
claude_args: |
--model sonnet --allowedTools "Bash(gh issue reopen:*), Bash(gh issue comment:*)"
......
......@@ -31,10 +31,21 @@ jobs:
contents: write
pull-requests: write
steps:
- name: Create GitHub App token for base repo
id: base-app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
permission-actions: write
permission-contents: write
permission-pull-requests: write
- name: Get PR info and check labels
id: pr-info
uses: actions/github-script@v7
with:
github-token: ${{ steps.base-app-token.outputs.token }}
script: |
const eventName = context.eventName;
let prNumber, headRepo, headBranch, prLabels, prAuthor;
......@@ -200,6 +211,17 @@ jobs:
? context.payload.workflow_run.conclusion
: '');
- name: Create GitHub App token for PR head repo
if: steps.pr-info.outputs.should_continue == 'true'
id: head-app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.DYAD_GITHUB_APP_ID }}
private-key: ${{ secrets.DYAD_GITHUB_APP_PRIVATE_KEY }}
owner: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.owner.login || github.event.workflow_run.head_repository.owner.login }}
repositories: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.name || github.event.workflow_run.head_repository.name }}
permission-contents: write
- name: Checkout repository
if: steps.pr-info.outputs.should_continue == 'true'
uses: actions/checkout@v5
......@@ -225,14 +247,14 @@ jobs:
# Setting pushurl separately ensures git push still targets the fork,
# because git uses pushurl over url when both are configured.
#
# We use PR_CONTENTS_RW_GITHUB_TOKEN (a PAT) instead of GITHUB_TOKEN so that
# We use a GitHub App token instead of GITHUB_TOKEN so that
# the push creates real PR events (synchronize) that trigger downstream workflows
# like CI. Pushes made with GITHUB_TOKEN are silently ignored by GitHub to prevent
# infinite loops.
git remote set-url --push origin "https://x-access-token:${PR_CONTENTS_RW_GITHUB_TOKEN}@github.com/${{ steps.pr-info.outputs.head_repo }}.git"
git remote set-url --push origin "https://x-access-token:${PR_HEAD_APP_TOKEN}@github.com/${{ steps.pr-info.outputs.head_repo }}.git"
echo "Configured pushurl to ${{ steps.pr-info.outputs.head_repo }}"
env:
PR_CONTENTS_RW_GITHUB_TOKEN: ${{ secrets.PR_CONTENTS_RW_GITHUB_TOKEN }}
PR_HEAD_APP_TOKEN: ${{ steps.head-app-token.outputs.token }}
- name: Ensure Homebrew paths are available
if: steps.pr-info.outputs.should_continue == 'true'
......@@ -247,14 +269,14 @@ jobs:
run: |
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "${{ steps.pr-info.outputs.current_label }}" --add-label "cc:pending"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Run PR Fix
if: steps.pr-info.outputs.should_continue == 'true'
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
github_token: ${{ steps.base-app-token.outputs.token }}
claude_args: --model claude-opus-4-6
prompt: |
Run the following skill end-to-end. Execute every step sequentially. Do not skip, summarize, or stop early.
......@@ -267,7 +289,7 @@ jobs:
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
github_token: ${{ steps.base-app-token.outputs.token }}
claude_args: --model claude-opus-4-6
prompt: |
The previous step may have made local commits but
......@@ -279,12 +301,12 @@ jobs:
- name: Check if commits were pushed
# Use always() to ensure we detect commits even if Claude Code fails partway through.
# The push itself (made with PR_CONTENTS_RW_GITHUB_TOKEN) triggers downstream workflows
# The push itself (made with the GitHub App token) triggers downstream workflows
# like CI, BugBot, and Claude PR Review naturally via pull_request synchronize events.
if: steps.pr-info.outputs.should_continue == 'true' && always()
id: retrigger
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
run: |
# Use the GitHub API to get the current head SHA, avoiding race conditions with git fetch
PR_HEAD_SHA=$(gh pr view ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --json headRefOid --jq '.headRefOid')
......@@ -317,21 +339,21 @@ jobs:
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:pending" --add-label "cc:request:${NEXT_COUNT}"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Update labels to done (no new commits)
if: steps.pr-info.outputs.should_continue == 'true' && success() && steps.retrigger.outputs.commits_pushed == 'false'
run: |
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:pending" --add-label "cc:done"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Update labels to failed
if: steps.pr-info.outputs.should_continue == 'true' && failure()
run: |
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:pending" --add-label "cc:failed"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.base-app-token.outputs.token }}
- name: Checkout base repo for labeler script
# The earlier checkout checks out the PR head repo, which for
......@@ -360,6 +382,7 @@ jobs:
steps.retrigger.outputs.commits_pushed != 'true'
uses: actions/github-script@v7
with:
github-token: ${{ steps.base-app-token.outputs.token }}
script: |
const { run } = require('./__base/scripts/pr-status-labeler.js');
await run({
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论