Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/linux-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ jobs:
if: github.event_name == 'release'
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
# Carry the release's own flag. The action defaults `prerelease` to
# false and writes it back, so attaching assets silently promoted a
# pre-release to the latest release -- which also fires `released`,
# pushing :latest to GHCR, and makes the in-app updater offer a build
# that was never verified.
prerelease: ${{ github.event.release.prerelease }}
files: |
dist/StemDeck-Linux-x64.tar.gz
dist/StemDeck-Linux-x64.tar.gz.sha256
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/macos-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ jobs:
- name: upload artifacts
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
# Carry the release's own flag. The action defaults `prerelease` to
# false and writes it back, so attaching assets silently promoted a
# pre-release to the latest release -- which also fires `released`,
# pushing :latest to GHCR, and makes the in-app updater offer a build
# that was never verified.
prerelease: ${{ github.event.release.prerelease }}
files: |
.build/macos-dist/StemDeck-macOS-arm64.dmg
.build/StemDeck-runtime-macOS-arm64.tar.zst
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/windows-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ jobs:
- name: upload artifacts
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
# Carry the release's own flag. The action defaults `prerelease` to
# false and writes it back, so attaching assets silently promoted a
# pre-release to the latest release -- which also fires `released`,
# pushing :latest to GHCR, and makes the in-app updater offer a build
# that was never verified.
prerelease: ${{ github.event.release.prerelease }}
files: |
dist/StemDeck-Windows-x64.NVIDIA.zip
dist/StemDeck-Windows-x64.NVIDIA.zip.sha256
Expand Down
12 changes: 11 additions & 1 deletion static/js/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -2513,8 +2513,18 @@ async function checkForUpdate() {
// Newest first, as GitHub returns them. Drafts are invisible to an
// unauthenticated request anyway, but filter them so a maintainer running a
// dev build is not offered a release that has no assets yet.
//
// Pre-releases are skipped as well: a release is published as a pre-release
// first, verified, and only then promoted ("Set as the latest release"), so
// nobody is offered a build that has not been through that. The list
// endpoint is used rather than /releases/latest because it keeps the choice
// here, in code, rather than in GitHub's endpoint semantics. It assumes a
// stable release inside the last 10 -- true unless ten consecutive
// pre-releases go out without one being promoted.
const releases = await res.json();
const data = Array.isArray(releases) ? releases.find((r) => !r.draft) : null;
const data = Array.isArray(releases)
? releases.find((r) => !r.draft && !r.prerelease)
: null;
if (!data) return;
const latest = normalizeVersion(data.tag_name);
// Compare canonically so a PEP440 current version (0.7.0a9) matches the
Expand Down
7 changes: 5 additions & 2 deletions tests/e2e/helpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,12 @@ export async function stubUpdateCheck(page, { available = false } = {}) {
route.fulfill({
status: 200,
contentType: "application/json",
// An ARRAY: the app polls /releases (the list), not /releases/latest,
// so that a version published as a pre-release is still seen.
// An ARRAY: the app polls /releases (the list), not /releases/latest.
// The unpromoted pre-release in front of the stable one is deliberate:
// it must be skipped, because a release is only offered once it has
// been promoted to the latest release.
body: JSON.stringify([
{ tag_name: "v9.9.10", draft: false, prerelease: true, body: "unpromoted", html_url: "https://example.invalid", assets: [] },
{ tag_name: "v9.9.9", draft: false, prerelease: false, body: "notes", html_url: "https://example.invalid", assets: [] },
]),
}));
Expand Down
5 changes: 5 additions & 0 deletions tests/e2e/report-failure.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ test.describe("failure notifications", () => {
await openStudio(page, { tauri: true, updateAvailable: true });
await expect(page.locator("#notifReleaseCard")).not.toHaveClass(/hidden/);
await expect(page.locator("#notifBadge")).not.toHaveClass(/hidden/);
// The newest release in the stub is an unpromoted pre-release. The card must
// name the promoted one behind it: a release is offered only once it has
// been promoted to the latest release, so nobody is updated to a build that
// was never verified.
await expect(page.locator("#notifReleaseDesc")).toHaveText("v9.9.9");

await failAnExport(page);
await openBell(page);
Expand Down
Loading