From c05ab6783f1846c0ab8f2f6b3bb5c5086f0c017e Mon Sep 17 00:00:00 2001 From: badcuban <108198679+badcuban@users.noreply.github.com> Date: Fri, 7 Aug 2026 13:53:16 -0400 Subject: [PATCH] Give release artifacts OS-scoped names and a Windows portable zip Threadlines-0.3.1-x64.zip was the macOS updater archive, and Windows users browsing the releases page grabbed it by mistake; every artifact now carries its platform in the name. Windows nsis builds also produce a portable zip alongside the installer (the mac dmg+zip pairing, mirrored), which doubles as the artifact fresh-install audits can run without touching an installed copy. The Windows upload glob learned about zips, which it would otherwise have silently dropped; the release smoke asserts the new config; the marketing download classifier is pinned by its first test (new vitest wiring for apps/marketing). --- .github/workflows/release.yml | 2 +- apps/marketing/package.json | 6 ++- apps/marketing/src/lib/releases.test.ts | 40 ++++++++++++++ docs/release.md | 27 ++++++---- pnpm-lock.yaml | 38 ++++++------- scripts/build-desktop-artifact.test.ts | 38 +++++++++++++ scripts/build-desktop-artifact.ts | 9 +++- scripts/merge-update-manifests.test.ts | 72 ++++++++++++------------- scripts/release-smoke.ts | 60 ++++++++++++++------- 9 files changed, 198 insertions(+), 94 deletions(-) create mode 100644 apps/marketing/src/lib/releases.test.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7134323c3..8396f80e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -560,7 +560,7 @@ jobs: run: | set -euo pipefail shopt -s nullglob - assets=(release/*.exe release/*.blockmap release/*.yml) + assets=(release/*.exe release/*.zip release/*.blockmap release/*.yml) if (( ${#assets[@]} == 0 )); then echo "No Windows release assets found." >&2 exit 1 diff --git a/apps/marketing/package.json b/apps/marketing/package.json index 5ec2018f0..6b765d622 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -7,6 +7,7 @@ "dev": "astro dev", "build": "astro build", "preview": "astro preview", + "test": "vp test run", "typecheck": "astro check" }, "dependencies": { @@ -15,6 +16,9 @@ }, "devDependencies": { "@astrojs/check": "^0.9.10", - "typescript": "~5.9.3" + "typescript": "~5.9.3", + "vite": "catalog:", + "vite-plus": "catalog:", + "vitest": "catalog:" } } diff --git a/apps/marketing/src/lib/releases.test.ts b/apps/marketing/src/lib/releases.test.ts new file mode 100644 index 000000000..b44275e42 --- /dev/null +++ b/apps/marketing/src/lib/releases.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from "vitest"; + +import { classifyAssets, type ReleaseAsset } from "./releases"; + +function asset(name: string): ReleaseAsset { + return { name, browser_download_url: `https://example.test/${name}`, size: 1 }; +} + +describe("classifyAssets", () => { + it("picks one installer per platform from the published release assets", () => { + const installers = classifyAssets([ + asset("Threadlines-0.4.0-win-x64.exe"), + asset("Threadlines-0.4.0-win-x64.exe.blockmap"), + asset("Threadlines-0.4.0-win-x64.zip"), + asset("Threadlines-0.4.0-win-arm64.exe"), + asset("Threadlines-0.4.0-mac-arm64.dmg"), + asset("Threadlines-0.4.0-mac-arm64.zip"), + asset("Threadlines-0.4.0-mac-x64.dmg"), + asset("Threadlines-0.4.0-mac-x64.zip"), + asset("Threadlines-0.4.0-linux-x86_64.AppImage"), + asset("latest.yml"), + asset("latest-mac.yml"), + ]); + + expect(installers.winX64?.name).toBe("Threadlines-0.4.0-win-x64.exe"); + expect(installers.winArm?.name).toBe("Threadlines-0.4.0-win-arm64.exe"); + expect(installers.macArm?.name).toBe("Threadlines-0.4.0-mac-arm64.dmg"); + expect(installers.macX64?.name).toBe("Threadlines-0.4.0-mac-x64.dmg"); + expect(installers.linuxX64?.name).toBe("Threadlines-0.4.0-linux-x86_64.AppImage"); + }); + + it("ignores updater and portable archives that share an installer's arch", () => { + const installers = classifyAssets([ + asset("Threadlines-0.4.0-mac-arm64.zip"), + asset("Threadlines-0.4.0-win-x64.zip"), + ]); + + expect(installers).toEqual({}); + }); +}); diff --git a/docs/release.md b/docs/release.md index 474baf4e3..e56e1fbc3 100644 --- a/docs/release.md +++ b/docs/release.md @@ -192,13 +192,14 @@ complete compact technical change list in a collapsed details section. The release assets should include: -- `Threadlines--x64.exe` -- `Threadlines--x64.exe.blockmap` -- `Threadlines--arm64.dmg` -- `Threadlines--arm64.zip` -- `Threadlines--x64.dmg` -- `Threadlines--x64.zip` -- `Threadlines--x86_64.AppImage` +- `Threadlines--win-x64.exe` +- `Threadlines--win-x64.exe.blockmap` +- `Threadlines--win-x64.zip` +- `Threadlines--mac-arm64.dmg` +- `Threadlines--mac-arm64.zip` +- `Threadlines--mac-x64.dmg` +- `Threadlines--mac-x64.zip` +- `Threadlines--linux-x86_64.AppImage` - `latest.yml` - `latest-mac.yml` - `latest-linux.yml` @@ -214,10 +215,14 @@ prereleases so updater checks can read the prerelease manifest. Download the matching asset from GitHub Releases: -- Windows: `Threadlines--x64.exe` -- Apple Silicon macOS: `Threadlines--arm64.dmg` -- Intel macOS: `Threadlines--x64.dmg` -- Linux x64: `Threadlines--x86_64.AppImage` +- Windows: `Threadlines--win-x64.exe` (or + `Threadlines--win-x64.zip` for a portable copy that does not install) +- Apple Silicon macOS: `Threadlines--mac-arm64.dmg` +- Intel macOS: `Threadlines--mac-x64.dmg` +- Linux x64: `Threadlines--linux-x86_64.AppImage` + +Every asset name carries its operating system, so a macOS updater zip can no +longer be mistaken for a Windows download. Windows and macOS public release artifacts are expected to be signed. Windows may still show SmartScreen reputation prompts until the signing identity has enough diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76878913f..1292e2686 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,6 +173,15 @@ importers: typescript: specifier: ~5.9.3 version: 5.9.3 + vite: + specifier: npm:@voidzero-dev/vite-plus-core@0.2.4 + version: '@voidzero-dev/vite-plus-core@0.2.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(typescript@5.9.3)(yaml@2.9.0)' + vite-plus: + specifier: 'catalog:' + version: 0.2.4(@types/node@24.13.3)(@vitest/browser-playwright@4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(typescript@5.9.3)(yaml@2.9.0))(msw@2.15.0(@types/node@24.13.3)(typescript@5.9.3))(playwright@1.62.1)(vitest@4.1.10))(esbuild@0.28.1)(jiti@2.7.0)(msw@2.15.0(@types/node@24.13.3)(typescript@5.9.3))(typescript@5.9.3)(yaml@2.9.0) + vitest: + specifier: 4.1.10 + version: 4.1.10(@types/node@24.13.3)(@vitest/browser-playwright@4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(typescript@5.9.3)(yaml@2.9.0))(msw@2.15.0(@types/node@24.13.3)(typescript@5.9.3))(playwright@1.62.1)(vitest@4.1.10))(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(typescript@5.9.3)(yaml@2.9.0))(msw@2.15.0(@types/node@24.13.3)(typescript@5.9.3)) apps/relay-worker: dependencies: @@ -4003,9 +4012,6 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-module-lexer@2.3.1: resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} @@ -5006,10 +5012,6 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - obug@2.1.3: - resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} - engines: {node: '>=12.20.0'} - obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} @@ -5675,10 +5677,6 @@ packages: resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} engines: {node: ^16.14.0 || >= 17.3.0} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} - engines: {node: '>=18'} - tinyexec@1.3.0: resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} @@ -9469,8 +9467,6 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.1.0: {} - es-module-lexer@2.3.1: {} es-object-atoms@1.1.2: @@ -10793,8 +10789,6 @@ snapshots: object-keys@1.1.1: optional: true - obug@2.1.3: {} - obug@2.1.4: {} ofetch@1.5.1: @@ -11659,8 +11653,6 @@ snapshots: tinyclip@0.1.15: {} - tinyexec@1.2.4: {} - tinyexec@1.3.0: {} tinyglobby@0.2.17: @@ -12021,15 +12013,15 @@ snapshots: '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 - es-module-lexer: 2.1.0 + es-module-lexer: 2.3.1 expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.3 + obug: 2.1.4 pathe: 2.0.3 picomatch: 4.0.5 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.2.4 + tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 vite: '@voidzero-dev/vite-plus-core@0.2.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(typescript@5.9.3)(yaml@2.9.0)' @@ -12050,15 +12042,15 @@ snapshots: '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 - es-module-lexer: 2.1.0 + es-module-lexer: 2.3.1 expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.3 + obug: 2.1.4 pathe: 2.0.3 picomatch: 4.0.5 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.2.4 + tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 vite: '@voidzero-dev/vite-plus-core@0.2.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(typescript@7.0.2)(yaml@2.9.0)' diff --git a/scripts/build-desktop-artifact.test.ts b/scripts/build-desktop-artifact.test.ts index 109195ae3..b956562fa 100644 --- a/scripts/build-desktop-artifact.test.ts +++ b/scripts/build-desktop-artifact.test.ts @@ -114,6 +114,44 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { }), ); + it.effect("names artifacts with an operating system token on every platform", () => + Effect.gen(function* () { + for (const [platform, target] of [ + ["mac", "dmg"], + ["win", "nsis"], + ["linux", "AppImage"], + ] as const) { + const buildConfig = yield* createBuildConfig( + platform, + target, + "0.0.19", + false, + false, + undefined, + ); + + assert.equal(buildConfig.artifactName, "Threadlines-${version}-${os}-${arch}.${ext}"); + } + }), + ); + + it.effect("pairs the default Windows installer with a portable zip", () => + Effect.gen(function* () { + const nsisConfig = yield* createBuildConfig("win", "nsis", "0.0.2", false, false, undefined); + assert.deepStrictEqual((nsisConfig.win as Record).target, ["nsis", "zip"]); + + const portableConfig = yield* createBuildConfig( + "win", + "portable", + "0.0.2", + false, + false, + undefined, + ); + assert.deepStrictEqual((portableConfig.win as Record).target, ["portable"]); + }), + ); + it.effect("keeps Windows executable resource editing enabled for unsigned builds", () => Effect.gen(function* () { const buildConfig = yield* createBuildConfig("win", "nsis", "0.0.2", false, false, undefined); diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts index f58a64553..d3e4fc603 100644 --- a/scripts/build-desktop-artifact.ts +++ b/scripts/build-desktop-artifact.ts @@ -823,7 +823,10 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( const buildConfig: Record = { appId: DESKTOP_RELEASE_APP_ID, productName: resolveDesktopProductName(version), - artifactName: "Threadlines-${version}-${arch}.${ext}", + // The ${os} token (mac/win/linux) keeps downloads self-describing on the + // GitHub releases page: without it a macOS updater zip reads as + // "Threadlines-0.3.1-x64.zip" and Windows users grab it by mistake. + artifactName: "Threadlines-${version}-${os}-${arch}.${ext}", directories: { buildResources: "apps/desktop/resources", }, @@ -903,7 +906,9 @@ export const createBuildConfig = Effect.fn("createBuildConfig")(function* ( }, ]; const winConfig: Record = { - target: [target], + // The zip is the portable, no-install Windows build. Pair it with the + // default nsis installer only; an explicitly requested target stays exact. + target: target === "nsis" ? [target, "zip"] : [target], icon: "icon.ico", }; if (signed) { diff --git a/scripts/merge-update-manifests.test.ts b/scripts/merge-update-manifests.test.ts index 32431b81d..f38efa4d6 100644 --- a/scripts/merge-update-manifests.test.ts +++ b/scripts/merge-update-manifests.test.ts @@ -20,13 +20,13 @@ describe("merge-update-manifests", () => { "mac", `version: 0.0.4 files: - - url: T3-Code-0.0.4-arm64.zip + - url: Threadlines-0.0.4-mac-arm64.zip sha512: arm64zip size: 125621344 - - url: T3-Code-0.0.4-arm64.dmg + - url: Threadlines-0.0.4-mac-arm64.dmg sha512: arm64dmg size: 131754935 -path: T3-Code-0.0.4-arm64.zip +path: Threadlines-0.0.4-mac-arm64.zip sha512: arm64zip releaseDate: '2026-03-07T10:32:14.587Z' `, @@ -37,13 +37,13 @@ releaseDate: '2026-03-07T10:32:14.587Z' "mac", `version: 0.0.4 files: - - url: T3-Code-0.0.4-x64.zip + - url: Threadlines-0.0.4-mac-x64.zip sha512: x64zip size: 132000112 - - url: T3-Code-0.0.4-x64.dmg + - url: Threadlines-0.0.4-mac-x64.dmg sha512: x64dmg size: 138148807 -path: T3-Code-0.0.4-x64.zip +path: Threadlines-0.0.4-mac-x64.zip sha512: x64zip releaseDate: '2026-03-07T10:36:07.540Z' `, @@ -57,10 +57,10 @@ releaseDate: '2026-03-07T10:36:07.540Z' assert.deepStrictEqual( merged.files.map((file) => file.url), [ - "T3-Code-0.0.4-arm64.zip", - "T3-Code-0.0.4-arm64.dmg", - "T3-Code-0.0.4-x64.zip", - "T3-Code-0.0.4-x64.dmg", + "Threadlines-0.0.4-mac-arm64.zip", + "Threadlines-0.0.4-mac-arm64.dmg", + "Threadlines-0.0.4-mac-x64.zip", + "Threadlines-0.0.4-mac-x64.dmg", ], ); @@ -74,13 +74,13 @@ releaseDate: '2026-03-07T10:36:07.540Z' "win", `version: 0.0.4 files: - - url: T3-Code-0.0.4-arm64.exe + - url: Threadlines-0.0.4-win-arm64.exe sha512: arm64exe size: 125621344 - - url: T3-Code-0.0.4-arm64.exe.blockmap + - url: Threadlines-0.0.4-win-arm64.exe.blockmap sha512: arm64blockmap size: 131754 -path: T3-Code-0.0.4-arm64.exe +path: Threadlines-0.0.4-win-arm64.exe sha512: arm64exe releaseDate: '2026-03-07T10:32:14.587Z' `, @@ -91,13 +91,13 @@ releaseDate: '2026-03-07T10:32:14.587Z' "win", `version: 0.0.4 files: - - url: T3-Code-0.0.4-x64.exe + - url: Threadlines-0.0.4-win-x64.exe sha512: x64exe size: 132000112 - - url: T3-Code-0.0.4-x64.exe.blockmap + - url: Threadlines-0.0.4-win-x64.exe.blockmap sha512: x64blockmap size: 138148 -path: T3-Code-0.0.4-x64.exe +path: Threadlines-0.0.4-win-x64.exe sha512: x64exe releaseDate: '2026-03-07T10:36:07.540Z' `, @@ -111,10 +111,10 @@ releaseDate: '2026-03-07T10:36:07.540Z' assert.deepStrictEqual( merged.files.map((file) => file.url), [ - "T3-Code-0.0.4-arm64.exe", - "T3-Code-0.0.4-arm64.exe.blockmap", - "T3-Code-0.0.4-x64.exe", - "T3-Code-0.0.4-x64.exe.blockmap", + "Threadlines-0.0.4-win-arm64.exe", + "Threadlines-0.0.4-win-arm64.exe.blockmap", + "Threadlines-0.0.4-win-x64.exe", + "Threadlines-0.0.4-win-x64.exe.blockmap", ], ); @@ -128,7 +128,7 @@ releaseDate: '2026-03-07T10:36:07.540Z' "win", `version: 0.0.4 files: - - url: T3-Code-0.0.4-arm64.exe + - url: Threadlines-0.0.4-win-arm64.exe sha512: arm64exe size: 1 releaseDate: '2026-03-07T10:32:14.587Z' @@ -140,7 +140,7 @@ releaseDate: '2026-03-07T10:32:14.587Z' "win", `version: 0.0.5 files: - - url: T3-Code-0.0.5-x64.exe + - url: Threadlines-0.0.5-win-x64.exe sha512: x64exe size: 1 releaseDate: '2026-03-07T10:36:07.540Z' @@ -159,7 +159,7 @@ releaseDate: '2026-03-07T10:36:07.540Z' "mac", `version: '1.0' files: - - url: T3-Code-1.0-x64.zip + - url: Threadlines-1.0-mac-x64.zip sha512: zipsha size: 1 releaseName: 'true' @@ -181,7 +181,7 @@ releaseDate: '2026-03-07T10:36:07.540Z' "win", `version: '1.0' files: - - url: T3-Code-1.0-x64.exe + - url: Threadlines-1.0-win-x64.exe sha512: exesha size: 1 releaseDate: '2026-03-07T10:36:07.540Z' @@ -200,26 +200,26 @@ releaseDate: '2026-03-07T10:36:07.540Z' it.layer(NodeServices.layer)("merge-update-manifests cli", (it) => { const arm64MacManifest = `version: 0.0.4 files: - - url: T3-Code-0.0.4-arm64.zip + - url: Threadlines-0.0.4-mac-arm64.zip sha512: arm64zip size: 125621344 - - url: T3-Code-0.0.4-arm64.dmg + - url: Threadlines-0.0.4-mac-arm64.dmg sha512: arm64dmg size: 131754935 -path: T3-Code-0.0.4-arm64.zip +path: Threadlines-0.0.4-mac-arm64.zip sha512: arm64zip releaseDate: '2026-03-07T10:32:14.587Z' `; const x64MacManifest = `version: 0.0.4 files: - - url: T3-Code-0.0.4-x64.zip + - url: Threadlines-0.0.4-mac-x64.zip sha512: x64zip size: 132000112 - - url: T3-Code-0.0.4-x64.dmg + - url: Threadlines-0.0.4-mac-x64.dmg sha512: x64dmg size: 138148807 -path: T3-Code-0.0.4-x64.zip +path: Threadlines-0.0.4-mac-x64.zip sha512: x64zip releaseDate: '2026-03-07T10:36:07.540Z' `; @@ -240,8 +240,8 @@ releaseDate: '2026-03-07T10:36:07.540Z' yield* runCli(["--platform", "mac", primaryPath, secondaryPath]); const merged = yield* fs.readFileString(primaryPath); - assert.ok(merged.includes("T3-Code-0.0.4-arm64.zip")); - assert.ok(merged.includes("T3-Code-0.0.4-x64.zip")); + assert.ok(merged.includes("Threadlines-0.0.4-mac-arm64.zip")); + assert.ok(merged.includes("Threadlines-0.0.4-mac-x64.zip")); assert.ok(!merged.includes("path:")); }), ); @@ -261,7 +261,7 @@ releaseDate: '2026-03-07T10:36:07.540Z' primaryPath, `version: 0.0.4 files: - - url: T3-Code-0.0.4-arm64.exe + - url: Threadlines-0.0.4-win-arm64.exe sha512: arm64exe size: 125621344 releaseDate: '2026-03-07T10:32:14.587Z' @@ -271,7 +271,7 @@ releaseDate: '2026-03-07T10:32:14.587Z' secondaryPath, `version: 0.0.4 files: - - url: T3-Code-0.0.4-x64.exe + - url: Threadlines-0.0.4-win-x64.exe sha512: x64exe size: 132000112 releaseDate: '2026-03-07T10:36:07.540Z' @@ -281,8 +281,8 @@ releaseDate: '2026-03-07T10:36:07.540Z' yield* runCli(["--platform", "win", primaryPath, secondaryPath, outputPath]); const merged = yield* fs.readFileString(outputPath); - assert.ok(merged.includes("T3-Code-0.0.4-arm64.exe")); - assert.ok(merged.includes("T3-Code-0.0.4-x64.exe")); + assert.ok(merged.includes("Threadlines-0.0.4-win-arm64.exe")); + assert.ok(merged.includes("Threadlines-0.0.4-win-x64.exe")); }), ); diff --git a/scripts/release-smoke.ts b/scripts/release-smoke.ts index 4bc5f2a04..e4d42b1a3 100644 --- a/scripts/release-smoke.ts +++ b/scripts/release-smoke.ts @@ -73,13 +73,13 @@ function writeMacManifestFixtures(targetRoot: string): { arm64Path: string; x64P arm64Path, `version: 9.9.9-smoke.0 files: - - url: Threadlines-9.9.9-smoke.0-arm64.zip + - url: Threadlines-9.9.9-smoke.0-mac-arm64.zip sha512: arm64zip size: 125621344 - - url: Threadlines-9.9.9-smoke.0-arm64.dmg + - url: Threadlines-9.9.9-smoke.0-mac-arm64.dmg sha512: arm64dmg size: 131754935 -path: Threadlines-9.9.9-smoke.0-arm64.zip +path: Threadlines-9.9.9-smoke.0-mac-arm64.zip sha512: arm64zip releaseDate: '2026-03-08T10:32:14.587Z' `, @@ -89,13 +89,13 @@ releaseDate: '2026-03-08T10:32:14.587Z' x64Path, `version: 9.9.9-smoke.0 files: - - url: Threadlines-9.9.9-smoke.0-x64.zip + - url: Threadlines-9.9.9-smoke.0-mac-x64.zip sha512: x64zip size: 132000112 - - url: Threadlines-9.9.9-smoke.0-x64.dmg + - url: Threadlines-9.9.9-smoke.0-mac-x64.dmg sha512: x64dmg size: 138148807 -path: Threadlines-9.9.9-smoke.0-x64.zip +path: Threadlines-9.9.9-smoke.0-mac-x64.zip sha512: x64zip releaseDate: '2026-03-08T10:36:07.540Z' `, @@ -118,13 +118,13 @@ function writeWindowsManifestFixtures( arm64Path, `version: 9.9.9-smoke.0 files: - - url: Threadlines-9.9.9-smoke.0-arm64.exe + - url: Threadlines-9.9.9-smoke.0-win-arm64.exe sha512: arm64exe size: 126621344 - - url: Threadlines-9.9.9-smoke.0-arm64.exe.blockmap + - url: Threadlines-9.9.9-smoke.0-win-arm64.exe.blockmap sha512: arm64blockmap size: 152344 -path: Threadlines-9.9.9-smoke.0-arm64.exe +path: Threadlines-9.9.9-smoke.0-win-arm64.exe sha512: arm64exe releaseDate: '2026-03-08T10:32:14.587Z' `, @@ -134,13 +134,13 @@ releaseDate: '2026-03-08T10:32:14.587Z' x64Path, `version: 9.9.9-smoke.0 files: - - url: Threadlines-9.9.9-smoke.0-x64.exe + - url: Threadlines-9.9.9-smoke.0-win-x64.exe sha512: x64exe size: 132000112 - - url: Threadlines-9.9.9-smoke.0-x64.exe.blockmap + - url: Threadlines-9.9.9-smoke.0-win-x64.exe.blockmap sha512: x64blockmap size: 160112 -path: Threadlines-9.9.9-smoke.0-x64.exe +path: Threadlines-9.9.9-smoke.0-win-x64.exe sha512: x64exe releaseDate: '2026-03-08T10:36:07.540Z' `, @@ -257,6 +257,16 @@ async function assertReleaseBuildIdentity(): Promise { DESKTOP_RELEASE_APP_ID, "macOS release app id changed; Squirrel.Mac updates require this to remain stable.", ); + assertEqual( + macBuildConfig.artifactName, + "Threadlines-${version}-${os}-${arch}.${ext}", + "Release artifacts must name their operating system so downloads are unambiguous.", + ); + assertEqual( + JSON.stringify(macConfig.target), + JSON.stringify(["dmg", "zip"]), + "macOS release builds must emit the dmg plus the Squirrel.Mac updater zip.", + ); assertEqual( macConfig.hardenedRuntime, true, @@ -292,6 +302,16 @@ async function assertReleaseBuildIdentity(): Promise { DESKTOP_RELEASE_APP_ID, "Windows release app id changed; NSIS upgrades require this to remain stable.", ); + assertEqual( + windowsBuildConfig.artifactName, + "Threadlines-${version}-${os}-${arch}.${ext}", + "Release artifacts must name their operating system so downloads are unambiguous.", + ); + assertEqual( + JSON.stringify(windowsConfig.target), + JSON.stringify(["nsis", "zip"]), + "Windows release builds must emit the NSIS installer plus the portable zip.", + ); assertEqual( typeof windowsConfig.azureSignOptions, "object", @@ -422,12 +442,12 @@ try { const mergedManifest = readFileSync(arm64Path, "utf8"); assertContains( mergedManifest, - "Threadlines-9.9.9-smoke.0-arm64.zip", + "Threadlines-9.9.9-smoke.0-mac-arm64.zip", "Merged manifest is missing the arm64 asset.", ); assertContains( mergedManifest, - "Threadlines-9.9.9-smoke.0-x64.zip", + "Threadlines-9.9.9-smoke.0-mac-x64.zip", "Merged manifest is missing the x64 asset.", ); @@ -449,34 +469,34 @@ try { const mergedWindowsManifest = readFileSync(mergedWindowsManifestPath, "utf8"); assertContains( mergedWindowsManifest, - "Threadlines-9.9.9-smoke.0-arm64.exe", + "Threadlines-9.9.9-smoke.0-win-arm64.exe", "Merged Windows manifest is missing the arm64 asset.", ); assertContains( mergedWindowsManifest, - "Threadlines-9.9.9-smoke.0-x64.exe", + "Threadlines-9.9.9-smoke.0-win-x64.exe", "Merged Windows manifest is missing the x64 asset.", ); const mergedNightlyWindowsManifest = readFileSync(mergedNightlyWindowsManifestPath, "utf8"); assertContains( mergedNightlyWindowsManifest, - "Threadlines-9.9.9-smoke.0-arm64.exe", + "Threadlines-9.9.9-smoke.0-win-arm64.exe", "Merged nightly Windows manifest is missing the arm64 asset.", ); assertContains( mergedNightlyWindowsManifest, - "Threadlines-9.9.9-smoke.0-x64.exe", + "Threadlines-9.9.9-smoke.0-win-x64.exe", "Merged nightly Windows manifest is missing the x64 asset.", ); const mergedPreviewWindowsManifest = readFileSync(mergedPreviewWindowsManifestPath, "utf8"); assertContains( mergedPreviewWindowsManifest, - "Threadlines-9.9.9-smoke.0-arm64.exe", + "Threadlines-9.9.9-smoke.0-win-arm64.exe", "Merged preview Windows manifest is missing the arm64 asset.", ); assertContains( mergedPreviewWindowsManifest, - "Threadlines-9.9.9-smoke.0-x64.exe", + "Threadlines-9.9.9-smoke.0-win-x64.exe", "Merged preview Windows manifest is missing the x64 asset.", ); assertMissing(