227 lines · 6.8 KB · TypeScript
RawDownload
#!/usr/bin/env bun import path from "path"import fs from "fs"import { $ } from "bun"import { fileURLToPath } from "url"import { createEncryptPromptsPlugin, generateKeyFragments } from "./encrypt-prompts-plugin" const __filename = fileURLToPath(import.meta.url)const __dirname = path.dirname(__filename)const dir = path.resolve(__dirname, "..") process.chdir(dir) import pkg from "../package.json"import { Script } from "@synsci/script" // Fetch and generate models.dev snapshot. Runtime refreshes stale snapshots// when current frontier IDs are missing, so avoid inventing model aliases here.const modelsRaw = process.env.MODELS_DEV_API_JSON  ? await Bun.file(process.env.MODELS_DEV_API_JSON).text()  : await fetch(`https://models.dev/api.json`).then((x) => x.text())const modelsJson = JSON.parse(modelsRaw) await Bun.write(  path.join(dir, "src/provider/models-snapshot.ts"),  `// Auto-generated by build.ts - do not edit\nexport const snapshot = ${JSON.stringify(modelsJson)} as const\n`,)console.log("Generated models-snapshot.ts") const singleFlag = process.argv.includes("--single")const baselineFlag = process.argv.includes("--baseline")const skipInstall = process.argv.includes("--skip-install") const allTargets: {  os: string  arch: "arm64" | "x64"  abi?: "musl"  avx2?: false}[] = [  {    os: "linux",    arch: "arm64",  },  {    os: "linux",    arch: "x64",  },  {    os: "linux",    arch: "x64",    avx2: false,  },  {    os: "linux",    arch: "arm64",    abi: "musl",  },  {    os: "linux",    arch: "x64",    abi: "musl",  },  {    os: "linux",    arch: "x64",    abi: "musl",    avx2: false,  },  {    os: "darwin",    arch: "arm64",  },  {    os: "darwin",    arch: "x64",  },  {    os: "darwin",    arch: "x64",    avx2: false,  },  {    os: "win32",    arch: "x64",  },  {    os: "win32",    arch: "x64",    avx2: false,  },] const targets = singleFlag  ? allTargets.filter((item) => {      if (item.os !== process.platform || item.arch !== process.arch) {        return false      }       // When building for the current platform, prefer a single native binary by default.      // Baseline binaries require additional Bun artifacts and can be flaky to download.      if (item.avx2 === false) {        return baselineFlag      }       // also skip abi-specific builds for the same reason      if (item.abi !== undefined) {        return false      }       return true    })  : allTargets await $`rm -rf dist` // Build the openscience web UI (frontend/workspace) and regenerate the embedded asset// manifest so the catch-all route in server.ts can serve it locally instead// of proxying to Vercel. The manifest imports every dist file with// `with { type: "file" }` so Bun's --compile bundles them into the binary.//// `frontend/workspace/package.json` references workspace packages via the `catalog:`// protocol, which only resolves after `bun install` at the monorepo root. On a// fresh clone vite's resolver fails with cryptic "Cannot find package" errors// — run the workspace install if `node_modules` is missing.const repoRoot = path.resolve(dir, "../..")const webAppDir = path.resolve(repoRoot, "frontend/workspace")if (!fs.existsSync(path.join(repoRoot, "node_modules")) || !fs.existsSync(path.join(webAppDir, "node_modules"))) {  console.log("workspace node_modules missing — running bun install at repo root")  await $`bun install`.cwd(repoRoot)}console.log("building frontend/workspace (openscience web)")await $`bun run build`.cwd(webAppDir)await $`bun run script/generate-web-assets.ts` // Generate encryption key for prompt files (unique per build)const keyFragments = generateKeyFragments()const { plugin: encryptPlugin, defines: keyDefines } = createEncryptPromptsPlugin(keyFragments) const binaries: Record<string, string> = {}if (!skipInstall) {  await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parcel/watcher"]}`}for (const item of targets) {  const name = [    pkg.name,    // changing to win32 flags npm for some reason    item.os === "win32" ? "windows" : item.os,    item.arch,    item.avx2 === false ? "baseline" : undefined,    item.abi === undefined ? undefined : item.abi,  ]    .filter(Boolean)    .join("-")  console.log(`building ${name}`)  await $`mkdir -p dist/${name}/bin`   await Bun.build({    conditions: ["browser"],    tsconfig: "./tsconfig.json",    plugins: [encryptPlugin],    sourcemap: "external",    compile: {      autoloadBunfig: false,      autoloadDotenv: false,      //@ts-ignore (bun types aren't up to date)      autoloadTsconfig: true,      autoloadPackageJson: true,      target: name.replace(pkg.name, "bun") as any,      outfile: `dist/${name}/bin/openscience`,      execArgv: [`--user-agent=openscience/${Script.version}`, "--use-system-ca", "--"],      windows: {},    },    entrypoints: ["./src/index.ts"],    define: {      ...keyDefines,      OPENSCIENCE_VERSION: `'${Script.version}'`,      OPENSCIENCE_CHANNEL: `'${Script.channel}'`,      OPENSCIENCE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "",    },  })   // Skills are served via API — no longer copied into dist   await Bun.file(`dist/${name}/package.json`).write(    JSON.stringify(      {        name,        version: Script.version,        os: [item.os],        cpu: [item.arch],        // Without libc, linux-x64 hosts install all four x64 variants        // (~750 MB) and glibc/musl selection falls to the bin wrapper.        ...(item.os === "linux" ? { libc: [item.abi === "musl" ? "musl" : "glibc"] } : {}),        // npm provenance refuses packages whose repository.url doesn't match        // the repo the workflow ran from (case-sensitive)        repository: {          type: "git",          url: "git+https://github.com/synthetic-sciences/openscience.git",        },      },      null,      2,    ),  )  binaries[name] = Script.version} if (Script.release) {  const checksums: string[] = []  for (const key of Object.keys(binaries)) {    const archiveName = key.replace(`${pkg.name}-`, "openscience-") + (key.includes("linux") ? ".tar.gz" : ".zip")    if (key.includes("linux")) {      await $`tar -czf ${path.resolve(dir, `dist/${archiveName}`)} *`.cwd(`dist/${key}/bin`)    } else {      await $`zip -r ${path.resolve(dir, `dist/${archiveName}`)} *`.cwd(`dist/${key}/bin`)    }    const bytes = await Bun.file(path.resolve(dir, `dist/${archiveName}`)).arrayBuffer()    const hash = new Bun.CryptoHasher("sha256").update(bytes).digest("hex")    checksums.push(`${hash}  ${archiveName}`)  }  // sha256sum-compatible manifest; the install script verifies against it  await Bun.write(path.resolve(dir, "dist/checksums.txt"), checksums.join("\n") + "\n")  await $`gh release upload v${Script.version} ./dist/*.zip ./dist/*.tar.gz ./dist/checksums.txt --clobber`} export { binaries }