Fix publish workflow to gate Docker on JSR availability - #1214
Fix publish workflow to gate Docker on JSR availability#1214aka-sacci-ccr wants to merge 1 commit into
Conversation
Checkout the release tag, verify the package resolves on JSR before building images, and split JSR publish from Docker so a failed image build does not block registry publication. Co-authored-by: Cursor <cursoragent@cursor.com>
Tagging OptionsShould a new tag be published when this PR is merged?
|
📝 WalkthroughWalkthroughThis PR modifies the GitHub Actions publish workflow to compute a single RELEASE_TAG from inputs or ref name, splits the pipeline into a jsr-publish job and a docker-build job that waits on it, adds JSR propagation verification via retried deno cache checks, and updates Docker image tagging to use RELEASE_TAG. ChangesPublish Workflow Restructuring
Sequence Diagram(s)sequenceDiagram
participant Trigger
participant jsr-publish
participant JSR Registry
participant docker-build
Trigger->>jsr-publish: compute RELEASE_TAG, checkout ref
jsr-publish->>JSR Registry: publish package
jsr-publish->>JSR Registry: deno cache check (retry up to 30x)
JSR Registry-->>jsr-publish: artifact resolvable
jsr-publish->>docker-build: job dependency satisfied
docker-build->>docker-build: checkout RELEASE_TAG, build & tag images
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
.github/workflows/publish.yaml (1)
25-27: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winCheckout steps don't disable credential persistence.
Both
actions/checkout@v4steps persist theGITHUB_TOKENcredential in the local git config by default; zizmor flags this (artipacked) at both locations. Worth hardening, especially for thedocker-buildjob which holds elevated permissions (packages: write,attestations: write,id-token: write).🛡️ Proposed fix
- uses: actions/checkout@v4 with: ref: ${{ env.RELEASE_TAG }} + persist-credentials: false(apply to both checkout steps)
Also applies to: 73-75
🤖 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/publish.yaml around lines 25 - 27, Both `actions/checkout@v4` steps are persisting the `GITHUB_TOKEN` in git config by default, so harden the workflow by adding `persist-credentials: false` to each checkout invocation. Update both checkout blocks in the publish workflow, including the one that uses `ref: ${{ env.RELEASE_TAG }}`, so credential persistence is disabled consistently across the job.Source: Linters/SAST tools
🤖 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/publish.yaml:
- Around line 42-61: The shell step in the JSR resolvability check is
interpolating RELEASE_TAG directly into the script via `${{ }}`, which can lead
to command injection. Update the workflow so the `Verify JSR package is
resolvable` step receives `RELEASE_TAG` through an `env:` mapping and then reads
it as a normal shell variable inside the bash script; keep the existing `deno
cache` loop and `VERSION` usage, but remove direct template expansion from the
run block.
- Around line 18-31: The jsr-publish workflow job is missing the OIDC permission
needed for trusted publishing. Update the permissions block in jsr-publish to
include id-token: write alongside contents: read so the npx jsr publish step can
authenticate; use the jsr-publish job name and its permissions section to locate
the change.
---
Nitpick comments:
In @.github/workflows/publish.yaml:
- Around line 25-27: Both `actions/checkout@v4` steps are persisting the
`GITHUB_TOKEN` in git config by default, so harden the workflow by adding
`persist-credentials: false` to each checkout invocation. Update both checkout
blocks in the publish workflow, including the one that uses `ref: ${{
env.RELEASE_TAG }}`, so credential persistence is disabled consistently across
the job.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: f5751713-1d2a-422e-a4c4-fa8616ef80f9
📒 Files selected for processing (1)
.github/workflows/publish.yaml
| jsr-publish: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| attestations: write | ||
| id-token: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ env.RELEASE_TAG }} | ||
|
|
||
| - uses: denoland/setup-deno@v2 | ||
| with: | ||
| deno-version: v2.4.0 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '1,220p' .github/workflows/publish.yamlRepository: deco-cx/deco
Length of output: 6050
🌐 Web query:
JSR GitHub Actions publish id-token write trusted publishing official docs
💡 Result:
To publish packages to JSR using trusted publishing from GitHub Actions, you must use OpenID Connect (OIDC) authentication [1][2]. This process eliminates the need for manual API tokens by allowing GitHub Actions to provide a short-lived OIDC token that JSR uses to verify your repository's identity [1][2][3]. To set up trusted publishing, follow these steps: 1. Link your package: Go to the settings tab of your package on jsr.io, enter your GitHub repository name, and click Link [1][4]. This is a required one-time setup [2]. 2. Configure your workflow: In your GitHub Actions workflow file (e.g.,.github/workflows/publish.yml), you must grant the job the necessary permissions to request an OIDC token [1][3]. Include the following in your job definition: permissions: contents: read id-token: write # Required for OIDC authentication with JSR 3. Publish: Use the jsr publish or deno publish command in your workflow steps [1][5][4]. By publishing through this native GitHub Actions integration, JSR automatically generates SLSA provenance attestations stored in the Sigstore Rekor transparency log, which helps verify the build source and provenance of your package [5][6]. You can opt out of this by using the --no-provenance flag [5][6]. Token-based authentication remains available for other CI providers, but does not support automatic provenance generation [1][4].
Citations:
- 1: https://jsr.io/docs/publishing-packages
- 2: https://github.com/teamitfi/js-to-make/blob/HEAD/SETUP.md
- 3: https://denoland.medium.com/introducing-jsr-the-javascript-registry-8f7d7331ccc5
- 4: https://github.com/jsr-io/jsr/blob/main/frontend/docs/publishing-packages.md
- 5: https://github.com/jsr-io/jsr/blob/main/frontend/docs/trust.md
- 6: https://jsr.io/docs/trust
Add id-token: write to jsr-publish
npx jsr publish uses JSR trusted publishing, so this job needs permissions: id-token: write alongside contents: read; otherwise the publish steps can’t authenticate.
🔧 Proposed fix
permissions:
contents: read
+ id-token: write📝 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.
| jsr-publish: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.RELEASE_TAG }} | |
| - uses: denoland/setup-deno@v2 | |
| with: | |
| deno-version: v2.4.0 | |
| jsr-publish: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.RELEASE_TAG }} | |
| - uses: denoland/setup-deno@v2 | |
| with: | |
| deno-version: v2.4.0 |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 25-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 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/publish.yaml around lines 18 - 31, The jsr-publish
workflow job is missing the OIDC permission needed for trusted publishing.
Update the permissions block in jsr-publish to include id-token: write alongside
contents: read so the npx jsr publish step can authenticate; use the jsr-publish
job name and its permissions section to locate the change.
Source: Linters/SAST tools
| - name: Verify JSR package is resolvable | ||
| run: | | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| VERSION="${{ env.RELEASE_TAG }}" | ||
|
|
||
| for attempt in $(seq 1 30); do | ||
| if deno cache "jsr:@deco/deco@${VERSION}/scripts/run"; then | ||
| echo "JSR version ${VERSION} is resolvable (attempt ${attempt})" | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "Waiting for JSR propagation (attempt ${attempt}/30)..." | ||
| sleep 10 | ||
| done | ||
|
|
||
| echo "JSR version ${VERSION} not resolvable after 5 minutes" | ||
| exit 1 | ||
|
|
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Script injection via direct ${{ }} interpolation into shell.
VERSION="${{ env.RELEASE_TAG }}" is template-expanded by GitHub Actions into the script text before bash runs it, rather than passed as a shell variable. If RELEASE_TAG (derived from a workflow_dispatch input or ref_name) ever contains shell metacharacters, this allows arbitrary command injection into the runner. zizmor flags this pattern explicitly.
🔒 Proposed fix using `env:` mapping
- name: Verify JSR package is resolvable
+ env:
+ VERSION: ${{ env.RELEASE_TAG }}
run: |
#!/bin/bash
set -euo pipefail
-
- VERSION="${{ env.RELEASE_TAG }}"
for attempt in $(seq 1 30); do
if deno cache "jsr:`@deco/deco`@${VERSION}/scripts/run"; then📝 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: Verify JSR package is resolvable | |
| run: | | |
| #!/bin/bash | |
| set -euo pipefail | |
| VERSION="${{ env.RELEASE_TAG }}" | |
| for attempt in $(seq 1 30); do | |
| if deno cache "jsr:@deco/deco@${VERSION}/scripts/run"; then | |
| echo "JSR version ${VERSION} is resolvable (attempt ${attempt})" | |
| exit 0 | |
| fi | |
| echo "Waiting for JSR propagation (attempt ${attempt}/30)..." | |
| sleep 10 | |
| done | |
| echo "JSR version ${VERSION} not resolvable after 5 minutes" | |
| exit 1 | |
| - name: Verify JSR package is resolvable | |
| env: | |
| VERSION: ${{ env.RELEASE_TAG }} | |
| run: | | |
| #!/bin/bash | |
| set -euo pipefail | |
| for attempt in $(seq 1 30); do | |
| if deno cache "jsr:`@deco/deco`@${VERSION}/scripts/run"; then | |
| echo "JSR version ${VERSION} is resolvable (attempt ${attempt})" | |
| exit 0 | |
| fi | |
| echo "Waiting for JSR propagation (attempt ${attempt}/30)..." | |
| sleep 10 | |
| done | |
| echo "JSR version ${VERSION} not resolvable after 5 minutes" | |
| exit 1 |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 47-47: 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/publish.yaml around lines 42 - 61, The shell step in the
JSR resolvability check is interpolating RELEASE_TAG directly into the script
via `${{ }}`, which can lead to command injection. Update the workflow so the
`Verify JSR package is resolvable` step receives `RELEASE_TAG` through an `env:`
mapping and then reads it as a normal shell variable inside the bash script;
keep the existing `deno cache` loop and `VERSION` usage, but remove direct
template expansion from the run block.
Source: Linters/SAST tools
There was a problem hiding this comment.
2 issues found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/publish.yaml">
<violation number="1" location=".github/workflows/publish.yaml:18">
P1: Restore `id-token: write` on `jsr-publish`; otherwise `npx jsr publish` loses the GitHub OIDC token JSR requires and publication can fail before Docker runs.</violation>
<violation number="2" location=".github/workflows/publish.yaml:47">
P2: Potential script injection via direct `${{ }}` interpolation into the shell script. While `env.RELEASE_TAG` is somewhat trusted (set from `workflow_dispatch` input or `ref_name`), the safer pattern is to pass it through the step's `env:` mapping to avoid shell metacharacter issues.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| jobs: | ||
| publish: | ||
| jsr-publish: |
There was a problem hiding this comment.
P1: Restore id-token: write on jsr-publish; otherwise npx jsr publish loses the GitHub OIDC token JSR requires and publication can fail before Docker runs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish.yaml, line 18:
<comment>Restore `id-token: write` on `jsr-publish`; otherwise `npx jsr publish` loses the GitHub OIDC token JSR requires and publication can fail before Docker runs.</comment>
<file context>
@@ -12,19 +12,23 @@ on:
jobs:
- publish:
+ jsr-publish:
runs-on: ubuntu-latest
</file context>
| #!/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| VERSION="${{ env.RELEASE_TAG }}" |
There was a problem hiding this comment.
P2: Potential script injection via direct ${{ }} interpolation into the shell script. While env.RELEASE_TAG is somewhat trusted (set from workflow_dispatch input or ref_name), the safer pattern is to pass it through the step's env: mapping to avoid shell metacharacter issues.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish.yaml, line 47:
<comment>Potential script injection via direct `${{ }}` interpolation into the shell script. While `env.RELEASE_TAG` is somewhat trusted (set from `workflow_dispatch` input or `ref_name`), the safer pattern is to pass it through the step's `env:` mapping to avoid shell metacharacter issues.</comment>
<file context>
@@ -35,6 +39,41 @@ jobs:
+ #!/bin/bash
+ set -euo pipefail
+
+ VERSION="${{ env.RELEASE_TAG }}"
+
+ for attempt in $(seq 1 30); do
</file context>
Summary
main) when publishing viaworkflow_dispatchjsr-publishanddocker-buildjobs so a Docker failure does not block JSR publicationdeno cache jsr:@deco/deco@<version>/scripts/runsucceeds before building imagesTest plan
1.202.1to confirm JSR resolves before Docker startsjsr-publishjob marked as successMade with Cursor
Summary by cubic
Gates Docker image builds on JSR availability and uses the release tag for manual publishes. Splits the publish workflow so a Docker failure doesn’t block JSR publication.
RELEASE_TAG) onworkflow_dispatchinstead ofmain.jsr-publishanddocker-buildjobs;docker-buildwaits onjsr-publish.deno cache jsr:@deco/deco@<version>/scripts/runresolves (up to 5 minutes) before building images.RELEASE_TAGto keep versions in sync.Written for commit 396cab8. Summary will update on new commits.
Summary by CodeRabbit