chore(release): drop the separate opencode-beta release repo (#56083)

Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot] 2026-10-16 13:54:42 +00:00 • committed by GitHub
parent c5681f4e15
commit ed772f5009
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 3 additions and 419 deletions

View file

@ -68,12 +68,10 @@ jobs:
OPENCODE_BUMP: ${{ inputs.bump }}
OPENCODE_VERSION: ${{ inputs.version }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GH_REPO: ${{ (github.ref_name == 'beta' && 'anomalyco/opencode-beta') || github.repository }}
outputs:
version: ${{ steps.version.outputs.version }}
release: ${{ steps.version.outputs.release }}
tag: ${{ steps.version.outputs.tag }}
repo: ${{ steps.version.outputs.repo }}
build-cli:
needs: version
@ -102,7 +100,7 @@ jobs:
env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
GH_REPO: ${{ needs.version.outputs.repo }}
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ steps.committer.outputs.token }}
- name: Build preview CLI
@ -113,7 +111,7 @@ jobs:
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
OPENCODE_RELEASE_NOTES: ${{ (github.ref_name == 'dev' && inputs.release_notes) || '' }}
GH_REPO: ${{ needs.version.outputs.repo }}
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ steps.committer.outputs.token }}
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
@ -650,7 +648,7 @@ jobs:
OPENCODE_RELEASE_NOTES: ${{ (github.ref_name == 'dev' && inputs.release_notes) || '' }}
AUR_KEY: ${{ secrets.AUR_KEY }}
GITHUB_TOKEN: ${{ steps.committer.outputs.token }}
GH_REPO: ${{ needs.version.outputs.repo }}
GH_REPO: ${{ github.repository }}
NPM_CONFIG_PROVENANCE: false
LATEST_YML_DIR: /tmp/latest-yml
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}

View file

@ -1,270 +0,0 @@
#!/usr/bin/env bun
import { $ } from "bun"
import path from "node:path"
import { parseArgs } from "node:util"
const { values } = parseArgs({
args: Bun.argv.slice(2),
options: {
"dry-run": { type: "boolean", default: false },
},
})
const dryRun = values["dry-run"]
const repo = process.env.GH_REPO
if (!repo) throw new Error("GH_REPO is required")
const releaseId = process.env.OPENCODE_RELEASE
if (!releaseId) throw new Error("OPENCODE_RELEASE is required")
const version = process.env.OPENCODE_VERSION
if (!version) throw new Error("OPENCODE_VERSION is required")
const dir = process.env.LATEST_YML_DIR
if (!dir) throw new Error("LATEST_YML_DIR is required")
const root = dir
const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN
if (!token) throw new Error("GH_TOKEN or GITHUB_TOKEN is required")
const rel = await fetch(`https://api.github.com/repos/${repo}/releases/${releaseId}`, {
headers: {
Authorization: `token ${token}`,
Accept: "application/vnd.github+json",
},
})
if (!rel.ok) {
throw new Error(`Failed to fetch release: ${rel.status} ${rel.statusText}`)
}
type Asset = {
name: string
url: string
}
type Release = {
assets?: Asset[]
}
const assets = ((await rel.json()) as Release).assets ?? []
const amap = new Map(assets.map((item) => [item.name, item]))
type Item = {
url: string
}
type Yml = {
version: string
files: Item[]
}
function parse(text: string): Yml {
const lines = text.split("\n")
let version = ""
const files: Item[] = []
let url = ""
const flush = () => {
if (!url) return
files.push({ url })
url = ""
}
for (const line of lines) {
const trim = line.trim()
if (line.startsWith("version:")) {
version = line.slice("version:".length).trim()
continue
}
if (trim.startsWith("- url:")) {
flush()
url = trim.slice("- url:".length).trim()
continue
}
const indented = line.startsWith(" ") || line.startsWith("\t")
if (!indented) flush()
}
flush()
return { version, files }
}
async function read(sub: string, file: string) {
const item = Bun.file(path.join(root, sub, file))
if (!(await item.exists())) return undefined
return parse(await item.text())
}
function pick(list: Item[], exts: string[]) {
for (const ext of exts) {
const found = list.find((item) => item.url.split("?")[0]?.toLowerCase().endsWith(ext))
if (found) return found.url
}
}
function link(raw: string) {
if (raw.startsWith("https://") || raw.startsWith("http://")) return raw
return `https://github.com/${repo}/releases/download/v${version}/${raw}`
}
async function sign(url: string, key: string) {
const name = decodeURIComponent(new URL(url).pathname.split("/").pop() ?? key)
const asset = amap.get(name)
const res = await fetch(asset?.url ?? url, {
headers: {
Authorization: `token ${token}`,
...(asset ? { Accept: "application/octet-stream" } : {}),
},
})
if (!res.ok) {
throw new Error(`Failed to fetch file ${name}: ${res.status} ${res.statusText} (${asset?.url ?? url})`)
}
const tmp = process.env.RUNNER_TEMP ?? "/tmp"
const file = path.join(tmp, name)
await Bun.write(file, await res.arrayBuffer())
await $`bunx @tauri-apps/cli signer sign ${file}`
const sigFile = Bun.file(`${file}.sig`)
if (!(await sigFile.exists())) throw new Error(`Signature file not found for ${name}`)
return (await sigFile.text()).trim()
}
const add = async (data: Record<string, { url: string; signature: string }>, key: string, raw: string | undefined) => {
if (!raw) return
if (data[key]) return
const url = link(raw)
data[key] = { url, signature: await sign(url, key) }
}
const alias = (data: Record<string, { url: string; signature: string }>, key: string, src: string) => {
if (data[key]) return
if (!data[src]) return
data[key] = data[src]
}
const winx = await read("latest-yml-x86_64-pc-windows-msvc", "latest.yml")
const wina = await read("latest-yml-aarch64-pc-windows-msvc", "latest.yml")
const macx = await read("latest-yml-x86_64-apple-darwin", "latest-mac.yml")
const maca = await read("latest-yml-aarch64-apple-darwin", "latest-mac.yml")
const linx = await read("latest-yml-x86_64-unknown-linux-gnu", "latest-linux.yml")
const lina = await read("latest-yml-aarch64-unknown-linux-gnu", "latest-linux-arm64.yml")
const yver = winx?.version ?? wina?.version ?? macx?.version ?? maca?.version ?? linx?.version ?? lina?.version
if (yver && yver !== version) throw new Error(`latest.yml version mismatch: expected ${version}, got ${yver}`)
const out: Record<string, { url: string; signature: string }> = {}
const winxexe = pick(winx?.files ?? [], [".exe"])
const winaexe = pick(wina?.files ?? [], [".exe"])
const macxTarGz = "opencode-desktop-mac-x64.app.tar.gz"
const macaTarGz = "opencode-desktop-mac-arm64.app.tar.gz"
const linxDeb = pick(linx?.files ?? [], [".deb"])
const linxRpm = pick(linx?.files ?? [], [".rpm"])
const linxAppImage = pick(linx?.files ?? [], [".appimage"])
const linaDeb = pick(lina?.files ?? [], [".deb"])
const linaRpm = pick(lina?.files ?? [], [".rpm"])
const linaAppImage = pick(lina?.files ?? [], [".appimage"])
await add(out, "windows-x86_64-nsis", winxexe)
await add(out, "windows-aarch64-nsis", winaexe)
await add(out, "darwin-x86_64-app", macxTarGz)
await add(out, "darwin-aarch64-app", macaTarGz)
await add(out, "linux-x86_64-deb", linxDeb)
await add(out, "linux-x86_64-rpm", linxRpm)
await add(out, "linux-x86_64-appimage", linxAppImage)
await add(out, "linux-aarch64-deb", linaDeb)
await add(out, "linux-aarch64-rpm", linaRpm)
await add(out, "linux-aarch64-appimage", linaAppImage)
alias(out, "windows-x86_64", "windows-x86_64-nsis")
alias(out, "windows-aarch64", "windows-aarch64-nsis")
alias(out, "darwin-x86_64", "darwin-x86_64-app")
alias(out, "darwin-aarch64", "darwin-aarch64-app")
alias(out, "linux-x86_64", "linux-x86_64-deb")
alias(out, "linux-aarch64", "linux-aarch64-deb")
const platforms = Object.fromEntries(
Object.keys(out)
.sort()
.map((key) => [key, out[key]]),
)
if (!Object.keys(platforms).length) throw new Error("No updater files found in latest.yml artifacts")
const data = {
version,
notes: "",
pub_date: new Date().toISOString(),
platforms,
}
const tmp = process.env.RUNNER_TEMP ?? "/tmp"
const file = path.join(tmp, "latest.json")
await Bun.write(file, JSON.stringify(data, null, 2))
const tag = `v${version}`
if (dryRun) {
console.log(`dry-run: wrote latest.json for ${tag} to ${file}`)
process.exit(0)
}
await $`gh release upload ${tag} ${file} --clobber --repo ${repo}`
console.log(`finalized latest.json for ${tag}`)

View file

@ -1,142 +0,0 @@
#!/usr/bin/env bun
import { $ } from "bun"
import path from "path"
const dir = process.env.LATEST_YML_DIR!
if (!dir) throw new Error("LATEST_YML_DIR is required")
const repo = process.env.GH_REPO
if (!repo) throw new Error("GH_REPO is required")
const version = process.env.OPENCODE_VERSION
if (!version) throw new Error("OPENCODE_VERSION is required")
type FileEntry = {
url: string
sha512: string
size: number
blockMapSize?: number
}
type LatestYml = {
version: string
files: FileEntry[]
releaseDate: string
}
function parse(content: string): LatestYml {
const lines = content.split("\n")
let version = ""
let releaseDate = ""
const files: FileEntry[] = []
let current: Partial<FileEntry> | undefined
const flush = () => {
if (current?.url && current.sha512 && current.size) files.push(current as FileEntry)
current = undefined
}
for (const line of lines) {
const indented = line.startsWith(" ") || line.startsWith(" -")
if (line.startsWith("version:")) version = line.slice("version:".length).trim()
else if (line.startsWith("releaseDate:"))
releaseDate = line.slice("releaseDate:".length).trim().replace(/^'|'$/g, "")
else if (line.trim().startsWith("- url:")) {
flush()
current = { url: line.trim().slice("- url:".length).trim() }
} else if (indented && current && line.trim().startsWith("sha512:"))
current.sha512 = line.trim().slice("sha512:".length).trim()
else if (indented && current && line.trim().startsWith("size:"))
current.size = Number(line.trim().slice("size:".length).trim())
else if (indented && current && line.trim().startsWith("blockMapSize:"))
current.blockMapSize = Number(line.trim().slice("blockMapSize:".length).trim())
else if (!indented && current) flush()
}
flush()
return { version, files, releaseDate }
}
function serialize(data: LatestYml) {
const lines = [`version: ${data.version}`, "files:"]
for (const file of data.files) {
lines.push(` - url: ${file.url}`)
lines.push(` sha512: ${file.sha512}`)
lines.push(` size: ${file.size}`)
if (file.blockMapSize) lines.push(` blockMapSize: ${file.blockMapSize}`)
}
lines.push(`releaseDate: '${data.releaseDate}'`)
return lines.join("\n") + "\n"
}
async function read(subdir: string, filename: string): Promise<LatestYml | undefined> {
const file = Bun.file(path.join(dir, subdir, filename))
if (!(await file.exists())) return undefined
return parse(await file.text())
}
const output: Record<string, string> = {}
// Windows: merge arm64 + x64 into single file
const winX64 = await read("latest-yml-x86_64-pc-windows-msvc", "latest.yml")
const winArm64 = await read("latest-yml-aarch64-pc-windows-msvc", "latest.yml")
if (winX64 || winArm64) {
const base = winArm64 ?? winX64!
output["latest.yml"] = serialize({
version: base.version,
files: [...(winArm64?.files ?? []), ...(winX64?.files ?? [])],
releaseDate: base.releaseDate,
})
}
// Linux x64: pass through
const linuxX64 = await read("latest-yml-x86_64-unknown-linux-gnu", "latest-linux.yml")
if (linuxX64) output["latest-linux.yml"] = serialize(linuxX64)
// Linux arm64: pass through
const linuxArm64 = await read("latest-yml-aarch64-unknown-linux-gnu", "latest-linux-arm64.yml")
if (linuxArm64) output["latest-linux-arm64.yml"] = serialize(linuxArm64)
// macOS: merge arm64 + x64 into single file
const macX64 = await read("latest-yml-x86_64-apple-darwin", "latest-mac.yml")
const macArm64 = await read("latest-yml-aarch64-apple-darwin", "latest-mac.yml")
if (macX64 || macArm64) {
const base = macArm64 ?? macX64!
output["latest-mac.yml"] = serialize({
version: base.version,
files: [...(macArm64?.files ?? []), ...(macX64?.files ?? [])],
releaseDate: base.releaseDate,
})
}
// Upload to release
const tag = `v${version}`
const tmp = process.env.RUNNER_TEMP ?? "/tmp"
for (const [filename, content] of Object.entries(output)) {
const filepath = path.join(tmp, filename)
await Bun.write(filepath, content)
await $`gh release upload ${tag} ${filepath} --clobber --repo ${repo}`
console.log(`uploaded ${filename}`)
}
console.log("finalized latest yml files")

View file

@ -9,8 +9,6 @@ if (!Script.preview || Script.channel === "beta") {
output.push(`tag=v${Script.version}`)
}
output.push(`repo=${process.env.GH_REPO}`)
if (process.env.GITHUB_OUTPUT) {
await Bun.write(process.env.GITHUB_OUTPUT, output.join("\n"))
}