diff options
| author | Brendan Allan <[email protected]> | 2026-03-04 15:12:34 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-04 15:12:34 +0800 |
| commit | 5cf235fa6cf7b4c890c68f8ff68a96fcae992abf (patch) | |
| tree | 25fdfd8ce95ad048fb097822995dcf060e8d6d8b /packages/desktop-electron/scripts | |
| parent | e4f0825c56300286ec0aa82b1006e4006a17e1e1 (diff) | |
| download | opencode-5cf235fa6cf7b4c890c68f8ff68a96fcae992abf.tar.gz opencode-5cf235fa6cf7b4c890c68f8ff68a96fcae992abf.zip | |
desktop: add electron version (#15663)
Diffstat (limited to 'packages/desktop-electron/scripts')
| -rw-r--r-- | packages/desktop-electron/scripts/copy-bundles.ts | 12 | ||||
| -rw-r--r-- | packages/desktop-electron/scripts/copy-icons.ts | 12 | ||||
| -rw-r--r-- | packages/desktop-electron/scripts/finalize-latest-yml.ts | 116 | ||||
| -rw-r--r-- | packages/desktop-electron/scripts/predev.ts | 17 | ||||
| -rwxr-xr-x | packages/desktop-electron/scripts/prepare.ts | 24 | ||||
| -rw-r--r-- | packages/desktop-electron/scripts/utils.ts | 69 |
6 files changed, 250 insertions, 0 deletions
diff --git a/packages/desktop-electron/scripts/copy-bundles.ts b/packages/desktop-electron/scripts/copy-bundles.ts new file mode 100644 index 000000000..6ef3335eb --- /dev/null +++ b/packages/desktop-electron/scripts/copy-bundles.ts @@ -0,0 +1,12 @@ +import { $ } from "bun" +import * as path from "node:path" + +import { RUST_TARGET } from "./utils" + +if (!RUST_TARGET) throw new Error("RUST_TARGET not defined") + +const BUNDLE_DIR = "dist" +const BUNDLES_OUT_DIR = path.join(process.cwd(), "dist/bundles") + +await $`mkdir -p ${BUNDLES_OUT_DIR}` +await $`cp -r ${BUNDLE_DIR}/* ${BUNDLES_OUT_DIR}` diff --git a/packages/desktop-electron/scripts/copy-icons.ts b/packages/desktop-electron/scripts/copy-icons.ts new file mode 100644 index 000000000..400f42757 --- /dev/null +++ b/packages/desktop-electron/scripts/copy-icons.ts @@ -0,0 +1,12 @@ +import { $ } from "bun" +import { resolveChannel } from "./utils" + +const arg = process.argv[2] +const channel = arg === "dev" || arg === "beta" || arg === "prod" ? arg : resolveChannel() + +const src = `./icons/${channel}` +const dest = "resources/icons" + +await $`rm -rf ${dest}` +await $`cp -R ${src} ${dest}` +console.log(`Copied ${channel} icons from ${src} to ${dest}`) diff --git a/packages/desktop-electron/scripts/finalize-latest-yml.ts b/packages/desktop-electron/scripts/finalize-latest-yml.ts new file mode 100644 index 000000000..42ec23b64 --- /dev/null +++ b/packages/desktop-electron/scripts/finalize-latest-yml.ts @@ -0,0 +1,116 @@ +#!/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: single arch, pass through +const win = await read("latest-yml-x86_64-pc-windows-msvc", "latest.yml") +if (win) output["latest.yml"] = serialize(win) + +// 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") diff --git a/packages/desktop-electron/scripts/predev.ts b/packages/desktop-electron/scripts/predev.ts new file mode 100644 index 000000000..a688d0e7f --- /dev/null +++ b/packages/desktop-electron/scripts/predev.ts @@ -0,0 +1,17 @@ +import { $ } from "bun" + +import { copyBinaryToSidecarFolder, getCurrentSidecar, windowsify } from "./utils" + +await $`bun ./scripts/copy-icons.ts ${process.env.OPENCODE_CHANNEL ?? "dev"}` + +const RUST_TARGET = Bun.env.RUST_TARGET + +const sidecarConfig = getCurrentSidecar(RUST_TARGET) + +const binaryPath = windowsify(`../opencode/dist/${sidecarConfig.ocBinary}/bin/opencode`) + +await (sidecarConfig.ocBinary.includes("-baseline") + ? $`cd ../opencode && bun run build --single --baseline` + : $`cd ../opencode && bun run build --single`) + +await copyBinaryToSidecarFolder(binaryPath, RUST_TARGET) diff --git a/packages/desktop-electron/scripts/prepare.ts b/packages/desktop-electron/scripts/prepare.ts new file mode 100755 index 000000000..3764db921 --- /dev/null +++ b/packages/desktop-electron/scripts/prepare.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env bun +import { $ } from "bun" + +import { Script } from "@opencode-ai/script" +import { copyBinaryToSidecarFolder, getCurrentSidecar, resolveChannel, windowsify } from "./utils" + +const channel = resolveChannel() +await $`bun ./scripts/copy-icons.ts ${channel}` + +const pkg = await Bun.file("./package.json").json() +pkg.version = Script.version +await Bun.write("./package.json", JSON.stringify(pkg, null, 2) + "\n") +console.log(`Updated package.json version to ${Script.version}`) + +const sidecarConfig = getCurrentSidecar() + +const dir = "resources/opencode-binaries" + +await $`mkdir -p ${dir}` +await $`gh run download ${Bun.env.GITHUB_RUN_ID} -n opencode-cli`.cwd(dir) + +await copyBinaryToSidecarFolder(windowsify(`${dir}/${sidecarConfig.ocBinary}/bin/opencode`)) + +await $`rm -rf ${dir}` diff --git a/packages/desktop-electron/scripts/utils.ts b/packages/desktop-electron/scripts/utils.ts new file mode 100644 index 000000000..4c9af1fc7 --- /dev/null +++ b/packages/desktop-electron/scripts/utils.ts @@ -0,0 +1,69 @@ +import { $ } from "bun" + +export type Channel = "dev" | "beta" | "prod" + +export function resolveChannel(): Channel { + const raw = Bun.env.OPENCODE_CHANNEL + if (raw === "dev" || raw === "beta" || raw === "prod") return raw + return "dev" +} + +export const SIDECAR_BINARIES: Array<{ rustTarget: string; ocBinary: string; assetExt: string }> = [ + { + rustTarget: "aarch64-apple-darwin", + ocBinary: "opencode-darwin-arm64", + assetExt: "zip", + }, + { + rustTarget: "x86_64-apple-darwin", + ocBinary: "opencode-darwin-x64-baseline", + assetExt: "zip", + }, + { + rustTarget: "x86_64-pc-windows-msvc", + ocBinary: "opencode-windows-x64-baseline", + assetExt: "zip", + }, + { + rustTarget: "x86_64-unknown-linux-gnu", + ocBinary: "opencode-linux-x64-baseline", + assetExt: "tar.gz", + }, + { + rustTarget: "aarch64-unknown-linux-gnu", + ocBinary: "opencode-linux-arm64", + assetExt: "tar.gz", + }, +] + +export const RUST_TARGET = Bun.env.RUST_TARGET + +function nativeTarget() { + const { platform, arch } = process + if (platform === "darwin") return arch === "arm64" ? "aarch64-apple-darwin" : "x86_64-apple-darwin" + if (platform === "win32") return "x86_64-pc-windows-msvc" + if (platform === "linux") return arch === "arm64" ? "aarch64-unknown-linux-gnu" : "x86_64-unknown-linux-gnu" + throw new Error(`Unsupported platform: ${platform}/${arch}`) +} + +export function getCurrentSidecar(target = RUST_TARGET ?? nativeTarget()) { + const binaryConfig = SIDECAR_BINARIES.find((b) => b.rustTarget === target) + if (!binaryConfig) throw new Error(`Sidecar configuration not available for Rust target '${target}'`) + + return binaryConfig +} + +export async function copyBinaryToSidecarFolder(source: string) { + const dir = `resources` + await $`mkdir -p ${dir}` + const dest = windowsify(`${dir}/opencode-cli`) + await $`cp ${source} ${dest}` + if (process.platform === "darwin") await $`codesign --force --sign - ${dest}` + + console.log(`Copied ${source} to ${dest}`) +} + +export function windowsify(path: string) { + if (path.endsWith(".exe")) return path + return `${path}${process.platform === "win32" ? ".exe" : ""}` +} |
