Conversation
📝 WalkthroughWalkthroughGitHub Actions workflows are updated to newer action majors (checkout/setup-node to v6; github-script to v8). The release workflow replaces softprops/action-gh-release with a gh-CLI Bash implementation and adds metadata reads and conditional publish/skip logic. CHANGELOG and ROADMAP text/date bumped. Changes
Sequence Diagram(s)sequenceDiagram
participant WF as GitHub Actions (release.yml)
participant META as Read metadata (npm/JSR)
participant REG as Registry(s) (npm / custom)
participant GH as gh CLI (GitHub Releases)
participant PKG as Publish target (npm/JSR registry)
WF->>META: read package metadata (name, version)
WF->>REG: query if package@version exists
REG-->>WF: exists? (yes/no)
alt exists
WF->>WF: set SKIP_PUBLISH=true
WF->>GH: check if release tag exists
GH-->>WF: exists? (yes/no)
alt release exists
WF->>WF: skip release creation, append warning
else
WF->>GH: create release with RELEASE_SUMMARY.md
end
else not exists
WF->>PKG: publish package
PKG-->>WF: publish result
WF->>GH: create release with RELEASE_SUMMARY.md
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Release Preflight
If you tag this commit as |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
.github/workflows/ci.yml (1)
21-23: Pin GitHub Actions to immutable commit SHAs instead of mutable major version tags.Using
@v6is mutable and reduces supply-chain security and reproducibility. Replace with full commit SHAs:
- Line 21:
actions/checkout@v6- Line 23:
actions/setup-node@v6- Line 52:
actions/checkout@v6- Line 59:
actions/checkout@v6- Line 66:
actions/checkout@v6For each, find the corresponding commit SHA in the action's GitHub releases and use it (e.g.,
actions/checkout@a81bbbf8298c0fa03ea29cdc473d45ade89f9a91).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 21 - 23, Replace mutable action tags with immutable commit SHAs: find every use of actions/checkout@v6 and actions/setup-node@v6 in the CI workflow and pin them to the corresponding full commit SHA from each action's GitHub releases (e.g., replace actions/checkout@v6 with actions/checkout@<commit-sha> and actions/setup-node@v6 with actions/setup-node@<commit-sha>); ensure you update all occurrences (multiple actions/checkout entries and the setup-node entry) so the workflow uses stable, reproducible SHAs rather than version tags..github/workflows/release.yml (1)
232-233: Consider adding temp file cleanup for hygiene.The temporary files created via
mktempare never explicitly cleaned up. While the runner is ephemeral, adding a trap for cleanup is a good defensive practice.♻️ Optional: Add trap for temp file cleanup
if gh release view "$TAG" >/dev/null 2>&1; then EXISTING_BODY=$(mktemp) RELEASE_BODY=$(mktemp) + trap 'rm -f "$EXISTING_BODY" "$RELEASE_BODY"' EXIT gh release view "$TAG" --json body --jq '.body' > "$EXISTING_BODY"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 232 - 233, The workflow creates temporary files via EXISTING_BODY=$(mktemp) and RELEASE_BODY=$(mktemp) but never removes them; add a trap to delete these temp files on exit/failure by registering a cleanup function (e.g., cleanup() that rm -f "$EXISTING_BODY" "$RELEASE_BODY") and calling trap cleanup EXIT so the files are removed when the job finishes or errors out.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 21-23: Replace mutable action tags with immutable commit SHAs:
find every use of actions/checkout@v6 and actions/setup-node@v6 in the CI
workflow and pin them to the corresponding full commit SHA from each action's
GitHub releases (e.g., replace actions/checkout@v6 with
actions/checkout@<commit-sha> and actions/setup-node@v6 with
actions/setup-node@<commit-sha>); ensure you update all occurrences (multiple
actions/checkout entries and the setup-node entry) so the workflow uses stable,
reproducible SHAs rather than version tags.
In @.github/workflows/release.yml:
- Around line 232-233: The workflow creates temporary files via
EXISTING_BODY=$(mktemp) and RELEASE_BODY=$(mktemp) but never removes them; add a
trap to delete these temp files on exit/failure by registering a cleanup
function (e.g., cleanup() that rm -f "$EXISTING_BODY" "$RELEASE_BODY") and
calling trap cleanup EXIT so the files are removed when the job finishes or
errors out.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ab2b802c-c0e3-481a-9297-6d91e58c0de0
📒 Files selected for processing (6)
.github/workflows/ci.yml.github/workflows/links.yml.github/workflows/release-pr.yml.github/workflows/release.ymlCHANGELOG.mdROADMAP.md
Release Preflight
If you tag this commit as |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)
277-301:⚠️ Potential issue | 🔴 CriticalFail fast before creating the GitHub Release.
Lines 277-295 can create an immutable GitHub Release and only then reach Lines 297-301. That leaves a visible release behind even when both registry publishes failed, and reruns will hit the existing-release path instead of repairing the bad state.
🧭 Suggested change
- - name: Create / update release + - name: Fail if both registries failed + if: ${{ needs.publish_npm.result != 'success' && needs.publish_jsr.result != 'success' }} + run: | + echo "Both npm and JSR publish failed." + exit 1 + + - name: Create / update release env: GH_TOKEN: ${{ github.token }} TAG: ${{ needs.verify.outputs.tag }} IS_PRERELEASE: ${{ needs.verify.outputs.is_prerelease }} shell: bash @@ - - name: Fail if both registries failed - if: ${{ needs.publish_npm.result != 'success' && needs.publish_jsr.result != 'success' }} - run: | - echo "Both npm and JSR publish failed." - exit 1🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 277 - 301, The release step ("Create / update release") runs before the failure check ("Fail if both registries failed"), allowing an immutable GitHub Release to be created even when both publishes failed; move the failure gate so the workflow fails fast: either (A) add a condition on the release step to only run when at least one publish succeeded (e.g., if: ${{ needs.publish_npm.result == 'success' || needs.publish_jsr.result == 'success' }}) or (B) move the existing "Fail if both registries failed" block so it executes before the "Create / update release" block; update references to TAG and IS_PRERELEASE unchanged and preserve the gh release create args logic.
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)
151-152: Pin npm to a stable 11.x instead oflatest.Trusted publishing only needs npm CLI 11.5.1+ on Node 22.14.0+, so
npm@latestis broader than this workflow needs. (docs.npmjs.com) Pin a known-good 11.x (or exact version) here so the release path does not drift independently of the Node 22 baseline.♻️ Suggested change
- name: Upgrade npm CLI (>=11.5.1 required for trusted publishing) - run: npm i -g npm@latest + run: npm i -g npm@11🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 151 - 152, Update the workflow step that currently runs "npm i -g npm@latest" (the step named "Upgrade npm CLI (>=11.5.1 required for trusted publishing)") to pin to a stable npm 11.x release instead of latest—e.g. change the command to install a known-good 11.x like "npm i -g npm@^11.5.1" or a fixed "npm@11.5.1" so the CI stays aligned with the Node 22 baseline and trusted-publishers requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 207-229: The step currently hardcodes require('./jsr.json') for
NAME, VERSION and the NPM_NAME node heredoc; change it to prefer jsr.jsonc when
present (fall back to jsr.json) and parse JSONC before JSON.parse so both .json
and .jsonc work. Update the NAME/VERSION assignments and the NPM_NAME node block
to first check fs.existsSync('./jsr.jsonc') and if present
readFileSync('./jsr.jsonc'), strip comments (e.g. remove // and /* */ blocks)
then JSON.parse that content; otherwise require('./jsr.json') as before,
ensuring the same package name logic that writes to NPM_NAME remains unchanged.
- Around line 278-295: Add an explicit repository variable and pass it to the gh
commands: add GH_REPO: ${{ github.repository }} to the env block (alongside
GH_TOKEN, TAG, IS_PRERELEASE), and update both gh invocations — the gh release
view "$TAG" check and the gh release create invocation built in the args array
(which reads RELEASE_SUMMARY.md into SUMMARY and appends --prerelease when
IS_PRERELEASE is true) — to include --repo "$GH_REPO" so the commands run
outside a checked-out repo.
---
Outside diff comments:
In @.github/workflows/release.yml:
- Around line 277-301: The release step ("Create / update release") runs before
the failure check ("Fail if both registries failed"), allowing an immutable
GitHub Release to be created even when both publishes failed; move the failure
gate so the workflow fails fast: either (A) add a condition on the release step
to only run when at least one publish succeeded (e.g., if: ${{
needs.publish_npm.result == 'success' || needs.publish_jsr.result == 'success'
}}) or (B) move the existing "Fail if both registries failed" block so it
executes before the "Create / update release" block; update references to TAG
and IS_PRERELEASE unchanged and preserve the gh release create args logic.
---
Nitpick comments:
In @.github/workflows/release.yml:
- Around line 151-152: Update the workflow step that currently runs "npm i -g
npm@latest" (the step named "Upgrade npm CLI (>=11.5.1 required for trusted
publishing)") to pin to a stable npm 11.x release instead of latest—e.g. change
the command to install a known-good 11.x like "npm i -g npm@^11.5.1" or a fixed
"npm@11.5.1" so the CI stays aligned with the Node 22 baseline and
trusted-publishers requirement.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: abcfd9e4-5d66-41af-8931-3a4d7bb14751
📒 Files selected for processing (3)
.github/workflows/release.ymlCHANGELOG.mdROADMAP.md
🚧 Files skipped from review as they are similar to previous changes (2)
- CHANGELOG.md
- ROADMAP.md
| - name: Read JSR metadata | ||
| id: jsr_pkg | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| NAME=$(node -p "require('./jsr.json').name") | ||
| VERSION=$(node -p "require('./jsr.json').version") | ||
| NPM_NAME=$(node - <<'NODE' | ||
| const name = require('./jsr.json').name; | ||
| if (!name.startsWith('@')) { | ||
| throw new Error(`Unsupported unscoped JSR package name: ${name}`); | ||
| } | ||
| const [scope, pkg] = name.slice(1).split('/'); | ||
| if (!scope || !pkg) { | ||
| throw new Error(`Unsupported JSR package name: ${name}`); | ||
| } | ||
| process.stdout.write(`@jsr/${scope}__${pkg}`); | ||
| NODE | ||
| ) | ||
| echo "name=$NAME" >> "$GITHUB_OUTPUT" | ||
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | ||
| echo "npm_name=$NPM_NAME" >> "$GITHUB_OUTPUT" | ||
|
|
There was a problem hiding this comment.
Handle jsr.jsonc in the publish job too.
The verify job accepts jsr.json or jsr.jsonc, but this step hardcodes require('./jsr.json'). If the repo keeps JSR metadata in jsr.jsonc, verify passes and publish_jsr fails here before the immutable-skip check runs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 207 - 229, The step currently
hardcodes require('./jsr.json') for NAME, VERSION and the NPM_NAME node heredoc;
change it to prefer jsr.jsonc when present (fall back to jsr.json) and parse
JSONC before JSON.parse so both .json and .jsonc work. Update the NAME/VERSION
assignments and the NPM_NAME node block to first check
fs.existsSync('./jsr.jsonc') and if present readFileSync('./jsr.jsonc'), strip
comments (e.g. remove // and /* */ blocks) then JSON.parse that content;
otherwise require('./jsr.json') as before, ensuring the same package name logic
that writes to NPM_NAME remains unchanged.
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| TAG: ${{ needs.verify.outputs.tag }} | ||
| IS_PRERELEASE: ${{ needs.verify.outputs.is_prerelease }} | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| if gh release view "$TAG" >/dev/null 2>&1; then | ||
| echo "::warning::GitHub Release $TAG already exists; skipping immutable release update." | ||
| else | ||
| SUMMARY=$(cat RELEASE_SUMMARY.md) | ||
| args=(release create "$TAG" --generate-notes --notes "$SUMMARY") | ||
| if [[ "$IS_PRERELEASE" == "true" ]]; then | ||
| args+=(--prerelease) | ||
| fi | ||
| gh "${args[@]}" | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the full github_release job and its context
cat -n .github/workflows/release.yml | head -350 | tail -100Repository: git-stunts/git-warp
Length of output: 4204
🏁 Script executed:
# Search for the github_release job definition to see the full context
grep -n "github_release:" .github/workflows/release.yml -A 30Repository: git-stunts/git-warp
Length of output: 1146
🏁 Script executed:
# Check if there are other jobs in this workflow that reference repository checkout
grep -n "uses: actions/checkout" .github/workflows/release.ymlRepository: git-stunts/git-warp
Length of output: 175
🌐 Web query:
GitHub CLI gh release view GH_REPO environment variable required no checkout
💡 Result:
gh release view only needs a repository context if you’re not running inside a checked-out git repo. You can provide that context either via --repo/-R or the GH_REPO environment variable (so GH_REPO is not required if you use --repo). [1], [2]
Examples (no checkout needed):
# Option A: pass repo explicitly
gh release view v1.2.3 --repo OWNER/REPO
gh release view --repo OWNER/REPO # shows latest release if no tag is given[1]
# Option B: set repo via environment variable
export GH_REPO=OWNER/REPO
gh release view v1.2.3
gh release view # latest release[2]
If you’re in CI / a clean environment, you may also need to provide auth via GH_TOKEN or GITHUB_TOKEN (in that precedence order). [2]
🌐 Web query:
gh CLI repository context requirements gh release create without checkout
💡 Result:
gh release create does not require a local checkout as long as gh can determine which repository to target.
How gh finds the repository context
- In a checked-out git repo:
ghinfers the repo from your local git remotes (typical default behavior). - Without a checkout: you must specify the repo explicitly via either:
-R/--repo [HOST/]OWNER/REPO(per-command), orGH_REPO=[HOST/]OWNER/REPO(environment variable). [2]
Creating a release without checking out the repo
gh release create v1.2.3 -R OWNER/REPO --generate-notesor
export GH_REPO=OWNER/REPO
gh release create v1.2.3 --generate-notes-R/--repo is supported by gh release create. [1]
Tags / targets when you don’t have local git data
- If the tag doesn’t exist,
ghwill create it from the latest state of the default branch; use--targetto point at a specific branch or full commit SHA. [1] - If you need an annotated tag, you must create it locally with
git, push it, then rungh release create. [1] - If you want to fail instead of auto-creating a tag, use
--verify-tag. [1]
Pass an explicit repository to the gh release commands.
This job lacks a repository checkout, so gh cannot infer the repository context. Both gh release view and gh release create require explicit repository specification when running outside a git repository. Without it, the commands will fail with unclear errors. Add GH_REPO: ${{ github.repository }} to the environment and pass --repo "$GH_REPO" to both release commands.
🛠️ Suggested change
env:
GH_TOKEN: ${{ github.token }}
+ GH_REPO: ${{ github.repository }}
TAG: ${{ needs.verify.outputs.tag }}
IS_PRERELEASE: ${{ needs.verify.outputs.is_prerelease }}
shell: bash
run: |
set -euo pipefail
- if gh release view "$TAG" >/dev/null 2>&1; then
+ if gh release view "$TAG" --repo "$GH_REPO" >/dev/null 2>&1; then
echo "::warning::GitHub Release $TAG already exists; skipping immutable release update."
else
SUMMARY=$(cat RELEASE_SUMMARY.md)
- args=(release create "$TAG" --generate-notes --notes "$SUMMARY")
+ args=(release create "$TAG" --repo "$GH_REPO" --generate-notes --notes "$SUMMARY")
if [[ "$IS_PRERELEASE" == "true" ]]; then
args+=(--prerelease)
fi
gh "${args[@]}"
fi📝 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.
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG: ${{ needs.verify.outputs.tag }} | |
| IS_PRERELEASE: ${{ needs.verify.outputs.is_prerelease }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| echo "::warning::GitHub Release $TAG already exists; skipping immutable release update." | |
| else | |
| SUMMARY=$(cat RELEASE_SUMMARY.md) | |
| args=(release create "$TAG" --generate-notes --notes "$SUMMARY") | |
| if [[ "$IS_PRERELEASE" == "true" ]]; then | |
| args+=(--prerelease) | |
| fi | |
| gh "${args[@]}" | |
| fi | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG: ${{ needs.verify.outputs.tag }} | |
| IS_PRERELEASE: ${{ needs.verify.outputs.is_prerelease }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if gh release view "$TAG" --repo "$GH_REPO" >/dev/null 2>&1; then | |
| echo "::warning::GitHub Release $TAG already exists; skipping immutable release update." | |
| else | |
| SUMMARY=$(cat RELEASE_SUMMARY.md) | |
| args=(release create "$TAG" --repo "$GH_REPO" --generate-notes --notes "$SUMMARY") | |
| if [[ "$IS_PRERELEASE" == "true" ]]; then | |
| args+=(--prerelease) | |
| fi | |
| gh "${args[@]}" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 278 - 295, Add an explicit
repository variable and pass it to the gh commands: add GH_REPO: ${{
github.repository }} to the env block (alongside GH_TOKEN, TAG, IS_PRERELEASE),
and update both gh invocations — the gh release view "$TAG" check and the gh
release create invocation built in the args array (which reads
RELEASE_SUMMARY.md into SUMMARY and appends --prerelease when IS_PRERELEASE is
true) — to include --repo "$GH_REPO" so the commands run outside a checked-out
repo.
Summary
softprops/action-gh-releasestep with an idempotentgh releaseshell stepCHANGELOG.mdandROADMAP.mdto reflect the workflow runtime refresh and thev14.1.0baselineValidation
ruby -e 'require "yaml"; %w[.github/workflows/ci.yml .github/workflows/links.yml .github/workflows/release-pr.yml .github/workflows/release.yml].each { |f| YAML.load_file(f); puts "OK #{f}" }'npx markdownlint CHANGELOG.md ROADMAP.mdsh scripts/hooks/pre-pushSummary by CodeRabbit