From 218534d936ad183ffbfd856151d3cdbef629d398 Mon Sep 17 00:00:00 2001 From: Brendonovich Date: Thu, 17 Sep 2026 06:19:46 +0000 Subject: [PATCH] chore(release): publish beta to GitHub --- packages/desktop/scripts/publish.ts | 63 +++++++++++++++++++++++------ script/version.ts | 24 +++++++++-- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/packages/desktop/scripts/publish.ts b/packages/desktop/scripts/publish.ts index 45aa120b930..1daa98820e8 100644 --- a/packages/desktop/scripts/publish.ts +++ b/packages/desktop/scripts/publish.ts @@ -1,6 +1,8 @@ #!/usr/bin/env bun import { Script } from "@opencode/script" +import { $ } from "bun" +import path from "node:path" import { UpdateArtifact } from "../../../script/update-artifact" const dryRun = process.argv.includes("--dry-run") @@ -8,8 +10,11 @@ if (!Script.release) { console.log("skipped desktop publication without a release") process.exit(0) } +const repo = process.env.GH_REPO +if (!repo) throw new Error("GH_REPO is required") const directory = process.env.OPENCODE_DESKTOP_DIST if (!directory) throw new Error("OPENCODE_DESKTOP_DIST is required") +const tag = `v${Script.version}` const files = ( await Array.fromAsync( new Bun.Glob("*.{exe,blockmap,dmg,zip,AppImage,deb,rpm,app.tar.gz}").scan({ cwd: directory, absolute: true }), @@ -17,16 +22,24 @@ const files = ( ).sort() if (!files.length) throw new Error("No desktop release files found") -const uploaded = await UpdateArtifact.upload({ version: Script.version, files, dryRun }) -const artifact = { - channel: Script.channel, - name: "desktop", - distribution: "opencode", - version: Script.version, - metadata: { files: uploaded, ...(await metadata(Script.version, uploaded)) }, +if (!dryRun) { + await $`gh release upload ${tag} ${files} --clobber --repo ${repo}` + await $`bun ${path.join(import.meta.dir, "finalize-latest-json.ts")}` + await $`bun ${path.join(import.meta.dir, "finalize-latest-yml.ts")}` +} + +const uploaded = await UpdateArtifact.upload({ version: Script.version, files, dryRun }) +const artifacts = [ + { distribution: "github", metadata: await githubMetadata(Script.version, repo) }, + { distribution: "opencode", metadata: { files: uploaded, ...(await metadata(Script.version, uploaded)) } }, +] + +if (!dryRun) await $`gh release edit ${tag} --draft=false --repo ${repo}` +for (const artifact of artifacts) { + const input = { ...artifact, channel: Script.channel, name: "desktop", version: Script.version } + if (dryRun) console.log(`dry-run artifact: ${JSON.stringify(input)}`) + if (!dryRun) await UpdateArtifact.publish(input) } -if (dryRun) console.log(`dry-run artifact: ${JSON.stringify(artifact)}`) -if (!dryRun) await UpdateArtifact.publish(artifact) type DesktopFile = { url: string @@ -35,6 +48,32 @@ type DesktopFile = { blockMapSize?: number } +async function githubMetadata(version: string, repo: string) { + const directory = process.env.RUNNER_TEMP ?? "/tmp" + const entries = await Promise.all( + [ + ["desktop.yml", "latest.yml"], + ["desktop-mac.yml", "latest-mac.yml"], + ["desktop-linux.yml", "latest-linux.yml"], + ["desktop-linux-arm64.yml", "latest-linux-arm64.yml"], + ].map(async ([name, source]) => { + const file = Bun.file(`${directory}/${source}`) + if (!(await file.exists())) return undefined + return [ + name, + parse( + await file.text(), + version, + (filename) => `https://github.com/${repo}/releases/download/v${version}/${encodeURIComponent(filename)}`, + ), + ] as const + }), + ) + const manifests = Object.fromEntries(entries.filter((entry) => entry !== undefined)) + if (!Object.keys(manifests).length) throw new Error("No desktop update metadata found") + return { manifests } +} + async function metadata(version: string, files: Record) { const directory = process.env.LATEST_YML_DIR if (!directory) throw new Error("LATEST_YML_DIR is required") @@ -65,7 +104,7 @@ async function metadata(version: string, files: Record) item.sources.map(async ([subdirectory, source]) => { const file = Bun.file(`${directory}/${subdirectory}/${source}`) if (!(await file.exists())) return undefined - return parse(await file.text(), version, files) + return parse(await file.text(), version, (filename) => files[filename]?.url) }), ) ).filter((manifest) => manifest !== undefined) @@ -81,7 +120,7 @@ async function metadata(version: string, files: Record) return { manifests } } -function parse(content: string, version: string, uploaded: Record) { +function parse(content: string, version: string, resolve: (filename: string) => string | undefined) { const lines = content.split("\n") const found = lines .find((line) => line.startsWith("version:")) @@ -100,7 +139,7 @@ function parse(content: string, version: string, uploaded: Record "No notable changes") + const dir = process.env.RUNNER_TEMP ?? "/tmp" + const notesFile = `${dir}/opencode-release-notes.txt` + await Bun.write(notesFile, body) + await $`gh release create v${Script.version} -d --target ${sha} --title "v${Script.version}" --notes-file ${notesFile}` + const release = await $`gh release view v${Script.version} --json tagName,databaseId`.json() + output.push(`release=${release.databaseId}`) + output.push(`tag=${release.tagName}`) +} else if (Script.channel === "beta") { + await $`gh release create v${Script.version} -d --title "v${Script.version}" --repo ${process.env.GH_REPO}` + const release = + await $`gh release view v${Script.version} --json tagName,databaseId --repo ${process.env.GH_REPO}`.json() + output.push(`release=${release.databaseId}`) + output.push(`tag=${release.tagName}`) } output.push(`repo=${process.env.GH_REPO}`)