Skip to content

ci: fix latest.json generation in release workflow#23

Merged
Nic-dorman merged 1 commit intomainfrom
ci/fix-latest-json-generation
Apr 16, 2026
Merged

ci: fix latest.json generation in release workflow#23
Nic-dorman merged 1 commit intomainfrom
ci/fix-latest-json-generation

Conversation

@Nic-dorman
Copy link
Copy Markdown
Contributor

Summary

Rewrites the release workflow to eliminate three classes of bug that corrupted the updater manifests in recent releases:

  • v__VERSION__ leaked into mac/linux URLs (v0.6.3) — tauri-action was emitting its own latest.json from 3 parallel matrix jobs, with the literal template as the tag name. Fix: includeUpdaterJson: false on tauri-action, real tag passed as tagName, latest.json composed once in a final job.
  • untagged-<id> slug in the Windows MSI URL (v0.6.3) — the release was created via gh api ... without the tag fully attached, and the Windows MSI upload (fastest platform) froze that placeholder into the manifest. Fix: create-release now uses gh release create --draft and asserts gh release view --json tagName matches before any platform job starts.
  • Parallel-write race on latest.json (v0.6.2 — Intel-mac cleanup step failed trying to delete an asset another job had already deleted). Fix: only one job (compose-latest-json, needs: all platforms) ever writes latest.json.

New safety net

compose-latest-json runs jq assertions before uploading the manifest — fails the workflow if any URL contains __VERSION__ or /releases/download/untagged-, any signature is empty/null, or the platform key set drifts from the expected 9. Catches regressions before they ship.

Dry-run

Ran the new composer locally against v0.6.4's already-uploaded .sig files — the generated platforms map is byte-identical to the hand-fixed latest.json currently on the v0.6.4 draft.

Test plan

  • Cut an rc tag (e.g. v0.6.5-rc.1) and confirm the full workflow runs green
  • Inspect the resulting latest.json — all 9 platform URLs should be /releases/download/v0.6.5-rc.1/... (no __VERSION__, no untagged-*)
  • Confirm compose-latest-json job logs show the integrity assertion passing
  • Verify Tauri updater on mac/linux/windows can read the manifest and offers the upgrade
  • Once confirmed, cut v0.6.5 and ship

Notes

  • Does not affect already-tagged releases (v0.6.4 and earlier); GH Actions uses the workflow file from the tag's ref.
  • tauri-action stays at @v0 — the problem wasn't the action version (we're already on the latest v0.6.2), it was how we were driving it.

🤖 Generated with Claude Code

Rewrites the release workflow to eliminate three classes of bug that
corrupted v0.6.2 and v0.6.3's updater manifests:

1. `v__VERSION__` template leakage into mac/linux URLs — tauri-action's
   `includeUpdaterJson` is now disabled and `tagName` receives the real
   tag, so the action no longer emits its own (racing) latest.json.
2. `untagged-<id>` slug in the Windows MSI URL — `create-release` now
   uses `gh release create` and asserts the release reports the correct
   tagName before any platform job starts uploading.
3. Parallel-write race on latest.json — the merge step is now a single
   authoritative composer (runs after all platform jobs) that reads
   .sig files, builds URLs from OWNER/REPO/TAG/ASSET, and emits the
   full 9-entry platforms map in one atomic jq invocation.

Adds a jq-based integrity assertion in the composer that fails the
workflow if any URL contains `__VERSION__` or `/releases/download/
untagged-`, any signature is empty, or the platform key set drifts
from the expected 9. Catches future regressions of the same class
before a broken manifest ships.

Dry-run of the new composer against v0.6.4's released .sig files
produced a platforms map identical to the hand-fixed latest.json
currently on the v0.6.4 draft.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jacderida
Copy link
Copy Markdown
Contributor

Reviewed on behalf of @chriso83 — sharing the analysis here so the reasoning is visible on the PR.

On the "draft release" concern

Worth calling out up front: this PR does not introduce draft-release behavior. The release has always been created as a draft, and it still is after this change.

  • Before (main, line 112): -F draft=true on the gh api call.
  • After (this PR): gh release create --draft.

Both produce a draft. The switch to gh release create is specifically because it attaches the pushed git tag to the release object at creation time, which is what fixes the untagged-<id> slug that corrupted the Windows MSI URL in v0.6.3's latest.json.

Why the draft is intentional

The draft is a staging area:

  1. create-release makes an empty draft.
  2. Parallel build jobs (linux, mac-arm, mac-x64, windows) upload .msi / .app.tar.gz / .AppImage / .deb / .rpm + .sig files into it.
  3. compose-latest-json assembles and uploads latest.json.
  4. A human clicks Publish release in the GitHub UI.

Nothing in the workflow (before or after this PR) auto-publishes, so the draft → published flip stays manual. That's the safety gate you want — a chance to review the bundle before it's live to the Tauri updater.

Review of the actual fixes

The three fixes line up cleanly with the three bugs described:

  • v__VERSION__ leak into mac/linux URLs (v0.6.3) — root cause was tauri-action emitting its own latest.json from three parallel matrix jobs with the unsubstituted template as the tag name. Fix correctly sets includeUpdaterJson: false and passes a concrete v${{ needs.release-meta.outputs.version }} as tagName. Good.
  • untagged-<id> slug in Windows MSI URL (v0.6.3) — fixed by gh release create + a hard assertion (attached_tag != $tag → exit 1) before any platform job runs. Solid, and the assertion is exactly the right place to fail fast.
  • Parallel-write race on latest.json (v0.6.2) — resolved by consolidating to a single writer (compose-latest-json, needs: all platforms). Correct.

The integrity gate in compose-latest-json is a nice belt-and-braces — it fails the workflow before upload if any URL regresses to __VERSION__ or untagged-*, any signature is empty/null, or the 9-platform key set drifts from expected. That's exactly the kind of assertion you want after a bug that actually shipped.

One thing worth emphasizing

The byte-identical dry-run against v0.6.4's .sig files is reassuring, but the real validation is test-plan item 1: cut a v0.6.5-rc.1 and watch the workflow end-to-end. I'd recommend not merging + cutting v0.6.5 directly — run the rc first to prove the composer works through the full pipeline, as the author also suggests.

TL;DR

No new drafting behavior. The PR fixes genuine updater-manifest bugs from v0.6.2 / v0.6.3 and adds assertions to prevent regressions. LGTM to merge, then validate on an rc before the next real release.

@Nic-dorman Nic-dorman merged commit 60c9870 into main Apr 16, 2026
4 checks passed
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Release candidate tag to validate the release-workflow fix landed in
PR #23 before cutting v0.6.5. Marks as a GitHub pre-release so the
generated `latest.json` can be inspected without being served to
users on the auto-updater path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Release candidate tag to validate the release-workflow fix landed in
PR #23 before cutting v0.6.5. Marks as a GitHub pre-release so the
generated `latest.json` can be inspected without being served to
users on the auto-updater path.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Tauri's MSI bundler errors on non-numeric pre-release identifiers:
```
failed to bundle project `optional pre-release identifier in app version
must be numeric-only and cannot be greater than 65535 for msi target`
```

This blocks v0.6.5-rc.1 (and any `-rc.N` / `-beta.N` / `-alpha.N` tag)
from producing the Windows MSI, which in turn skips compose-latest-json
(it needs: build-windows), leaving the release workflow fix from PR #23
unvalidated.

The constraint is Windows-specific (MSI inherits the 4-part numeric PE
resource version). Fix by computing a numeric-only `wix.version` from
tauri.conf.json at build time — strip everything after `-` so `0.6.5-rc.1`
becomes `0.6.5` for MSI metadata only. The app's embedded semver (what
the updater compares, what `env!("CARGO_PKG_VERSION")` returns) stays
untouched, so pre-release tags still behave as pre-releases everywhere
except the Windows installer's Add/Remove Programs display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Tauri's MSI bundler errors on non-numeric pre-release identifiers:
```
failed to bundle project `optional pre-release identifier in app version
must be numeric-only and cannot be greater than 65535 for msi target`
```

This blocks v0.6.5-rc.1 (and any `-rc.N` / `-beta.N` / `-alpha.N` tag)
from producing the Windows MSI, which in turn skips compose-latest-json
(it needs: build-windows), leaving the release workflow fix from PR #23
unvalidated.

The constraint is Windows-specific (MSI inherits the 4-part numeric PE
resource version). Fix by computing a numeric-only `wix.version` from
tauri.conf.json at build time — strip everything after `-` so `0.6.5-rc.1`
becomes `0.6.5` for MSI metadata only. The app's embedded semver (what
the updater compares, what `env!("CARGO_PKG_VERSION")` returns) stays
untouched, so pre-release tags still behave as pre-releases everywhere
except the Windows installer's Add/Remove Programs display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Tauri's MSI bundler errors on non-numeric pre-release identifiers:
```
failed to bundle project `optional pre-release identifier in app version
must be numeric-only and cannot be greater than 65535 for msi target`
```

This blocks v0.6.5-rc.1 (and any `-rc.N` / `-beta.N` / `-alpha.N` tag)
from producing the Windows MSI, which in turn skips compose-latest-json
(it needs: build-windows), leaving the release workflow fix from PR #23
unvalidated.

The constraint is Windows-specific (MSI inherits the 4-part numeric PE
resource version). Fix by computing a numeric-only `wix.version` from
tauri.conf.json at build time — strip everything after `-` so `0.6.5-rc.1`
becomes `0.6.5` for MSI metadata only. The app's embedded semver (what
the updater compares, what `env!("CARGO_PKG_VERSION")` returns) stays
untouched, so pre-release tags still behave as pre-releases everywhere
except the Windows installer's Add/Remove Programs display.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Promotes v0.6.5-rc.1 (validated via the release-workflow fix in PR #23)
to a full release. Draft release cut from this tag will ship via the
auto-updater once published.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nic-dorman added a commit that referenced this pull request Apr 16, 2026
Promotes v0.6.5-rc.1 (validated via the release-workflow fix in PR #23)
to a full release. Draft release cut from this tag will ship via the
auto-updater once published.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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