Skip to content

ci(release): stop the macOS preview updater bundle colliding with itself - #1281

Merged
debpalash merged 4 commits into
mainfrom
fix/preview-macos-updater-asset-collision
Jul 29, 2026
Merged

ci(release): stop the macOS preview updater bundle colliding with itself#1281
debpalash merged 4 commits into
mainfrom
fix/preview-macos-updater-asset-collision

Conversation

@debpalash

@debpalash debpalash commented Jul 27, 2026

Copy link
Copy Markdown
Owner

The nightly preview run has been failing on both macOS legs since early July, and nobody noticed because the failure lands after the dmg upload — so each run looked partly successful while going red.

Uploading OmniVoice Studio_x64.app.tar.gz...
##[error]Validation Failed: {"resource":"ReleaseAsset","code":"already_exists","field":"name"}

Cause

preview is a rolling release, reused every night. macOS updater artifacts are the only ones Tauri names without the version:

Artifact Name Result
dmg OmniVoice Studio_0.4.1-103_x64.dmg unique per run — uploads fine
updater OmniVoice Studio_x64.app.tar.gz constant — collides on run 2+

Windows .msi and Linux .AppImage are version-stamped too, so only macOS is affected.

Impact (verified against the live release)

Asset Last written
OmniVoice.Studio_x64.app.tar.gz 2026-07-04
OmniVoice.Studio_aarch64.app.tar.gz 2026-07-05
latest.json 2026-07-13

Three weeks stale as of today. Preview-channel macOS users have had no working update path, which per CLAUDE.md is a release-channel bug to fix immediately rather than backlog.

Fix

Delete this arch's updater bundle before uploading the new one.

  • Matches the stored name by querying the release rather than guessing the spelling — GitHub rewrites spaces to dots, so OmniVoice Studio_x64.app.tar.gz is stored as OmniVoice.Studio_x64.app.tar.gz, and gh release delete-asset with the uploaded name would silently no-op. That subtlety is why the naive version of this fix wouldn't have worked.
  • Scoped to the preview path — a v* tag creates a fresh release with nothing to collide with.
  • Scoped to the job's own arch, so the parallel aarch64/x64 legs never touch each other's assets.
  • Non-fatal if the asset is absent.

Verified the filter against all 209 live preview assets: it matches exactly the 4 colliding updater files and no versioned artifact.

Not addressed here

Those 209 assets are themselves a problem — every night's dmg/msi/AppImage accumulates on the rolling release forever. Worth a retention sweep, but that's a separate change with its own blast radius.

Updated the nightly macOS release.yml workflow to, on rolling preview runs, query the existing preview prerelease assets and delete any matching stale architecture-specific updater bundle .app.tar.gz (and optional .sig) before uploading, failing only on unexpected gh errors (authorization/rate limits/network/arch issues) while treating missing releases/assets as benign. This prevents updater bundle name collisions that caused preview release failures after earlier partial uploads. Also added caveat-aware engine-selection warning toasts (engines.selectWithCaveat) with updated localized strings and corresponding test coverage; please review the preview-only asset deletion matching scope/patterns and the new “routing_reason → warning toast and exit” behavior for correctness.

debpalash and others added 2 commits July 28, 2026 04:42
…arding it

Four of the open low-VRAM reports (#1240, #1246, #1248 on 4 GB cards; #1277
on 6 GB) share one shape: the user generates, waits out the entire 300s
compute budget, and is then told the job "was too heavy for the available
compute".

The warning existed the whole time. Routing computes it (#1226's `_caveat`:
"…has 4.0 GB VRAM; this engine wants about 6 GB. It will run, but expect slow
generations that may time out"), and `/engines/select` echoes it in
`routing_reason` — but notifyEngineSelected only surfaced a reason when
`routing_status === 'cpu_fallback'`. The VRAM caveat rides on an ACCELERATED
verdict, so it fell through to the green "switched" success toast and was
thrown away. The user was told everything was fine, then waited five minutes
to find out it wasn't.

Now any caveat on the echo raises a warn-tone toast naming it, with a longer
duration since it lists the ways around the limit. This covers the kernel-risk
caveat on the same path.

Deliberately still ADVISORY, not blocking — matching the routing layer's
documented contract (the driver can page to system RAM, and short inputs fit
where long ones don't). The engine is still selected; the user just finds out
now instead of after the timeout. This is the first-run path too: the wizard's
library step shares notifyEngineSelected.

Fail-before verified: both new tests fail against the previous version.

Known remaining gap: a user whose engine is already selected sees this only
when they re-pick. A generate-time preflight would close that, but it needs a
"once per session, not per generate" design — filed as follow-up rather than
guessed at here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The nightly preview run has failed on both macOS legs since early July:

  Uploading OmniVoice Studio_x64.app.tar.gz...
  ##[error]Validation Failed: {"resource":"ReleaseAsset",
                               "code":"already_exists","field":"name"}

`preview` is a ROLLING release, reused every night, and macOS updater
artifacts are the only ones Tauri names without the version:

  OmniVoice Studio_0.4.1-103_x64.dmg   unique per run — uploads fine
  OmniVoice Studio_x64.app.tar.gz      constant — collides on run 2+

Consequences, verified against the live release: the macOS updater bundles on
`preview` were last written 2026-07-04 (x64) and 2026-07-05 (aarch64), and
latest.json 2026-07-13 — three weeks stale as of today. Preview-channel macOS
users had no working update path. The failure also lands AFTER the dmg
upload, so each run looked partly successful while going red.

Deletes this arch's updater bundle before the upload. Matches the STORED
asset name by querying the release rather than guessing the spelling — GitHub
rewrites spaces to dots, so "OmniVoice Studio_x64.app.tar.gz" is stored as
"OmniVoice.Studio_x64.app.tar.gz" and a literal delete-asset by the uploaded
name would silently no-op.

Scoped to the preview path (a v* tag creates a fresh release with nothing to
collide with) and to the job's own arch, so the parallel aarch64/x64 legs
can't touch each other's assets. Verified the filter against all 209 live
preview assets: it matches exactly the 4 colliding updater files and no
versioned artifact.

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

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Prevents rolling macOS preview updater collisions.

  • Deletes only the current architecture’s stale updater bundle and signature before upload.
  • Treats missing assets as benign while failing on authorization, rate-limit, and network errors.
  • Adds regression coverage for asset scoping and GitHub CLI failure handling.

Important Files Changed

Filename Overview
.github/workflows/release.yml Adds preview-only, architecture-scoped cleanup of stale macOS updater assets before the Tauri release upload.
tests/test_release_preview_asset_cleanup.py Exercises the workflow shell body against stubbed GitHub CLI responses, including missing assets, authorization failures, and architecture filtering.

Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/preview-mac..." | Re-trigger Greptile

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bb1a13b-fa02-46f7-8f88-e17fab9ac663

📥 Commits

Reviewing files that changed from the base of the PR and between 6975534 and 9e6ac5c.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • tests/test_release_preview_asset_cleanup.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/release.yml

📝 Walkthrough

Walkthrough

The workflow removes stale architecture-specific macOS updater assets from preview releases. The frontend adds localized caveat messages and changes engine-selection notifications to show warning toasts when routing reasons are returned, with tests for VRAM and kernel-risk cases.

Changes

Preview release asset cleanup

Layer / File(s) Summary
Remove stale macOS preview assets
.github/workflows/release.yml, tests/test_release_preview_asset_cleanup.py
The macOS preview build removes architecture-specific updater assets before upload, while tests cover missing releases, read failures, selective deletion, already-deleted assets, and denied deletion.

Engine caveat notifications

Layer / File(s) Summary
Add caveat message translations
frontend/src/i18n/locales/*.json
Adds engines.selectWithCaveat templates containing the selected engine and routing reason across supported locales.
Show warning toasts for routing caveats
frontend/src/utils/engineSelectToast.js, frontend/src/test/engineSelectToast.test.js
Displays a warning toast for selections with routing_reason and tests VRAM and kernel-risk caveat cases instead of success notifications.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 6 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title fits the release fix, but it lacks the required issue reference in the title or body. Add the issue key or link to the title or description, while keeping the conventional-commit scope.
Description check ⚠️ Warning The description is useful, but it does not follow the required template sections like Summary, Changes, Type, Testing, Checklist, and Release cadence. Rewrite the PR description using the repository template and fill in Summary, Changes, Type, Testing, Checklist, and Release cadence.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
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.
Cross-Platform Default Parity ✅ Passed The only scoped behavioral change is a preview-gate macOS release-workflow step; no default runtime path diverges across platforms.
I18n Completeness (21 Locales) ✅ Passed selectWithCaveat exists in all 21 locale files; the changed frontend code uses t(...) for user-facing text and adds no hardcoded strings.
Local-First Guarantee ✅ Passed Runtime JS only changes local toast/translation text; no network, telemetry, account, or key usage appears there, and the gh release calls are confined to CI workflow.
Backward Compatibility ✅ Passed Only the release workflow and its test changed; no backend schema, omnivoice_data handling, or engine model-state code was touched, so no migration/reinstall risk.

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/release.yml:
- Around line 554-568: Update the preview cleanup commands around gh release
view and gh release delete-asset to distinguish expected missing release/asset
responses from authentication, permission, network, and transient failures.
Continue successfully only when the release or target asset is genuinely absent;
otherwise propagate the gh failure so the workflow stops before the Tauri
upload. Do not use unconditional || true or failure-swallowing fallbacks for
these API calls.

In `@frontend/src/i18n/locales/pl.json`:
- Line 582: Replace the selectWithCaveat value with natural, locale-specific
wording in frontend/src/i18n/locales/pl.json#L582-L582,
frontend/src/i18n/locales/pt.json#L582-L582,
frontend/src/i18n/locales/ru.json#L582-L582,
frontend/src/i18n/locales/sv.json#L582-L582,
frontend/src/i18n/locales/th.json#L582-L582,
frontend/src/i18n/locales/tr.json#L582-L582,
frontend/src/i18n/locales/uk.json#L582-L582,
frontend/src/i18n/locales/vi.json#L582-L582,
frontend/src/i18n/locales/zh-CN.json#L541-L541, and
frontend/src/i18n/locales/zh-TW.json#L582-L582, while preserving the {{engine}}
and {{reason}} placeholders.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c33faa79-2795-4953-8084-7056a3d07829

📥 Commits

Reviewing files that changed from the base of the PR and between b01c635 and 6975534.

📒 Files selected for processing (24)
  • .github/workflows/release.yml
  • frontend/src/i18n/locales/ar.json
  • frontend/src/i18n/locales/de.json
  • frontend/src/i18n/locales/en.json
  • frontend/src/i18n/locales/es.json
  • frontend/src/i18n/locales/fr.json
  • frontend/src/i18n/locales/hi.json
  • frontend/src/i18n/locales/id.json
  • frontend/src/i18n/locales/it.json
  • frontend/src/i18n/locales/ja.json
  • frontend/src/i18n/locales/ko.json
  • frontend/src/i18n/locales/nl.json
  • frontend/src/i18n/locales/pl.json
  • frontend/src/i18n/locales/pt.json
  • frontend/src/i18n/locales/ru.json
  • frontend/src/i18n/locales/sv.json
  • frontend/src/i18n/locales/th.json
  • frontend/src/i18n/locales/tr.json
  • frontend/src/i18n/locales/uk.json
  • frontend/src/i18n/locales/vi.json
  • frontend/src/i18n/locales/zh-CN.json
  • frontend/src/i18n/locales/zh-TW.json
  • frontend/src/test/engineSelectToast.test.js
  • frontend/src/utils/engineSelectToast.js

Comment thread .github/workflows/release.yml Outdated
Comment thread frontend/src/i18n/locales/pl.json Outdated
The cleanup step treated every `gh` failure as "nothing to clear" — 401, 403,
429 and network errors included. That reintroduces the outage it was written
to fix, with the evidence removed: the stale bundle survives, the Tauri upload
dies with `already_exists`, and the one step that could have explained why is
green. Three weeks of broken macOS Preview updates started exactly this way.

Only an absent release/asset is benign now. A 404 on view means "no preview
release yet" (GH_TOKEN is scoped to this repo, so 404 really is absence); a
404 on delete means someone already removed it, which satisfies the goal. Every
other failure fails the step with the reason printed. An unexpected arch is
also fatal rather than a silent skip — same class of blind spot.

Adds tests/test_release_preview_asset_cleanup.py, which extracts this step's
real shell body from release.yml (so it cannot drift) and runs it against a
stubbed `gh`: 6 of the 8 cases fail against the previous version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread frontend/src/utils/engineSelectToast.js Outdated
# Conflicts:
#	frontend/src/i18n/locales/ar.json
#	frontend/src/i18n/locales/de.json
#	frontend/src/i18n/locales/en.json
#	frontend/src/i18n/locales/es.json
#	frontend/src/i18n/locales/fr.json
#	frontend/src/i18n/locales/hi.json
#	frontend/src/i18n/locales/id.json
#	frontend/src/i18n/locales/it.json
#	frontend/src/i18n/locales/ja.json
#	frontend/src/i18n/locales/ko.json
#	frontend/src/i18n/locales/nl.json
#	frontend/src/i18n/locales/pl.json
#	frontend/src/i18n/locales/pt.json
#	frontend/src/i18n/locales/ru.json
#	frontend/src/i18n/locales/sv.json
#	frontend/src/i18n/locales/th.json
#	frontend/src/i18n/locales/tr.json
#	frontend/src/i18n/locales/uk.json
#	frontend/src/i18n/locales/vi.json
#	frontend/src/i18n/locales/zh-CN.json
#	frontend/src/i18n/locales/zh-TW.json
#	frontend/src/test/engineSelectToast.test.js
#	frontend/src/utils/engineSelectToast.js
@debpalash
debpalash merged commit 602ea6f into main Jul 29, 2026
22 of 23 checks passed
@debpalash
debpalash deleted the fix/preview-macos-updater-asset-collision branch July 29, 2026 08:16
debpalash added a commit that referenced this pull request Jul 29, 2026
… code (#1290)

* ci(windows): make the ffmpeg retry test the outcome, not choco's exit code

The chocolatey feed 503'd; choco printed "Unable to find package 'ffmpeg'"
and "installed 0/0 packages" — then exited 0. The retry loop added on
2026-07-20 for this exact class was `choco install ... && break`, so it broke
out on attempt 1, no backoff ran, and the job died one line later on
`ffmpeg: command not found`. It took #1281 red on an unrelated change.

A retry that trusts a lying exit code is not a retry. The loop now exits on
`command -v ffmpeg` and still fails the job loudly when ffmpeg never arrives.

Tests extract the real step body from ci.yml and run it against a stubbed
choco; 2 of the 4 fail against the previous loop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(ci): pin PATH to the stub dir so the retry test can't false-green

The harness inherited the ambient PATH, so a real ffmpeg satisfied
`command -v` and the loop exited on attempt 1 — every assertion passed
against a broken workflow. It happened twice: /opt/homebrew/bin locally, then
/usr/bin on the Linux runner, which is what took this PR red.

PATH is now the stub dir alone, with the few real tools the stubs need
symlinked in, and stub shebangs are absolute (`/usr/bin/env bash` cannot
resolve bash when PATH is one directory). test_harness_actually_hides_ffmpeg
asserts the sandbox is a sandbox, so the next leak fails loudly instead of
quietly passing.

2 of 5 fail against the old `&& break` loop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ci: give the Windows smoke leg its own timeout, per-leg not shared

Smoke (Windows) has been dying at 10m08s inside `uv sync`, and the shared
10-minute budget made it self-perpetuating: the leg is killed before the
post-step saves the uv cache, so the next run starts cold and dies the same
way. Nothing primes the cache, so it never gets faster.

Measured on run 30385710466 — Linux 65s, macOS 65s, Windows still installing
torch when the job was killed. Windows now gets 25 minutes, priced for one
cold install to finish and populate the cache; warm runs land nowhere near it.

Per-leg rather than raising the shared value, so a genuine hang on Linux or
macOS still fails fast instead of inheriting Windows' allowance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ci(windows): skip the backoff after the final attempt; tighten the tests

CodeRabbit, both valid:

- The loop announced "retrying in 90s" and slept after attempt 3, though no
  fourth attempt exists — 90s added to an already-doomed job.
- The retry tests asserted `attempts >= N`, so a regression that kept going
  after ffmpeg appeared would still pass. Pinned to exact counts, plus a case
  asserting the final attempt announces no retry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <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.

1 participant