Skip to content

fix(release): dev builds bump off last-released + correct npm dist-tags#3733

Draft
insipx wants to merge 1 commit into
mainfrom
fix-dev-release-version
Draft

fix(release): dev builds bump off last-released + correct npm dist-tags#3733
insipx wants to merge 1 commit into
mainfrom
fix-dev-release-version

Conversation

@insipx

@insipx insipx commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Problem

A workflow_dispatch dev release published the already-shipped version under the wrong npm dist-tag — e.g. @xmtp/node-sdk 6.0.0-dev.<sha> tagged prerelease, when it should be 6.1.0-dev.<sha> tagged dev.

Bug 1 — dev didn't bump

After xmtp-js merged into the monorepo, the independent-track SDK manifests on main now hold the last-released version (node-sdk=6.0.0, browser-sdk=7.0.0, ios/android=4.10.0), not the old "next-target in manifest" convention. The dev path called xmtp-release compute-version --release-type dev, which only appends -dev.<sha> to the manifest base (no bump) → it previews the already-shipped version.

The nightly path already solves this: run the git-cliff "plan" oracle to compute the pending {version, kind}, then resolve-sdk-version (applies the pending bump kind to an independent SDK's own base; uses the pending libxmtp version for follows-libxmtp). This PR routes dev through the same path:

  • Add a gate input to release-gate-plan.yml (default true); dev runs it gateless — the cross-test gate is a nightly-only correctness gate and must not block dev.
  • Run the plan job for dev too; widen the six release-* if: clauses so dev runs when the plan succeeded and there is something pending (nothing-pending != 'true').
  • Change the dev branch of all 6 release-*.yml "Compute version" steps from compute-version to resolve-sdk-version (mirrors the existing nightly branch, including the empty-pending guard).

rc/final are unaffected — they run from release/* branches where create-release-branch pre-bumps the manifests, so compute-version is already correct there. Bindings (node/wasm + Cargo, still 1.11.0-dev in-manifest) are unchanged.

Bug 2 — wrong npm dist-tag

npm-publish.yml bucketed both *dev* and *rc* into prerelease. Now each prerelease channel gets its own dist-tag: nightly→nightly, -devdev, -rcrc, else→latest (matches the old xmtp-js dev dist-tag).

Scope

YAML only — no TypeScript change. The lib (resolveSdkVersion) already handled releaseType: "dev" correctly.

Verification

nightly-dry-run.yml gains a release-type input (nightly|dev); the dev path previews the gateless oracle + resolve-sdk-version per SDK and prints the versions — no publish/tag jobs exist, so it structurally cannot mutate any registry.

Ran it end-to-end on a fork. Oracle returned 1.11.0/minor; resolved dev versions:

SDK dev version
node-sdk 6.1.0-dev.<sha> (was 6.0.0-dev)
browser-sdk 7.1.0-dev.<sha>
ios / android 4.11.0-dev.<sha>
node/wasm bindings + libxmtp 1.11.0-dev.<sha> (unchanged — no regression)

Plus: release-tools 172 tests pass, typecheck clean, all 10 workflow YAMLs parse.

🤖 Generated with Claude Code

Note

Fix dev release builds to bump off last-released version and use correct npm dist-tags

  • Dev release workflows now use xmtp-release resolve-sdk-version (instead of compute-version) across all SDK platforms (Android, iOS, Node, WASM, Browser), deriving the dev version from the pending libxmtp version/kind; each job fails early if those inputs are absent.
  • NPM publish now assigns separate dist-tags: dev for -dev versions and rc for -rc versions, replacing the shared prerelease tag used previously.
  • The release gate workflow gains an optional gate boolean input (default true); dev releases pass gate=false to skip cross-test evaluation.
  • The nightly dry-run workflow gains a release-type input (nightly|dev) and a preview-dev-versions job that resolves and displays per-SDK dev versions in the job summary.
  • Behavioral Change: -dev and -rc npm packages that previously shared the prerelease dist-tag are now tagged separately as dev and rc.

Macroscope summarized 6d9d746.

@insipx insipx requested a review from a team as a code owner June 5, 2026 17:36
@claude

claude Bot commented Jun 5, 2026

Copy link
Copy Markdown

Claude finished @insipx's task —— View job


PR Review — June 5, 2026, 6:57 PM UTC

Summary

This PR fixes two bugs in the dev release workflow: (1) dev builds now correctly bump version numbers off the last-released version instead of just appending -dev.<sha>, and (2) npm dist-tags are now properly assigned per channel. The changes route dev releases through the same release-gate-plan workflow that nightly uses, but with the cross-test gate disabled.

Issues Found

1. Critical: Inconsistent ref resolution for dev releases (High severity)
.github/workflows/release.yml:163

When release-type is dev, the workflow passes ref: ${{ needs.validate.outputs.ref }} to the plan job. However, the validate job defaults this to ${GITHUB_REF} for manual workflow_dispatch runs. This means a dev release triggered from a release/* branch (or with a custom inputs.ref) will resolve versions from that branch instead of main, publishing incorrect dev versions.

Fix recommendation: Pin dev releases to always use main:

with:
  ref: ${{ needs.validate.outputs.release-type == 'dev' && 'main' || needs.validate.outputs.ref }}

(Note: This was already identified by Macroscope but is critical enough to emphasize)


2. High: Race condition in nightly-dry-run preview-dev-versions job (Medium-High severity)
.github/workflows/nightly-dry-run.yml:51

The preview-dev-versions job checks out inputs.ref || github.ref_name instead of the exact SHA that the plan job computed (needs.plan.outputs.sha). If the ref is a moving branch (e.g., main) and new commits land between jobs, resolve-sdk-version will read manifests from a different commit than the plan oracle used, producing incorrect preview versions.

Fix recommendation:

- uses: actions/checkout@v6
  with:
    ref: ${{ needs.plan.outputs.sha }}  # Use exact SHA from plan
    fetch-depth: 0

3. Medium: Potential version string injection in npm-publish dist-tag logic (Low-Medium severity)
.github/workflows/npm-publish.yml:108-116

The dist-tag computation uses substring matching ([[ "$VERSION" == *"nightly"* ]]) on the version string without validation. While the version comes from trusted sources (computed by xmtp-release tool), a malformed or unexpected version string could theoretically produce an incorrect tag. Consider adding validation or using a more explicit pattern match.

Suggestion: Add a validation step or use explicit regex patterns:

if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-nightly\. ]]; then
  echo "tag=nightly" >> "$GITHUB_OUTPUT"
elif [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-dev\. ]]; then
  echo "tag=dev" >> "$GITHUB_OUTPUT"
# ... etc

4. Minor: Inconsistent error handling in version computation (Low severity)
Multiple files: release-android.yml:50-52, release-ios.yml:58-60, etc.

All SDK release workflows now fail fast with exit 1 if pending-version or pending-kind are empty for dev releases. This is good defensive programming, but the error messages are generic. Consider adding which SDK failed to aid debugging:

echo "::error::dev resolve called with empty pending version/kind for SDK: android"

5. Code quality: Duplicated version resolution logic (Refactoring opportunity)

All six SDK release workflows (release-{android,ios,node-sdk,browser-sdk,node,wasm}.yml) now contain nearly identical "Compute version" steps with the same branching logic. This creates a maintenance burden — any future change to the version resolution logic must be applied to 6 files.

Suggestion: Consider extracting this into a composite action or reusable workflow to maintain DRY principle.


What Works Well

Gateless dev path is correct — dev releases appropriately skip the cross-test gate which is nightly-specific
npm dist-tag logic covers all cases — nightly, dev, rc, and latest are all properly handled
Fail-fast guards are defensive — empty pending-version/kind validation prevents publishing incorrect versions
Dry-run preview is comprehensivenightly-dry-run.yml now previews dev versions for all SDKs with no publish side effects
Consistent use of resolve-sdk-version — all dev paths now use the correct resolution strategy


Test Coverage

The PR description mentions that release-tools has 172 passing tests and all YAML files parse correctly. However, there's no explicit test coverage for:

  • The new gateless plan job path when gate: false
  • The new dev version resolution across all 6 SDK workflows
  • The npm dist-tag selection logic for all version string patterns

Recommendation: Consider adding integration tests or at minimum, document the manual verification performed via the fork run.


Security Considerations

No significant security issues. The workflows properly use secrets: inherit and don't expose sensitive data. The version computation is deterministic and sourced from trusted inputs.


Performance

No performance concerns. The changes add minimal overhead (one additional conditional check per workflow).


Overall Assessment

The core fix is sound and addresses real bugs in the dev release flow. The two critical issues (#1 and #2 above) should be addressed before merge to prevent dev releases from computing versions against the wrong commit. The refactoring opportunity (#5) is not blocking but would improve long-term maintainability.

Macroscope's approvability verdict of "Needs human review" is appropriate given the unresolved ref-resolution issues.


Review completed at 2026-06-05 18:57:03 UTC

Comment thread .github/workflows/nightly-dry-run.yml
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jun 5, 2026
@macroscopeapp

macroscopeapp Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

2 blocking correctness issues found. An unresolved high-severity review comment identifies missing output exports (node, wasm) that could prevent Node/WASM bindings from being published. This potential bug in the release workflow should be verified before merging.

You can customize Macroscope's approvability policy. Learn more.

@insipx insipx force-pushed the fix-dev-release-version branch from aef33d4 to 457aebd Compare June 5, 2026 18:42
@macroscopeapp macroscopeapp Bot dismissed their stale review June 5, 2026 18:42

Dismissing prior approval to re-evaluate 457aebd

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml
A workflow_dispatch 'dev' release published the already-shipped version under
the wrong npm dist-tag (e.g. @xmtp/node-sdk 6.0.0-dev.<sha> tagged 'prerelease'
instead of 6.1.0-dev.<sha> tagged 'dev').

Bug 1 — dev did not bump. After xmtp-js merged in, independent-track SDK
manifests on main hold the last-released version (node-sdk=6.0.0,
browser-sdk=7.0.0, ios/android=4.10.0), not a next-target. The dev path called
'compute-version --release-type dev', which only appends '-dev.<sha>' to that
base (no bump). The nightly path already solves this via the git-cliff oracle +
resolve-sdk-version; route dev through the same path:
  - add a 'gate' input to release-gate-plan.yml (default true); dev runs it
    gateless (the cross-test gate is a nightly-only correctness gate)
  - run the plan job for dev too; widen the release-* if-gating so dev runs when
    the plan succeeded and there is something pending
  - change the dev branch of all 6 release-*.yml compute steps from
    compute-version to resolve-sdk-version (mirrors the nightly branch)
rc/final are unaffected (they run from release/* branches with pre-bumped
manifests). Bindings (still 1.11.0-dev in-manifest) are unchanged.

Bug 2 — wrong npm dist-tag. npm-publish.yml bucketed dev+rc into 'prerelease';
now nightly->nightly, -dev->dev, -rc->rc, else->latest.

nightly-dry-run.yml gains a release-type input (nightly|dev); the dev path
previews the gateless oracle + resolve-sdk-version per SDK (no publish jobs).

Verified end-to-end via nightly-dry-run on a fork: oracle 1.11.0/minor ->
node-sdk 6.1.0-dev, browser 7.1.0-dev, ios/android 4.11.0-dev, bindings
1.11.0-dev (unchanged).
@insipx insipx force-pushed the fix-dev-release-version branch from 457aebd to 6d9d746 Compare June 5, 2026 18:55
@insipx

insipx commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback (head now 6d9d746):

Macroscope blocker — dead release-node/release-wasm jobs (FIXED). This was a bad rebase resolution on my part: #3734 deleted the standalone top-level release-node/release-wasm jobs (node/wasm bindings now publish inside the SDKs via the bindings: reusable-workflow call), but my conflict resolution resurrected them — and they referenced validate.outputs.node/wasm, which #3734 stopped exporting, so they'd be permanently skipped. I rebuilt release.yml from current main and re-applied only my deltas (run plan for dev too, gateless; widen the nightly gate clause to nightly+dev on the 4 surviving SDK jobs: ios/android/node-sdk/browser-sdk). The dev fix still reaches node/wasm bindings through the bindings: reuse (release-node.yml/release-wasm.yml carry the resolve-sdk-version dev branch and receive pending-version/pending-kind).

Claude review — SHA drift in the dev dry-run preview (FIXED). nightly-dry-run.yml's preview-dev-versions now checks out needs.plan.outputs.sha (the exact commit the oracle resolved against) instead of branch HEAD, so the previewed short-sha can't drift.

Doc clarity (FIXED). Updated the release-gate-plan.yml header comment to describe the dual nightly+dev usage and the gateless dev path; renamed the step-summary heading "Nightly release plan" → "Release plan".

Re-verified end-to-end on a fork after the rebase (run): oracle 1.11.0/minor → node-sdk 6.1.0-dev.6d9d746, browser 7.1.0-dev, ios/android 4.11.0-dev, bindings/libxmtp 1.11.0-dev. The dry-run workflow has no publish/tag jobs, so it cannot mutate any registry.

Left as non-blocking (out of scope for this fix): the theoretical 1.2.3-devmode.4 dist-tag substring edge case (versions are generated by xmtp-release, controlled), and adding actionlint/an automated dev dry-run to CI (process improvement).

@insipx insipx marked this pull request as draft June 5, 2026 20:23
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.

1 participant