Skip to content

ci: publish only the verified release tarball and split the GitHub release job - #317

Merged
thomson-t merged 3 commits into
mainfrom
ci/release-publish-verified-tarball
Oct 15, 2026
Merged

thomson-t merged 3 commits into
mainfrom
ci/release-publish-verified-tarball

Conversation

@thomson-t

@thomson-t thomson-t commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Why

A release should publish exactly the tarball that its own run built. Today the publish step does not guarantee that:

  • It collects every artifact in the run that matches a name pattern and publishes whichever file has the expected name.
  • It shares a job with the GitHub release step.
  • It tags the release at the default branch head, not at the commit that was built.
  • It passes the maintenance branch name straight through as the npm dist-tag, so maintenance/4.x requests 4.x. npm rejects that tag because it reads as a version range.
  • It publishes a prerelease VERSION from main as latest.

Once this lands:

  • the published tarball must match the digest recorded at build time;
  • the GitHub release runs separately and tags the built commit;
  • maintenance releases get a dist-tag that npm accepts;
  • prereleases get their own dist-tag and never move latest.

Programme

Standalone change, no programme.

What changes

  • build-package outputs the tarball's SHA-256 and artifact name.
  • release-and-tag becomes two jobs:
    • publish-npm downloads that artifact by exact name, checks it with sha256sum --check, and runs npm publish. It has no checkout and no npm cache, holds id-token: write with contents: read, and runs in the npm-publish environment.
    • github-release holds contents: write and no id-token. It verifies the same file, then creates the release with that exact tarball, tagged at github.sha.
  • setup-and-version requires VERSION to be MAJOR.MINOR.PATCH[-prerelease].
    • On main, the release publishes as latest.
    • maintenance/<N>.x publishes as maintenance-<N>.x when VERSION's major is N.
    • Any other branch name, or a major mismatch, stops the run before publishing.
    • A prerelease VERSION publishes as next from main, or as maintenance-<N>.x-next from a maintenance branch. The GitHub release is marked latest only for a latest publish.

Start reading at .github/workflows/release-publish.yml.

Linked work

None.

Rollout

Path: CI only, and merging publishes nothing. The build-package action is shared with pull-request.yml, so from merge every PR's package job also records the digest; this PR's own CI runs that step. The release jobs change from the next Release – Publish run, which happens when VERSION changes.
Feature flags: none.
Turning it off: revert this pull request. The revert applies from the next workflow run.
What we watch: the package job on this PR and on later PRs, and the publish-npm and github-release jobs of the next release. A failing digest check, dist-tag step or publish means we revert.

Risks

  • The digest check rejects the genuine tarball.
    • Mitigation: both jobs read the same stored artifact.
    • If it happens: the run stops before npm publish, so nothing is published.
  • github-release fails after publish-npm succeeds.
    • Mitigation: none. The old job also published before it created the release.
    • If it happens: the npm version exists without a GitHub release. Re-run the failed job.
  • Maintenance releases now use the dist-tag maintenance-<N>.x.
    • Mitigation: none needed. <N>.x was refused by npm, and no maintenance dist-tag exists today.
    • If it happens: installs by tag use the new name.

Risk class: medium. The release jobs only run end to end on a real release.

Who

Written by: the pull request's author.
Code reviewed before opening: no one.
Design reviewed before opening: no one.
Decision this implements: no separate decision record.
Checked: on 14 and 15 October 2026, locally:

  • Ran the version and dist-tag step under bash against 13 branch and VERSION cases, including prereleases.
  • Ran the digest check with a matching digest, a mismatched one, a missing file and an empty digest.
  • Confirmed with npm's semver module that 4.x is a range and maintenance-4.x is not.
  • Ran trunk check on both files.

Not checked: a real release run.

Size

Hand-written: 103 lines (74 added, 29 removed) across 2 files.
Generated: none.

🤖 Generated with Claude Code

The publish job now downloads the package artifact by its exact name and
checks the tarball's SHA-256 against the digest recorded by build-package
before running npm publish. It no longer restores an npm cache, and it
runs in the npm-publish environment.

Creating the GitHub release moves to its own job, which holds only the
contents write grant, attaches the same verified tarball, and tags the
commit that was built.

setup-and-version now requires VERSION to be a semantic version and
derives the npm dist-tag: main publishes as latest, and maintenance/<N>.x
publishes as maintenance-<N>.x only for versions in major N. npm refuses
dist-tags that parse as semver ranges, such as 4.x. Other branches stop
before publishing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@thomson-t
thomson-t requested a review from a team as a code owner October 15, 2026 17:07
@paved-road-agent

paved-road-agent Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🛣️ Rokt Platform Paved Roads Scan Complete

✅ No paved-road issues detected

Scanned files against 188 rules. · Reviewed commit: 3f0a55158e

📜 Scan history (3 scans)
Commit Time (UTC) Status Findings
3f0a551 2026-10-15 17:48:55 ✅ success 0
26fd0d9 2026-10-15 17:34:57 ✅ success 0
f8e8b97 2026-10-15 17:07:17 ✅ success 0
ℹ️ About Rokt Platform Paved Roads Agent

This scan was performed by the Rokt Platform Paved Roads Agent, an automated paved-road standards review tool for pull requests. See the User Guide for details on severity levels, feedback reactions, and scan configuration.

Reply @paved-road-agent help for the list of commands you can run on this PR.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T17:09:44.968130Z f8e8b97 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pr-security-agent

pr-security-agent Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🔒 Security Scan Complete

✅ No security issues detected

Scanned files against 71 rules. · Reviewed commit: 3f0a55158e

📜 Scan history (3 scans)
Commit Time (UTC) Status Findings
3f0a551 2026-10-15 17:49:10 ✅ success 0
26fd0d9 2026-10-15 17:35:14 ✅ success 0
f8e8b97 2026-10-15 17:07:32 ✅ success 0
ℹ️ About PR Security Agent

This scan was performed by PR Security Agent, an automated security review tool for pull requests. See the User Guide for details on severity levels, feedback reactions, and scan configuration.

Reply @pr-security-agent help for the list of commands you can run on this PR.

@claude claude 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.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Besides the inline finding, I checked two other things and neither is a problem. First, the new artifact-name output is set: the package-tarball step writes it at .github/actions/build-package/action.yml:53. Second, the downloaded tarball ends up at Rokt.Widget/rokt-react-native-sdk-<version>.tgz. Both uploaded files sit under Rokt.Widget/, so the artifact stores them at its root, and that matches the path the digest checks and the release upload expect.

Extended reasoning...

I confirmed the composite action writes the artifact-name output that the new download steps use. I also confirmed that the upload-artifact path layout puts the tarball where the sha256sum --check lines and the release upload look for it.

Comment thread .github/workflows/release-publish.yml Resolved
A prerelease VERSION such as 5.0.0-rc.1 on main was published as npm
latest and marked as the latest GitHub release. Prereleases now publish
under next on any release branch, and the GitHub release is marked latest
only when the npm tag is latest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment thread .github/workflows/release-publish.yml Resolved
A prerelease from maintenance/<N>.x moved next, which main's prereleases
use, back to an older major. Maintenance prereleases now publish under
maintenance-<N>.x-next, and only main's prereleases use next.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@claude claude 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.

I reviewed the latest push and didn't find any new bugs. Both of my earlier dist-tag comments are addressed in the code. Because this changes the npm OIDC publishing pipeline, which CODEOWNERS assigns to the SDK team, a human look before the next release is still worthwhile.

What was reviewed:

  • Prerelease tagging: a prerelease on main now gets next, and one on maintenance/<N>.x gets maintenance-<N>.x-next. makeLatest now depends on the computed tag being latest, not on the branch.
  • Artifact handoff: artifact-name and sha256 are produced by the package-tarball step, passed on as job outputs, and checked in both publish-npm and github-release before the tarball is used.
  • Permissions: publish-npm keeps id-token: write with only contents: read and no checkout. github-release has contents: write and no id-token.
Extended reasoning...

The change touches two CI files: the shared build-package composite action, which now records the tarball's SHA-256 and artifact name, and release-publish.yml. In release-publish.yml the combined release job becomes separate publish-npm and github-release jobs, and dist-tag selection plus a semver check move into setup-and-version. The sensitive surface is the npm trusted-publishing path (id-token: write, the new npm-publish environment) and the release job's contents: write token. The two findings raised on earlier pushes, prereleases on main publishing as latest and maintenance prereleases taking over next, are fixed in the current diff. I deferred instead of approving because .github/workflows/ and .github/actions/ belong to a CODEOWNERS team and the release jobs only run end to end on a real release.

Comment thread .github/workflows/release-publish.yml Resolved

@nickolas-dimitrakas nickolas-dimitrakas 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.

Approving. Digest check, job split, permissions and dist-tag logic look right; I confirmed with semver that maintenance-4.x and maintenance-4.x-next are not ranges. Before merging: the npm-publish environment doesn't exist yet, so create it (required reviewers, restrict to main and maintenance/*) and confirm the npm trusted-publisher entry matches it, or the first release will fail at publish.

@thomson-t
thomson-t merged commit 172c700 into main Oct 15, 2026
14 checks passed
@thomson-t
thomson-t deleted the ci/release-publish-verified-tarball branch October 15, 2026 21:18
@rokt-releases rokt-releases Bot mentioned this pull request Oct 26, 2026
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