#!/usr/bin/env bun import { $ } from "bun" import pkg from "../package.json" import { Script } from "@synsci/script" import { fileURLToPath } from "url" const dir = fileURLToPath(new URL("..", import.meta.url)) process.chdir(dir) const binaries: Record = {} for (const filepath of new Bun.Glob("@synsci/*/package.json").scanSync({ cwd: "./dist" })) { const platform = await Bun.file(`./dist/${filepath}`).json() // never let the meta package list itself (re-runs re-glob the dist dir) if (platform.name === pkg.name) continue binaries[platform.name] = platform.version } console.log("binaries", binaries) if (Object.keys(binaries).length === 0) { throw new Error("No binary packages found in dist/. Did the build step run?") } const version = Object.values(binaries)[0] await $`mkdir -p ./dist/${pkg.name}` await $`cp -r ./bin ./dist/${pkg.name}/bin` await $`cp ./script/preinstall.mjs ./dist/${pkg.name}/preinstall.mjs` await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs` await Bun.file(`./dist/${pkg.name}/package.json`).write( JSON.stringify( { name: pkg.name, bin: { openscience: `./bin/openscience`, }, scripts: { // best-effort: clears a stale global @synsci/cli whose `openscience` // bin link would make npm refuse the install (EEXIST); never fails preinstall: "node ./preinstall.mjs || exit 0", postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs", }, version: version, // 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", }, optionalDependencies: binaries, }, null, 2, ), ) // Publish platform packages SEQUENTIALLY with retries. Each tarball is ~90MB; // publishing all 11 in parallel saturates the uplink and npm times out. const results: PromiseSettledResult[] = [] for (const [name] of Object.entries(binaries)) { try { if (!name.includes("windows")) { await $`chmod 755 ./dist/${name}/bin/openscience` } await $`bun pm pack`.cwd(`./dist/${name}`) let published = false let lastErr: unknown for (let attempt = 1; attempt <= 5 && !published; attempt++) { try { await $`npm publish *.tgz --access public --tag ${Script.channel}`.cwd(`./dist/${name}`) published = true } catch (e) { lastErr = e const msg = e instanceof Error ? e.message : String(e) if (msg.includes("cannot publish over") || msg.includes("previously published")) { published = true // already on the registry break } console.warn(` retry ${name} (attempt ${attempt})`) } } if (!published) throw lastErr console.log(` published ${name}`) results.push({ status: "fulfilled", value: name }) } catch (e) { results.push({ status: "rejected", reason: e }) } } const failed = results.filter((r) => r.status === "rejected") const succeeded = results.filter((r) => r.status === "fulfilled") if (failed.length > 0) { console.error(`${failed.length}/${results.length} binary packages failed to publish:`) for (const f of failed) console.error(" ", (f as PromiseRejectedResult).reason) throw new Error("Refusing to publish @synsci/openscience wrapper because one or more platform packages failed") } if (succeeded.length > 0) { console.log(`${succeeded.length}/${results.length} binary packages published successfully`) } await $`cd ./dist/${pkg.name} && bun pm pack && npm publish *.tgz --access public --tag ${Script.channel}` // registries (Homebrew tap, AUR) — non-fatal, npm publish above is what matters if (!Script.preview) { try { // Calculate SHA values const arm64Sha = await $`sha256sum ./dist/openscience-linux-arm64.tar.gz | cut -d' ' -f1` .text() .then((x) => x.trim()) const x64Sha = await $`sha256sum ./dist/openscience-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) const macX64Sha = await $`sha256sum ./dist/openscience-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) const macArm64Sha = await $`sha256sum ./dist/openscience-darwin-arm64.zip | cut -d' ' -f1` .text() .then((x) => x.trim()) const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) // Homebrew formula const homebrewFormula = [ "# typed: false", "# frozen_string_literal: true", "", "# This file was generated by GoReleaser. DO NOT EDIT.", "class Openscience < Formula", ` desc "The AI research agent built for the terminal."`, ` homepage "https://github.com/synthetic-sciences/OpenScience"`, ` version "${Script.version.split("-")[0]}"`, "", ` depends_on "ripgrep"`, "", " on_macos do", " if Hardware::CPU.intel?", ` url "https://github.com/synthetic-sciences/OpenScience/releases/download/v${Script.version}/openscience-darwin-x64.zip"`, ` sha256 "${macX64Sha}"`, "", " def install", ' bin.install "openscience"', " end", " end", " if Hardware::CPU.arm?", ` url "https://github.com/synthetic-sciences/OpenScience/releases/download/v${Script.version}/openscience-darwin-arm64.zip"`, ` sha256 "${macArm64Sha}"`, "", " def install", ' bin.install "openscience"', " end", " end", " end", "", " on_linux do", " if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?", ` url "https://github.com/synthetic-sciences/OpenScience/releases/download/v${Script.version}/openscience-linux-x64.tar.gz"`, ` sha256 "${x64Sha}"`, " def install", ' bin.install "openscience"', " end", " end", " if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?", ` url "https://github.com/synthetic-sciences/OpenScience/releases/download/v${Script.version}/openscience-linux-arm64.tar.gz"`, ` sha256 "${arm64Sha}"`, " def install", ' bin.install "openscience"', " end", " end", " end", "end", "", "", ].join("\n") // The workflow's github.token is scoped to this repo and can never push // to the tap repo — a dedicated fine-grained PAT is required. const token = process.env.HOMEBREW_TAP_TOKEN if (!token) { console.warn( "::warning title=homebrew skipped::HOMEBREW_TAP_TOKEN not set — brew users stay on the previous version", ) } else { const tap = `https://x-access-token:${token}@github.com/synthetic-sciences/homebrew-tap.git` await $`rm -rf ./dist/homebrew-tap` await $`git clone ${tap} ./dist/homebrew-tap` await Bun.file("./dist/homebrew-tap/openscience.rb").write(homebrewFormula) await $`cd ./dist/homebrew-tap && git add openscience.rb` await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"` await $`cd ./dist/homebrew-tap && git push` } } catch (e) { const message = e instanceof Error ? e.message : String(e) console.warn( `::warning title=homebrew failed::tap update failed — brew users stay on the previous version (${message})`, ) } }