Skip to content

0.1.5

Choose a tag to compare

@github-actions github-actions released this 20 Apr 16:27
bc15f39
updates-122: clips desktop auto-updater + download page (#233)

* updates-122: clips desktop auto-updater + download page + release workflow

Initial drop from concurrent agents — auto-updater, download landing
page, desktop release GitHub Actions workflow.

* fix(clips): address all 6 Builder review blockers for auto-updater + download page

1. tauri.conf.json: updater endpoint points at the immutable
   /releases/download/clips-latest/clips-latest.json URL instead of
   /releases/latest/download/ which resolved to whichever GitHub tag
   happened to be marked "latest".
2. clips-desktop-release.yml: add a pre-build guard that fails the
   workflow if the updater pubkey is still the placeholder, preventing
   accidental release with a non-verifying signature.
3. download.tsx: when the manifest fetch fails or has no matching
   asset, the primary button now falls back to the public Releases
   PAGE (a browsable URL) instead of the JSON manifest itself. Button
   is explicitly disabled during the loading state.
4. download.tsx: Mac arch detection now uses
   navigator.userAgentData.architecture (Chromium: "arm" | "x86")
   as the canonical signal. Safari, which lacks userAgentData, still
   defaults to Apple Silicon since it's the overwhelming majority on
   currently-shipping Macs.
5. clips-latest.json.get.ts (new): same-origin server proxy for the
   updater manifest. GitHub release-asset responses don't include CORS
   headers, so a direct browser fetch fails silently. The download
   page now hits /api/clips-latest.json which server-side fetches
   GitHub and returns the JSON with correct headers.
6. UpdateBanner.tsx: surface the error state with a Retry button
   (wires to new retryUpdateCheck() export) and a Dismiss option.
   Signature-verification, download, and network failures no longer
   fail silently.

* fix(clips): 5 follow-ups from Builder incremental review

1. UpdateBanner error-dismiss: scope dismissal to the specific error
   message so a new, different failure still surfaces. Previously
   dismissing one error silenced every subsequent error for the rest of
   the session.
2. styles.css: add missing `.update-banner--error` styling (red-tinted
   surface with 10% background + 40% border).
3. /api/clips-latest.json: add 10s AbortSignal.timeout on the upstream
   fetch so a slow GitHub can't hang the request indefinitely. Maps
   TimeoutError to a distinct 502 status message.
4. download.tsx fallback URL: point at
   /releases?q=clips-v (the versioned-release listing that actually
   holds installers) instead of the clips-latest tag page (manifest
   only, no DMG/MSI/AppImage).
5. download.tsx arch detection: use async
   userAgentData.getHighEntropyValues(["architecture"]). The sync
   property is an empty string until the high-entropy call resolves.
   Keeps a sync Apple-Silicon default, then corrects to Intel when
   Chromium confirms it.

* fix(clips): download page now sources installers from releases API, not updater manifest

Addresses 3 architectural issues from Builder's final review pass.

Root cause: `/download` was parsing the Tauri updater manifest for
installer URLs, but that manifest lists updater ARCHIVES
(`.app.tar.gz`, `.msi.zip`, `.AppImage.tar.gz`) used for in-app
patching — not the raw installers (.dmg / .msi / .exe) users want on
first install. Every platform fell back to the Releases page.

Fix:

- Rewrite `/api/clips-latest.json` to hit GitHub's releases API,
  pick the most recent published `clips-v*` release, and return its
  asset list with each asset classified by kind (mac-universal /
  mac-arm64 / mac-x64 / windows-msi / windows-exe / linux-*).
  Explicitly skips .sig and updater-archive assets (`unknown` kind).
- Rewrite `/download` to match by classified kind instead of filename
  regex. Collapses the two Mac variants into a single "macOS
  Universal (Apple Silicon + Intel)" card, since the workflow builds
  `universal-apple-darwin` which produces one DMG that runs on both.
  Drops the Linux variant — `tauri.conf.json` only lists
  `["dmg", "msi"]` bundle targets, so no AppImage is produced.
- Drop the Ubuntu matrix job + Linux deps step from the release
  workflow to match: no AppImage bundle → no point building one.

Arch-split detection (`refinePlatform`, `userAgentData`) is gone
since macOS is now a single variant.

* fix(clips): cache + paginate /api/clips-latest.json

Two operability fixes on the GitHub-releases proxy from Builder's
final review:

- 5-minute process-wide memoization (+ request-coalescing via an
  in-flight promise) so a burst of hits to /download share one
  upstream fetch. GitHub's unauthenticated REST API is 60 req/hr/IP,
  so the naive every-request fetch would 429 under modest traffic.
- Stale-while-error fallback — if GitHub errors out AND we have a
  prior successful payload, serve that instead of bubbling the
  error. Prevents a transient upstream hiccup from taking the
  download page offline.
- HTTP `cache-control: max-age=60` stays on the response so
  downstream caches + the browser also cache aggressively. The
  client-side `fetch` drops its `cache: "no-cache"` which was
  bypassing both layers.
- Paginate through `/releases` up to 10 pages (1000 releases)
  looking for the latest `clips-v*`. Previously capped at the
  first 50; as the repo accumulates other releases the `clips-v*`
  entry would eventually fall off page 1 and the endpoint would
  return a false 404.

* fix(clips): scan all release pages + preserve upstream status codes

Two follow-ups from Builder's review on d7a9502a:

- findLatestClipsRelease no longer early-exits on first match. Walks
  every page (bounded by MAX_PAGES) and tracks the `published_at`
  maximum. Previously depended on GitHub's implicit created_at-desc
  sort, which can diverge from published_at (e.g. a draft published
  later than a later-created draft). Same worst-case page count, no
  ordering assumption.
- Introduce UpstreamError carrying the GitHub HTTP status. fetchPage
  throws that on non-OK responses; the route handler now propagates
  the original status (429 stays 429, 503 stays 503) instead of
  flattening everything to 502. Rate limits + service-unavailable
  failures now show up correctly to monitors.