#!/usr/bin/env bun import { Script } from "@opencode-ai/script" import { $ } from "bun" import { fileURLToPath } from "url" console.log("=== publishing ===\n") // kilocode_change start - consume changesets on the publish runner so changelog // changes are included in the release commit. Previously this ran in the // version job on a separate runner whose workspace was discarded. { await $`bun install` const paths = ["packages/kilo-vscode/CHANGELOG.md", "packages/opencode/CHANGELOG.md"] const before = new Map() for (const p of paths) { before.set( p, await Bun.file(p) .text() .catch(() => ""), ) } await $`bunx changeset version` // Changeset computes its own version from package.json, but we use // Script.version. Fix the heading in any changelog that was modified. for (const p of paths) { const content = await Bun.file(p) .text() .catch(() => "") if (content !== before.get(p)) { await Bun.write(p, content.replace(/^## .+$/m, `## ${Script.version}`)) } } } // kilocode_change end const pkgjsons = await Array.fromAsync( new Bun.Glob("**/package.json").scan({ absolute: true, }), ).then((arr) => arr.filter((x) => !x.includes("node_modules") && !x.includes("dist"))) for (const file of pkgjsons) { let pkg = await Bun.file(file).text() pkg = pkg.replaceAll(/"version": "[^"]+"/g, `"version": "${Script.version}"`) console.log("updated:", file) await Bun.file(file).write(pkg) } const extensionToml = fileURLToPath(new URL("../packages/extensions/zed/extension.toml", import.meta.url)) let toml = await Bun.file(extensionToml).text() toml = toml.replace(/^version = "[^"]+"/m, `version = "${Script.version}"`) toml = toml.replaceAll(/releases\/download\/v[^/]+\//g, `releases/download/v${Script.version}/`) console.log("updated:", extensionToml) await Bun.file(extensionToml).write(toml) await $`bun install` await import(`../packages/sdk/js/script/build.ts`) if (Script.release) { // kilocode_change start - commit, tag, and push with rebase + retry to handle // concurrent merges to main. Rebase (instead of cherry-pick) handles // overlapping file changes cleanly, and the retry loop covers the narrow // window between fetch and push where another commit could land. await $`git commit -am "release: v${Script.version}"` await $`git tag v${Script.version}` const retries = 3 for (let i = 1; i <= retries; i++) { await $`git fetch origin main` const rebase = await $`git rebase origin/main`.nothrow() if (rebase.exitCode !== 0) { console.error(`rebase failed (attempt ${i}/${retries}), aborting rebase`) await $`git rebase --abort`.nothrow() if (i === retries) throw new Error("failed to rebase release commit onto origin/main after " + retries + " attempts") await new Promise((r) => setTimeout(r, 3_000)) continue } const push = await $`git push origin HEAD:main --tags --no-verify --force-with-lease`.nothrow() if (push.exitCode === 0) { console.log("release commit pushed successfully") break } console.warn(`push rejected (attempt ${i}/${retries}), retrying...`) if (i === retries) throw new Error("failed to push release commit after " + retries + " attempts") await new Promise((r) => setTimeout(r, 3_000)) } // kilocode_change end // kilocode_change start // kilocode_change end // kilocode_change start - mark prerelease GitHub releases accordingly // and populate release notes from the changelog updated by changeset above. // Use an absolute path for the CHANGELOG because the imported SDK build // script chdirs into packages/sdk/js, so a relative path would miss the file // and fall through to the "No notable changes" default. const kind = Script.preview ? "pre-release" : "release" const flags = Script.preview ? ["--draft=false", "--prerelease"] : ["--draft=false"] flags.push("--title", `v${Script.version} (${kind})`) const changelogPath = fileURLToPath(new URL("../packages/kilo-vscode/CHANGELOG.md", import.meta.url)) const changelog = await Bun.file(changelogPath) .text() .catch(() => "") const body = extractLatestSection(changelog) || "No notable changes" const tmp = process.env.RUNNER_TEMP ?? "/tmp" const notes = `${tmp}/release-notes.txt` await Bun.write(notes, body) flags.push("--notes-file", notes) await $`gh release edit v${Script.version} ${flags} --repo ${process.env.GH_REPO}` // kilocode_change end } console.log("\n=== cli ===\n") await import(`../packages/opencode/script/publish.ts`) console.log("\n=== sdk ===\n") 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 // kilocode_change start - Kilo does not ship the opencode desktop app // if (Script.release) { // await $`bun ./packages/desktop/scripts/finalize-latest-json.ts` // await $`bun ./packages/desktop/scripts/finalize-latest-yml.ts` // } // kilocode_change end const dir = fileURLToPath(new URL("..", import.meta.url)) process.chdir(dir) // kilocode_change start - extract latest changelog section for release notes function extractLatestSection(changelog: string): string { if (!changelog) return "" const lines = changelog.split("\n") const start = lines.findIndex((line) => /^## /.test(line)) if (start < 0) return "" const end = lines.findIndex((line, i) => i > start && /^## /.test(line)) return lines .slice(start + 1, end < 0 ? undefined : end) .join("\n") .trim() } // kilocode_change end