From 51ce954857c2f8c0db3ce43c75328c11c0e38285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Fri, 27 Feb 2026 15:38:51 -0300 Subject: [PATCH] fix: build and release cli --- flake.nix | 3 +- packages/opencode/bin/kilo | 18 +- packages/opencode/script/build.ts | 21 +++ packages/opencode/script/postinstall.mjs | 229 +++++++++++++---------- script/beta.ts | 10 +- script/publish.ts | 7 +- 6 files changed, 171 insertions(+), 117 deletions(-) diff --git a/flake.nix b/flake.nix index fdd673dd73..ed9a99e8a6 100644 --- a/flake.nix +++ b/flake.nix @@ -162,7 +162,8 @@ unzip gnutar gzip - ripgrep + patchelf + ripgrep kilo-dev kilo-install-bin kilo-bin diff --git a/packages/opencode/bin/kilo b/packages/opencode/bin/kilo index 64c5529501..9f69ce669b 100755 --- a/packages/opencode/bin/kilo +++ b/packages/opencode/bin/kilo @@ -25,11 +25,20 @@ if (envPath) { const scriptPath = fs.realpathSync(__filename) const scriptDir = path.dirname(scriptPath) -// +// kilocode_change start - fall through to findBinary() if cached binary fails const cached = path.join(scriptDir, ".kilo") if (fs.existsSync(cached)) { - run(cached) + const result = childProcess.spawnSync(cached, process.argv.slice(2), { + stdio: "inherit", + }) + if (!result.error) { + const code = typeof result.status === "number" ? result.status : 0 + process.exit(code) + } + // cached binary failed (e.g. wrong platform/arch, missing dynamic linker), + // fall through to findBinary() which has better variant detection } +// kilocode_change end const platformMap = { darwin: "darwin", @@ -166,9 +175,6 @@ function findBinary(startDir) { } } -const scriptPath = fs.realpathSync(__filename) -const scriptDir = path.dirname(scriptPath) - const resolved = findBinary(scriptDir) if (!resolved) { console.error( @@ -179,4 +185,4 @@ if (!resolved) { process.exit(1) } -run(resolved) +run(resolved) \ No newline at end of file diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 88444cb5f9..919ee4244c 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -188,6 +188,27 @@ for (const item of targets) { }, }) + // kilocode_change start - fix Nix-specific ELF interpreter paths for Linux binaries + if (item.os === "linux") { + const interpreters: Record = { + x64: "/lib64/ld-linux-x86-64.so.2", + arm64: "/lib/ld-linux-aarch64.so.1", + "x64-musl": "/lib/ld-musl-x86_64.so.1", + "arm64-musl": "/lib/ld-musl-aarch64.so.1", + } + const key = item.abi === "musl" ? `${item.arch}-musl` : item.arch + const interpreter = interpreters[key] + if (interpreter) { + try { + await $`patchelf --set-interpreter ${interpreter} dist/${name}/bin/kilo` + console.log(`patched interpreter for ${name} -> ${interpreter}`) + } catch { + console.warn(`patchelf not available, skipping interpreter fix for ${name}`) + } + } + } + // kilocode_change end + await $`rm -rf ./dist/${name}/bin/tui` await Bun.file(`dist/${name}/package.json`).write( JSON.stringify( diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs index e2098cbbe8..a95813d295 100644 --- a/packages/opencode/script/postinstall.mjs +++ b/packages/opencode/script/postinstall.mjs @@ -3,129 +3,150 @@ import fs from "fs" import path from "path" import os from "os" +import childProcess from "child_process" import { fileURLToPath } from "url" import { createRequire } from "module" const __dirname = path.dirname(fileURLToPath(import.meta.url)) const require = createRequire(import.meta.url) +// kilocode_change start - variant detection matching bin/kilo logic +const platformMap = { + darwin: "darwin", + linux: "linux", + win32: "windows", +} +const archMap = { + x64: "x64", + arm64: "arm64", + arm: "arm", +} + function detectPlatformAndArch() { - // Map platform names - let platform - switch (os.platform()) { - case "darwin": - platform = "darwin" - break - case "linux": - platform = "linux" - break - case "win32": - platform = "windows" - break - default: - platform = os.platform() - break - } - - // Map architecture names - let arch - switch (os.arch()) { - case "x64": - arch = "x64" - break - case "arm64": - arch = "arm64" - break - case "arm": - arch = "arm" - break - default: - arch = os.arch() - break - } - + const platform = platformMap[os.platform()] || os.platform() + const arch = archMap[os.arch()] || os.arch() return { platform, arch } } -function findBinary() { +function supportsAvx2() { const { platform, arch } = detectPlatformAndArch() - const packageName = `@kilocode/cli-${platform}-${arch}` - const binaryName = platform === "windows" ? "kilo.exe" : "kilo" + if (arch !== "x64") return false - try { - // Use require.resolve to find the package - const packageJsonPath = require.resolve(`${packageName}/package.json`) - const packageDir = path.dirname(packageJsonPath) - const binaryPath = path.join(packageDir, "bin", binaryName) - - if (!fs.existsSync(binaryPath)) { - throw new Error(`Binary not found at ${binaryPath}`) - } - - return { binaryPath, binaryName } - } catch (error) { - throw new Error(`Could not find package ${packageName}: ${error.message}`) - } -} - -function prepareBinDirectory(binaryName) { - const binDir = path.join(__dirname, "bin") - const targetPath = path.join(binDir, binaryName) - - // Ensure bin directory exists - if (!fs.existsSync(binDir)) { - fs.mkdirSync(binDir, { recursive: true }) - } - - // Remove existing binary/symlink if it exists - if (fs.existsSync(targetPath)) { - fs.unlinkSync(targetPath) - } - - return { binDir, targetPath } -} - -function symlinkBinary(sourcePath, binaryName) { - const { targetPath } = prepareBinDirectory(binaryName) - - fs.symlinkSync(sourcePath, targetPath) - console.log(`kilo binary symlinked: ${targetPath} -> ${sourcePath}`) - - // Verify the file exists after operation - if (!fs.existsSync(targetPath)) { - throw new Error(`Failed to symlink binary to ${targetPath}`) - } -} - -async function main() { - try { - if (os.platform() === "win32") { - // On Windows, the .exe is already included in the package and bin field points to it - // No postinstall setup needed - console.log("Windows detected: binary setup not needed (using packaged .exe)") - return - } - - // On non-Windows platforms, just verify the binary package exists - // Don't replace the wrapper script - it handles binary execution - const { binaryPath } = findBinary() - const target = path.join(__dirname, "bin", ".kilo") // kilocode_change - if (fs.existsSync(target)) fs.unlinkSync(target) + if (platform === "linux") { try { - fs.linkSync(binaryPath, target) + return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8")) } catch { - fs.copyFileSync(binaryPath, target) + return false } - fs.chmodSync(target, 0o755) - } catch (error) { - console.error("Failed to setup kilo binary:", error.message) - process.exit(1) } + + if (platform === "darwin") { + try { + const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { + encoding: "utf8", + timeout: 1500, + }) + if (result.status !== 0) return false + return (result.stdout || "").trim() === "1" + } catch { + return false + } + } + + return false +} + +function isMusl() { + try { + if (fs.existsSync("/etc/alpine-release")) return true + } catch { + // ignore + } + + try { + const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" }) + const text = ((result.stdout || "") + (result.stderr || "")).toLowerCase() + if (text.includes("musl")) return true + } catch { + // ignore + } + + return false +} + +function getPackageNames() { + const { platform, arch } = detectPlatformAndArch() + const base = `@kilocode/cli-${platform}-${arch}` + const avx2 = supportsAvx2() + const baseline = arch === "x64" && !avx2 + + if (platform === "linux") { + const musl = isMusl() + if (musl) { + if (arch === "x64") { + if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base] + return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`] + } + return [`${base}-musl`, base] + } + if (arch === "x64") { + if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`] + return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`] + } + return [base, `${base}-musl`] + } + + if (arch === "x64") { + if (baseline) return [`${base}-baseline`, base] + return [base, `${base}-baseline`] + } + return [base] +} + +function findBinary() { + const { platform } = detectPlatformAndArch() + const binaryName = platform === "windows" ? "kilo.exe" : "kilo" + const names = getPackageNames() + + for (const packageName of names) { + try { + const packageJsonPath = require.resolve(`${packageName}/package.json`) + const packageDir = path.dirname(packageJsonPath) + const binaryPath = path.join(packageDir, "bin", binaryName) + + if (fs.existsSync(binaryPath)) { + return { binaryPath, binaryName } + } + } catch { + // package not installed, try next variant + } + } + + throw new Error(`Could not find any binary package. Tried: ${names.map((n) => `"${n}"`).join(", ")}`) +} +// kilocode_change end + +function main() { + if (os.platform() === "win32") { + // On Windows, the .exe is already included in the package and bin field points to it + console.log("Windows detected: binary setup not needed (using packaged .exe)") + return + } + + const { binaryPath } = findBinary() + const target = path.join(__dirname, "bin", ".kilo") // kilocode_change + if (fs.existsSync(target)) fs.unlinkSync(target) + try { + fs.linkSync(binaryPath, target) + } catch { + fs.copyFileSync(binaryPath, target) + } + fs.chmodSync(target, 0o755) } try { main() } catch (error) { - console.error("Postinstall script error:", error.message) - process.exit(0) + console.error("Failed to setup kilo binary:", error.message) + process.exit(1) } diff --git a/script/beta.ts b/script/beta.ts index b0e6c2dcc1..aa6659dac3 100755 --- a/script/beta.ts +++ b/script/beta.ts @@ -89,11 +89,11 @@ async function main() { return } - console.log("Fetching latest dev branch...") - await $`git fetch origin dev` + console.log("Fetching latest main branch...") + await $`git fetch origin main` - console.log("Checking out beta branch...") - await $`git checkout -B beta origin/dev` + console.log("Checking out main branch...") + await $`git checkout -B beta origin/main` const applied: number[] = [] const failed: FailedPR[] = [] @@ -177,7 +177,7 @@ async function main() { await $`git fetch origin beta` const localTree = await $`git rev-parse beta^{tree}`.text() - const remoteTrees = (await $`git log origin/dev..origin/beta --format=%T`.text()).split("\n") + const remoteTrees = (await $`git log origin/main..origin/beta --format=%T`.text()).split("\n") const matchIdx = remoteTrees.indexOf(localTree.trim()) if (matchIdx !== -1) { diff --git a/script/publish.ts b/script/publish.ts index b7ed5c8221..4ef6fb5804 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -62,7 +62,7 @@ if (Script.release) { await $`git commit -am "release: v${Script.version}"` await $`git tag v${Script.version}` await $`git fetch origin` - await $`git cherry-pick HEAD..origin/dev`.nothrow() + await $`git cherry-pick HEAD..origin/main`.nothrow() // kilocode_change await $`git push origin HEAD --tags --no-verify --force-with-lease` await new Promise((resolve) => setTimeout(resolve, 5_000)) } @@ -79,5 +79,10 @@ await import(`../packages/sdk/js/script/publish.ts`) console.log("\n=== plugin ===\n") await import(`../packages/plugin/script/publish.ts`) +// kilocode_change start +console.log("\n=== vscode ===\n") +await import(`../packages/kilo-vscode/script/publish.ts`) +// kilocode_change end + const dir = fileURLToPath(new URL("..", import.meta.url)) process.chdir(dir)