#!/usr/bin/env node /** * End-to-end desktop release helper. * * 1. Reads the version from src-tauri/tauri.conf.json (you bumped it * and rebuilt before running this). * 2. Uploads every supplied bundle artifact + its `.sig` to the * Azure Blob container `msi` (account `heicodeblob`). Container * is configured public-blob-read, so the URLs are durable. * 3. Generates `website/public/updater/latest.json` pointing at * those URLs and inlining the signatures. * * After this script runs you still need to `git commit && git push` * the website manifest so the static site redeploys. * * Usage (PowerShell example): * * $env:AZURE_STORAGE_CONNECTION_STRING = "" * node scripts/release-desktop.mjs ` * --notes "0.1.1 — bug fixes" ` * --windows-x86_64 src-tauri/target/release/bundle/nsis/heicode_0.1.1_x64-setup.exe * * Tip: the connection string lives in scripts/.release-credentials * locally (gitignored). `source` it before running on Mac/Linux, or * use `Get-Content | ForEach-Object {...}` on Windows. */ import { readFileSync, statSync, mkdirSync, writeFileSync, existsSync, } from 'node:fs' import { dirname, basename, resolve, join } from 'node:path' import { argv, env, exit } from 'node:process' import { execSync, spawnSync } from 'node:child_process' const REPO_ROOT = resolve(import.meta.dirname, '..', '..', '..') const CONTAINER = 'msi' const ACCOUNT = 'heicodeblob' const PUBLIC_HOST = `https://${ACCOUNT}.blob.core.windows.net/${CONTAINER}` const MANIFEST_REL = 'website/public/updater/latest.json' const TAURI_CONF = resolve(import.meta.dirname, '..', 'src-tauri', 'tauri.conf.json') const PLATFORM_KEYS = [ 'windows-x86_64', 'darwin-aarch64', 'darwin-x86_64', 'linux-x86_64', ] function parseArgs(rawArgs) { const out = {} for (let i = 0; i < rawArgs.length; i++) { const k = rawArgs[i] if (!k?.startsWith('--')) continue const next = rawArgs[i + 1] if (!next || next.startsWith('--')) { out[k.slice(2)] = true } else { out[k.slice(2)] = next i++ } } return out } function die(msg, code = 1) { console.error(`error: ${msg}`) exit(code) } function getTauriVersion() { const json = JSON.parse(readFileSync(TAURI_CONF, 'utf8')) if (!json.version) die('tauri.conf.json has no version') return json.version } function uploadBlob(localPath, remoteName) { if (!env.AZURE_STORAGE_CONNECTION_STRING) { die('AZURE_STORAGE_CONNECTION_STRING is not set (see scripts/.release-credentials)') } const result = spawnSync( 'az', [ 'storage', 'blob', 'upload', '--container-name', CONTAINER, '--name', remoteName, '--file', localPath, '--overwrite', '--no-progress', ], { stdio: ['ignore', 'pipe', 'pipe'], env, encoding: 'utf8' }, ) if (result.status !== 0) { console.error(result.stderr) die(`upload failed for ${remoteName}`) } return `${PUBLIC_HOST}/${remoteName}` } const args = parseArgs(argv.slice(2)) const version = getTauriVersion() const versionPrefix = `desktop/${version}` const platforms = {} for (const key of PLATFORM_KEYS) { const path = args[key] if (!path || path === true) continue const local = resolve(path) try { statSync(local) } catch { die(`${key} artifact missing: ${local}`) } const sigLocal = `${local}.sig` let signature try { signature = readFileSync(sigLocal, 'utf8').trim() } catch { die(`signature missing: ${sigLocal} (rebuild with TAURI_SIGNING_PRIVATE_KEY set)`) } const remoteArtifact = `${versionPrefix}/${basename(local)}` const remoteSig = `${versionPrefix}/${basename(sigLocal)}` console.log(`Uploading ${key}: ${remoteArtifact}`) const url = uploadBlob(local, remoteArtifact) uploadBlob(sigLocal, remoteSig) platforms[key] = { signature, url } } if (Object.keys(platforms).length === 0) { die('no platform artifacts supplied — pass at least one -- ') } const manifest = { version, notes: args.notes || `Heicode ${version}`, pub_date: new Date().toISOString(), platforms, } const manifestPath = resolve(REPO_ROOT, MANIFEST_REL) if (!existsSync(dirname(manifestPath))) mkdirSync(dirname(manifestPath), { recursive: true }) writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`) console.log(`\nWrote ${manifestPath}`) console.log(`Version : ${version}`) console.log(`Platforms: ${Object.keys(platforms).join(', ')}`) console.log('\nNext: commit + push website to redeploy Azure SWA.') console.log(` cd ${join(REPO_ROOT, 'website')}`) console.log(` git add public/updater/latest.json`) console.log(` git commit -m "release: desktop ${version}"`) console.log(' git push')