Skip to content

Fix publish workflow to gate Docker on JSR availability - #1214

Open
aka-sacci-ccr wants to merge 1 commit into
mainfrom
fix/publish-jsr-verification
Open

Fix publish workflow to gate Docker on JSR availability#1214
aka-sacci-ccr wants to merge 1 commit into
mainfrom
fix/publish-jsr-verification

Conversation

@aka-sacci-ccr

@aka-sacci-ccr aka-sacci-ccr commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Check out the release tag (not main) when publishing via workflow_dispatch
  • Split the workflow into jsr-publish and docker-build jobs so a Docker failure does not block JSR publication
  • Poll JSR after publish until deno cache jsr:@deco/deco@<version>/scripts/run succeeds before building images

Test plan

  • Merge and re-dispatch Publish workflow for 1.202.1 to confirm JSR resolves before Docker starts
  • Verify a tag-triggered release still publishes all three JSR packages and builds Docker images
  • Confirm a Docker build failure leaves the jsr-publish job marked as success

Made 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.

  • Bug Fixes
    • Check out the release tag (RELEASE_TAG) on workflow_dispatch instead of main.
    • Split into jsr-publish and docker-build jobs; docker-build waits on jsr-publish.
    • After publishing, poll until deno cache jsr:@deco/deco@<version>/scripts/run resolves (up to 5 minutes) before building images.
    • Tag Docker images with RELEASE_TAG to keep versions in sync.

Written for commit 396cab8. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Chores
    • Improved the release workflow to use a single consistent release tag across publishing and image builds.
    • Added a verification step to confirm the published package is available before continuing.
    • Split Docker image building into a separate stage that runs after package publishing.

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>
@github-actions

Copy link
Copy Markdown
Contributor

Tagging Options

Should a new tag be published when this PR is merged?

  • 👍 for Patch 1.202.2 update
  • 🎉 for Minor 1.203.0 update
  • 🚀 for Major 2.0.0 update

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

Publish Workflow Restructuring

Layer / File(s) Summary
RELEASE_TAG derivation and jsr-publish job
.github/workflows/publish.yaml
Adds a top-level RELEASE_TAG env var derived from manual input or ref name, and a new jsr-publish job that checks out the pinned ref, sets up Deno v2.4.0, and runs JSR publish steps.
JSR verification and docker-build job
.github/workflows/publish.yaml
Adds a retry-based deno cache verification step (up to 30 attempts) to confirm JSR artifact propagation, and introduces a docker-build job that depends on jsr-publish, with checkout pinned to RELEASE_TAG.
Docker image tag updates
.github/workflows/publish.yaml
Updates IMAGE_TAG_COMMIT for both builder images (including deno2) to use env.RELEASE_TAG instead of directly referencing the input/ref expression.

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
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A tag computed once, then shared with care,
JSR checked twice before Docker dares,
No more racing ref names through the night,
🐇 hop, hop, retry till the cache is right,
Then build the image, tagged and tight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: gating Docker build/publish on JSR availability in the publish workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/publish-jsr-verification

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
.github/workflows/publish.yaml (1)

25-27: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Checkout steps don't disable credential persistence.

Both actions/checkout@v4 steps persist the GITHUB_TOKEN credential in the local git config by default; zizmor flags this (artipacked) at both locations. Worth hardening, especially for the docker-build job 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

📥 Commits

Reviewing files that changed from the base of the PR and between 53410fe and 396cab8.

📒 Files selected for processing (1)
  • .github/workflows/publish.yaml

Comment on lines +18 to +31
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' .github/workflows/publish.yaml

Repository: 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:


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.

Suggested change
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

Comment on lines +42 to +61
- 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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.

Suggested change
- 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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants