Files
2026-07-13 12:28:55 +08:00

92 lines
3.6 KiB
TypeScript

#!/usr/bin/env bun
// Publishes @opencode-ai/models to npm, opencode-style:
// - the version is never stored in git: it is read from npm
// plus a semver bump computed here (patch by default);
// - `--if-changed` (scheduled data releases) skips publishing when the
// freshly generated snapshot payload is byte-identical to the one inside
// the currently published tarball;
// - package.json is restored after publishing.
//
// Auth: npm Trusted Publishing (OIDC) in CI — no token needed once the
// package is linked to this repo+workflow on npmjs.com. `--provenance` is
// added automatically when running in GitHub Actions.
import path from "node:path"
import { appendFile, mkdtemp, rm } from "node:fs/promises"
import { tmpdir } from "node:os"
import { $ } from "bun"
import { loadCatalog, snapshotPayload } from "./generate.ts"
const pkg = path.join(import.meta.dirname, "..")
const packageName = "@opencode-ai/models"
const packageJsonPath = path.join(pkg, "package.json")
const bumpArg = process.argv.find((argument) => argument.startsWith("--bump="))?.slice("--bump=".length) ?? "patch"
const ifChanged = process.argv.includes("--if-changed")
if (!["patch", "minor", "major"].includes(bumpArg)) {
console.error(`Invalid --bump=${bumpArg}; expected patch, minor, or major`)
process.exit(1)
}
async function currentVersion(): Promise<string> {
return (await $`npm view ${packageName} version`.text()).trim()
}
function bump(version: string, kind: string): string {
const [major = 0, minor = 0, patch = 0] = version.split(".").map((part) => Number.parseInt(part, 10))
if (kind === "major") return `${major + 1}.0.0`
if (kind === "minor") return `${major}.${minor + 1}.0`
return `${major}.${minor}.${patch + 1}`
}
/** The `const data = ...` line of the published dist/snapshot.js, or undefined. */
async function publishedSnapshotLine(): Promise<string | undefined> {
const directory = await mkdtemp(path.join(tmpdir(), "models-dev-publish-"))
try {
const tarball = (await $`npm pack ${packageName}@latest --pack-destination ${directory}`.cwd(directory).text())
.trim()
.split("\n")
.at(-1)!
await $`tar -xzf ${path.join(directory, tarball)} -C ${directory}`
const file = Bun.file(path.join(directory, "package", "dist", "snapshot.js"))
if (!(await file.exists())) return undefined
const text = await file.text()
return text.split("\n").find((line) => line.startsWith("const data = "))
} finally {
await rm(directory, { recursive: true, force: true })
}
}
if (ifChanged) {
const catalog = await loadCatalog()
const fresh = `const data = /* @__PURE__ */ JSON.parse(${JSON.stringify(snapshotPayload(catalog))})`
const published = await publishedSnapshotLine()
if (published === fresh) {
console.log("Snapshot unchanged since the published version; skipping publish")
process.exit(0)
}
}
const current = await currentVersion()
const next = bump(current, bumpArg)
console.log(`Publishing ${packageName}@${next} (${bumpArg} bump from ${current})`)
const packageJsonText = await Bun.file(packageJsonPath).text()
const packageJson = JSON.parse(packageJsonText)
try {
packageJson.version = next
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n")
const provenance = process.env["GITHUB_ACTIONS"] === "true" ? ["--provenance"] : []
await $`npm publish --access public ${provenance}`.cwd(pkg)
const output = process.env["GITHUB_OUTPUT"]
if (output !== undefined) await appendFile(output, `version=${next}\n`)
console.log(`Published ${packageName}@${next}`)
} finally {
await Bun.write(packageJsonPath, packageJsonText)
}