From 864991fe3fdca280c9dd695d95a3eef478a50c5c Mon Sep 17 00:00:00 2001 From: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com> Date: Thu, 13 Aug 2026 20:01:00 +0530 Subject: [PATCH] fix(release): verify all desktop assets --- RELEASING.md | 2 +- app/DISTRIBUTION.md | 6 ++-- app/scripts/verify-windows-installer.mjs | 2 ++ app/scripts/verify-windows-installer.test.ts | 31 ++++++++++++++++++-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index ab14983c..83f739a2 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,7 +4,7 @@ This document describes the actual steps a maintainer takes to cut a CLI or macO The Electron desktop app (`app/`) is released manually under `desktop-v` tags. Build macOS and Linux artifacts as described in `app/DISTRIBUTION.md`; the tag also runs the read-only `Build Windows installer` workflow on `windows-latest`. Download its `CodeBurn-Windows-Installer` artifact and upload both the `.exe` and `.exe.blockmap` with the other platform assets. The workflow never publishes release assets. -Before announcing a desktop release, the release owner must confirm the live GitHub Release contains all four macOS `.dmg`/`.zip` files, the Linux `.AppImage`, and both Windows installer files. Publishing the Release runs the workflow's read-only live-asset verification job. If assets are uploaded after publication, rerun `Build Windows installer` with the `release_tag` input and require that verification job to pass. A failed or missing verification is a release blocker. +Before announcing a desktop release, the release owner must confirm the live GitHub Release contains all four macOS `.dmg`/`.zip` files, the Linux `.AppImage`, `.deb`, and `.rpm`, and both Windows installer files. Publishing the Release runs the workflow's read-only live-asset verification job. If assets are uploaded after publication, rerun `Build Windows installer` with the `release_tag` input and require that verification job to pass. A failed or missing verification is a release blocker. ## Versioning diff --git a/app/DISTRIBUTION.md b/app/DISTRIBUTION.md index 5fedd46d..431c1900 100644 --- a/app/DISTRIBUTION.md +++ b/app/DISTRIBUTION.md @@ -256,8 +256,10 @@ Actions artifact. The workflow has read-only repository permissions and does Before publishing the GitHub Release, the release owner must download that workflow artifact and manually upload both Windows files along with the four -macOS `.dmg`/`.zip` files and `CodeBurn-.AppImage`. Confirm the live -release contains every required platform asset before announcing it. The +macOS `.dmg`/`.zip` files, `CodeBurn-.AppImage`, +`codeburn-desktop__amd64.deb`, and +`codeburn-desktop-.x86_64.rpm`. Confirm the live release contains +every required platform asset before announcing it. The website's download links **pin that tag** in their URLs, so a release with a missing installer is broken even when another Windows distribution channel is available. The Windows installer uses an explicit `nsis.artifactName` of diff --git a/app/scripts/verify-windows-installer.mjs b/app/scripts/verify-windows-installer.mjs index dfd4b18a..84d499e6 100644 --- a/app/scripts/verify-windows-installer.mjs +++ b/app/scripts/verify-windows-installer.mjs @@ -44,6 +44,8 @@ function verifyLiveRelease(tag, assetPath) { `CodeBurn-${version}-arm64-mac.zip`, `CodeBurn-${version}-mac.zip`, `CodeBurn-${version}.AppImage`, + `codeburn-desktop_${version}_amd64.deb`, + `codeburn-desktop-${version}.x86_64.rpm`, `CodeBurn-Setup-${version}.exe`, `CodeBurn-Setup-${version}.exe.blockmap`, ] diff --git a/app/scripts/verify-windows-installer.test.ts b/app/scripts/verify-windows-installer.test.ts index 0c63c4f0..2dd252f8 100644 --- a/app/scripts/verify-windows-installer.test.ts +++ b/app/scripts/verify-windows-installer.test.ts @@ -2,10 +2,12 @@ import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { spawnSync } from 'node:child_process' +import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' import { rootFromModuleUrl } from './windows-installer-paths.mjs' const verifier = new URL('./verify-windows-installer.mjs', import.meta.url) +const verifierPath = fileURLToPath(verifier) function fixture(options: { appVersion?: string @@ -30,7 +32,7 @@ function fixture(options: { writeFileSync(path, 'fixture') } - const args = [verifier.pathname, '--root', root, '--artifacts', releaseDir] + const args = [verifierPath, '--root', root, '--artifacts', releaseDir] if (options.tag) args.push('--tag', options.tag) return spawnSync(process.execPath, args, { encoding: 'utf8' }) } @@ -40,7 +42,7 @@ function releaseFixture(files: string[]) { const assets = join(root, 'assets.json') writeFileSync(assets, JSON.stringify(files)) return spawnSync(process.execPath, [ - verifier.pathname, + verifierPath, '--tag', 'desktop-v1.2.3', '--release-assets', @@ -117,6 +119,8 @@ describe('Windows installer release manifest verifier', () => { 'CodeBurn-1.2.3-arm64-mac.zip', 'CodeBurn-1.2.3-mac.zip', 'CodeBurn-1.2.3.AppImage', + 'codeburn-desktop_1.2.3_amd64.deb', + 'codeburn-desktop-1.2.3.x86_64.rpm', 'CodeBurn-Setup-1.2.3.exe', 'CodeBurn-Setup-1.2.3.exe.blockmap', ]) @@ -132,10 +136,33 @@ describe('Windows installer release manifest verifier', () => { 'CodeBurn-1.2.3-arm64-mac.zip', 'CodeBurn-1.2.3-mac.zip', 'CodeBurn-1.2.3.AppImage', + 'codeburn-desktop_1.2.3_amd64.deb', + 'codeburn-desktop-1.2.3.x86_64.rpm', 'CodeBurn-Setup-1.2.3.exe.blockmap', ]) expect(result.status).toBe(1) expect(result.stderr).toContain('live release is missing CodeBurn-Setup-1.2.3.exe') }) + + it.each([ + 'codeburn-desktop_1.2.3_amd64.deb', + 'codeburn-desktop-1.2.3.x86_64.rpm', + ])('rejects a live desktop release missing %s', missing => { + const required = [ + 'CodeBurn-1.2.3-arm64.dmg', + 'CodeBurn-1.2.3.dmg', + 'CodeBurn-1.2.3-arm64-mac.zip', + 'CodeBurn-1.2.3-mac.zip', + 'CodeBurn-1.2.3.AppImage', + 'codeburn-desktop_1.2.3_amd64.deb', + 'codeburn-desktop-1.2.3.x86_64.rpm', + 'CodeBurn-Setup-1.2.3.exe', + 'CodeBurn-Setup-1.2.3.exe.blockmap', + ] + const result = releaseFixture(required.filter(asset => asset !== missing)) + + expect(result.status).toBe(1) + expect(result.stderr).toContain(`live release is missing ${missing}`) + }) })