Add upstream release watcher (workload-identity auth)#4
Conversation
Swap static ANTHROPIC_API_KEY for OIDC/WIF: add id-token: write and the anthropic_federation_* inputs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WalkthroughChangesLightGBM upstream release automation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant GitHubReleasesAPI
participant ClaudeCodeAction
participant CreatePullRequest
GitHubActions->>GitHubReleasesAPI: Fetch latest LightGBM release
GitHubActions->>GitHubActions: Compare versions and prepare header diff
GitHubActions->>ClaudeCodeAction: Review diff and edit repository files
ClaudeCodeAction->>GitHubActions: Return updated files and PR body content
GitHubActions->>CreatePullRequest: Create labeled update PR
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
.github/workflows/upstream-release.yml (1)
32-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winBrittle version extraction from
build.rs.
grep 'unwrap_or_else' build.rs | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1assumes there's exactly oneunwrap_or_elsecall with a semver literal in the whole file. Per build.rs get_lightgbm_version() defaults to "4.6.0" today, but any futureunwrap_or_elsecall elsewhere inbuild.rscontaining a version-like literal would silently produce the wrong "current" version and desync the whole workflow's diffing/PR logic.Consider anchoring the grep more specifically, e.g.
grep -oE 'LIGHTGBM_VERSION.*[0-9]+\.[0-9]+\.[0-9]+'orgrep -A1 'fn get_lightgbm_version'.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/upstream-release.yml around lines 32 - 46, Update the versions step’s CURRENT extraction to target the pinned version within get_lightgbm_version, using the LIGHTGBM_VERSION assignment or function-scoped context instead of matching any unwrap_or_else in build.rs. Preserve the existing validation and workflow outputs while ensuring future unrelated version literals cannot be selected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/upstream-release.yml:
- Around line 56-68: Validate the upstream tag in the workflow before assigning
or using LATEST, requiring the expected semver-shaped value after removing the
optional v prefix and failing otherwise. In the “Skip if an update PR is already
open” step, pass the validated value through env and reference it as a shell
variable when constructing BRANCH; apply the same indirect env-based handling to
the Claude prompt interpolation of LATEST, preserving the existing
release-update flow.
- Around line 18-21: Add issues: write to the workflow-level permissions block
alongside id-token, contents, and pull-requests so the label-ensure step can
create repository labels using GITHUB_TOKEN.
---
Nitpick comments:
In @.github/workflows/upstream-release.yml:
- Around line 32-46: Update the versions step’s CURRENT extraction to target the
pinned version within get_lightgbm_version, using the LIGHTGBM_VERSION
assignment or function-scoped context instead of matching any unwrap_or_else in
build.rs. Preserve the existing validation and workflow outputs while ensuring
future unrelated version literals cannot be selected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b2500c64-8c76-481c-a6cb-9cd7e5d3ec72
📒 Files selected for processing (1)
.github/workflows/upstream-release.yml
| permissions: | ||
| id-token: write # fetch the GitHub OIDC token for Anthropic WIF | ||
| contents: write # minimum GitHub allows for creating a PR branch | ||
| pull-requests: write # open/update the PR |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does the GitHub REST "Create a label" endpoint require issues:write permission on GITHUB_TOKEN, or is pull-requests:write sufficient?
💡 Result:
For the GitHub REST API "Create a label" endpoint, possessing issues:write permission is generally recommended and recognized as the effective requirement for success [1][2]. While technical documentation for fine-grained personal access tokens often lists both issues:write and pull-requests:write as potential permission sets for label management [3][2], real-world usage in GitHub Actions with the GITHUB_TOKEN frequently shows that pull-requests:write alone is insufficient for creating new labels [1][2]. Users consistently report encountering authorization errors when attempting to create labels with only pull-requests:write and resolve them by explicitly adding issues:write to their workflow permissions [1][2]. Therefore, if your workflow needs to create labels, you should include issues:write in your permissions configuration: permissions: issues: write pull-requests: write # Optional, depending on your other needs This configuration ensures the GITHUB_TOKEN has the necessary access to manage repository labels effectively [1][2].
Citations:
- 1: HttpError: You do not have permission to create labels on this repository actions/labeler#870
- 2: Fails to create label when action runs googleapis/release-please-action#1105
- 3: https://help.github.com/en/enterprise-server@3.10/rest/issues/labels
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files .github/workflows/upstream-release.yml
echo '---'
nl -ba .github/workflows/upstream-release.yml | sed -n '130,190p'
echo '--- search ---'
rg -n "gh label create|create-pull-request|permissions:" .github/workflows/upstream-release.ymlRepository: aryehlev/lightgbm-rust
Length of output: 243
🏁 Script executed:
#!/bin/bash
set -euo pipefail
nl -ba .github/workflows/upstream-release.yml | sed -n '145,180p'
echo '---'
rg -n "GH_TOKEN|GITHUB_TOKEN|gh label create|issues: write|pull-requests: write|contents: write|id-token: write" .github/workflows/upstream-release.ymlRepository: aryehlev/lightgbm-rust
Length of output: 200
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- permissions and label steps ---'
sed -n '1,230p' .github/workflows/upstream-release.yml | cat -n | sed -n '1,230p'
echo '--- focused search ---'
rg -n "gh label create|create-pull-request|GH_TOKEN|GITHUB_TOKEN|issues: write|pull-requests: write|contents: write|id-token: write" .github/workflows/upstream-release.ymlRepository: aryehlev/lightgbm-rust
Length of output: 10905
Add issues: write to the workflow permissions.
gh label create runs against GITHUB_TOKEN, and pull-requests: write doesn’t cover label creation. Without issues: write, the label-ensure step can silently no-op under || true, so dependencies and upstream-release may never be created on a fresh repo.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/upstream-release.yml around lines 18 - 21, Add issues:
write to the workflow-level permissions block alongside id-token, contents, and
pull-requests so the label-ensure step can create repository labels using
GITHUB_TOKEN.
| - name: Skip if an update PR is already open | ||
| id: existing | ||
| if: steps.versions.outputs.update == 'true' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| BRANCH="chore/lightgbm-${{ steps.versions.outputs.latest }}" | ||
| if gh pr list --state open --head "$BRANCH" --json number --jq '.[0].number' | grep -q '[0-9]'; then | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
| fi |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Template-injection risk: unsanitized upstream tag spliced directly into a shell script.
LATEST (Line 40) is taken verbatim from tag_name of microsoft/LightGBM's latest release with only a v prefix stripped — unlike CURRENT, it is never constrained to a semver shape. It is then substituted directly via ${{ }} into this run: script rather than via an env:-indirected $VAR reference (as is correctly done elsewhere in this same file, e.g. Lines 74-75). This is exactly the classic GitHub Actions template-injection pattern zizmor flags at Line 63: if the upstream tag ever contained shell metacharacters (`, $(), quotes), it would execute arbitrary commands in a job holding contents: write, pull-requests: write, and an OIDC token for Anthropic — not merely break string comparison. The same unvalidated LATEST is also interpolated straight into the Claude prompt text (Lines 114-151), giving it a secondary prompt-injection surface against an agent with Bash access.
Harden at the source and at this use site:
🔒 Proposed fix
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
+ [[ "$LATEST" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "::error::Unexpected release tag format: $LATEST"; exit 1; } - name: Skip if an update PR is already open
id: existing
if: steps.versions.outputs.update == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ LATEST: ${{ steps.versions.outputs.latest }}
run: |
set -euo pipefail
- BRANCH="chore/lightgbm-${{ steps.versions.outputs.latest }}"
+ BRANCH="chore/lightgbm-${LATEST}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Skip if an update PR is already open | |
| id: existing | |
| if: steps.versions.outputs.update == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| BRANCH="chore/lightgbm-${{ steps.versions.outputs.latest }}" | |
| if gh pr list --state open --head "$BRANCH" --json number --jq '.[0].number' | grep -q '[0-9]'; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Skip if an update PR is already open | |
| id: existing | |
| if: steps.versions.outputs.update == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LATEST: ${{ steps.versions.outputs.latest }} | |
| run: | | |
| set -euo pipefail | |
| BRANCH="chore/lightgbm-${LATEST}" | |
| if gh pr list --state open --head "$BRANCH" --json number --jq '.[0].number' | grep -q '[0-9]'; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi |
🧰 Tools
🪛 zizmor (1.26.1)
[info] 63-63: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/upstream-release.yml around lines 56 - 68, Validate the
upstream tag in the workflow before assigning or using LATEST, requiring the
expected semver-shaped value after removing the optional v prefix and failing
otherwise. In the “Skip if an update PR is already open” step, pass the
validated value through env and reference it as a shell variable when
constructing BRANCH; apply the same indirect env-based handling to the Claude
prompt interpolation of LATEST, preserving the existing release-update flow.
Source: Linters/SAST tools
Adds a weekly (and manual) watcher that opens a PR when the upstream library
publishes a newer release than the one pinned in
build.rs. Claude reviews theupstream C API header diff and edits the crate;
peter-evans/create-pull-requestopens the PR. The only remote write is the PR itself.
Auth: Anthropic workload identity federation (OIDC) — no stored API key.
Before this can run, add repo secrets:
ANTHROPIC_FEDERATION_RULE_ID,ANTHROPIC_ORGANIZATION_ID,ANTHROPIC_SERVICE_ACCOUNT_ID(+
ANTHROPIC_WORKSPACE_IDif the federation rule spans multiple workspaces).Note: the federation rule subject should match this repo\x27s default branch once merged.
🤖 Generated with Claude Code
Summary by CodeRabbit