|
5 | 5 | secrets: |
6 | 6 | CLAUDE_API_KEY: |
7 | 7 | required: true |
| 8 | + # Optional team-context credentials. If absent, the review still runs, |
| 9 | + # just without injected standards (backward compatible during rollout). |
| 10 | + TEAM_CONTEXT_APP_ID: |
| 11 | + required: false |
| 12 | + TEAM_CONTEXT_APP_PRIVATE_KEY: |
| 13 | + required: false |
| 14 | + # Newline list of `repo:path[,path...]` to fetch — kept in a secret so the |
| 15 | + # source repos/paths don't live in this public repo. |
| 16 | + TEAM_CONTEXT_SOURCES: |
| 17 | + required: false |
8 | 18 |
|
9 | 19 | permissions: {} |
10 | 20 |
|
| 21 | +# Run conditions (claude-review inherits via `needs`): same-repo PRs only, skip |
| 22 | +# changeset-release branches, skip the disable-claude-code-review label. |
| 23 | + |
11 | 24 | jobs: |
12 | | - claude-review: |
13 | | - # Only run on PRs from the same repository (not forks) to avoid API costs on external contributions. |
14 | | - # Skip changeset-release branches — those are automated version-bump PRs, not code to review. |
15 | | - # Skip PRs labelled disable-claude-code-review. |
| 25 | + # Job 1 — fetches review context with a short-lived, read-only token and hands |
| 26 | + # it to job 2 as an artifact. No PR content is checked out here, and the token |
| 27 | + # is never passed to the review job. |
| 28 | + fetch-context: |
16 | 29 | if: >- |
17 | 30 | github.event.pull_request.head.repo.full_name == github.repository && |
18 | 31 | !startsWith(github.head_ref, 'changeset-release/') && |
19 | 32 | !contains(github.event.pull_request.labels.*.name, 'disable-claude-code-review') |
20 | 33 | runs-on: ubuntu-latest |
| 34 | + permissions: {} |
| 35 | + steps: |
| 36 | + - id: guard |
| 37 | + name: Detect whether team-context inputs were provided |
| 38 | + shell: bash |
| 39 | + env: |
| 40 | + APP_ID: ${{ secrets.TEAM_CONTEXT_APP_ID }} |
| 41 | + SOURCES: ${{ secrets.TEAM_CONTEXT_SOURCES }} |
| 42 | + run: | |
| 43 | + if [[ -n "$APP_ID" && -n "$SOURCES" ]]; then |
| 44 | + echo "enabled=true" >> "$GITHUB_OUTPUT" |
| 45 | + else |
| 46 | + echo "enabled=false" >> "$GITHUB_OUTPUT" |
| 47 | + echo "::notice::Team context not configured — review will run without injected standards." |
| 48 | + fi |
| 49 | + - name: Prepare context dir |
| 50 | + shell: bash |
| 51 | + # Always produce an artifact (even when the fetch is skipped) so job 2's |
| 52 | + # download never fails. The marker must be non-hidden — upload-artifact |
| 53 | + # excludes dotfiles by default, so a `.placeholder` would yield an empty |
| 54 | + # (uncreated) artifact and break the download. |
| 55 | + run: | |
| 56 | + mkdir -p review-context |
| 57 | + touch review-context/placeholder |
| 58 | + - name: Fetch review context |
| 59 | + if: steps.guard.outputs.enabled == 'true' |
| 60 | + # TODO(before merge): switch @feat/... back to @main. The branch ref lets |
| 61 | + # this PR self-test the action before it exists on main; reusable |
| 62 | + # workflows resolve composite actions by full path, not local ./. |
| 63 | + uses: 0xPolygon/pipelines/.github/actions/fetch-team-context@feat/claude-review-team-context |
| 64 | + with: |
| 65 | + app-id: ${{ secrets.TEAM_CONTEXT_APP_ID }} |
| 66 | + private-key: ${{ secrets.TEAM_CONTEXT_APP_PRIVATE_KEY }} |
| 67 | + sources: ${{ secrets.TEAM_CONTEXT_SOURCES }} |
| 68 | + output-dir: review-context |
| 69 | + - name: Upload context artifact |
| 70 | + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 |
| 71 | + with: |
| 72 | + name: review-context |
| 73 | + path: review-context |
| 74 | + retention-days: 1 |
| 75 | + |
| 76 | + # Job 2 — the review. Separate runner; not given the team-context credentials. |
| 77 | + claude-review: |
| 78 | + needs: fetch-context |
| 79 | + runs-on: ubuntu-latest |
21 | 80 | permissions: |
22 | 81 | contents: read |
23 | 82 | pull-requests: write |
|
29 | 88 | uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 |
30 | 89 | with: |
31 | 90 | fetch-depth: 1 |
| 91 | + - name: Download review context |
| 92 | + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 |
| 93 | + with: |
| 94 | + name: review-context |
| 95 | + path: .review-context |
| 96 | + - name: Inject team standards into CLAUDE.md |
| 97 | + shell: bash |
| 98 | + # Load the standards natively via CLAUDE.md so they're guaranteed in the |
| 99 | + # review's context; any other fetched docs stay on disk for on-demand reads. |
| 100 | + run: | |
| 101 | + std="$(find .review-context -type f -name team-standards.md | head -1)" |
| 102 | + if [[ -z "$std" ]]; then |
| 103 | + echo "::warning::No team standards in context — reviewing without them." |
| 104 | + exit 0 |
| 105 | + fi |
| 106 | + { |
| 107 | + echo "" |
| 108 | + echo "<!-- BEGIN injected team standards (CI review only; not committed) -->" |
| 109 | + echo "# Team standards (injected for this review)" |
| 110 | + echo "" |
| 111 | + echo "These team standards govern this review. Additional reference material" |
| 112 | + echo "(rationale docs and a service dependency graph for cross-repo impact)" |
| 113 | + echo "is available on disk under \`.review-context/\` — consult it as needed." |
| 114 | + echo "" |
| 115 | + cat "$std" |
| 116 | + echo "" |
| 117 | + echo "<!-- END injected team standards -->" |
| 118 | + } >> CLAUDE.md |
| 119 | + echo "Injected $(wc -l < "$std") lines of team standards into CLAUDE.md" |
32 | 120 | - name: Run Claude Code Review |
33 | 121 | id: claude-review |
34 | 122 | uses: anthropics/claude-code-action@df37d2f0760a4b5683a6e617c9325bc1a36443f6 # v1 |
|
0 commit comments