Repository navigation
feat(cli): publish bolt-cli to a homebrew tap #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,11 +87,87 @@ const tags = [`${image}:${version}`, `${image}:${Script.channel}`] | |
| const tagFlags = tags.flatMap((t) => ["-t", t]) | ||
|
|
||
| // registries | ||
| // AUR and Homebrew publishing was removed: the old blocks targeted upstream's | ||
| // `opencode-bin` AUR package and `anomalyco/homebrew-tap`, which Bolt neither owns | ||
| // nor has credentials for, and their sha256 inputs (dist archives) are not | ||
| // downloaded into the publish job. Restore from git history once Bolt has its | ||
| // own tap and AUR package. | ||
| // AUR publishing was removed: the old block targeted upstream's `opencode-bin` | ||
| // AUR package, which Bolt does not own. Restore from git history once Bolt has | ||
| // its own AUR package. | ||
| if (!Script.preview) { | ||
| await $`docker buildx build --platform ${platforms} ${tagFlags} --push .` | ||
| await brew() | ||
| } | ||
|
|
||
| // Regenerates the Homebrew formula in bolt-builder/homebrew-tap so | ||
| // `brew install bolt-builder/tap/bolt-cli` tracks the latest release. The | ||
| // sha256 inputs are the exact release assets users download, fetched from the | ||
| // (still draft) GitHub release rather than rebuilt locally. | ||
| async function brew() { | ||
| const token = process.env.HOMEBREW_TAP_TOKEN | ||
| if (!token) { | ||
| console.warn("HOMEBREW_TAP_TOKEN is not set; skipping homebrew tap update") | ||
| return | ||
| } | ||
|
|
||
| const assets = [ | ||
| "bolt-cli-darwin-arm64.zip", | ||
| "bolt-cli-darwin-x64.zip", | ||
| "bolt-cli-linux-arm64.tar.gz", | ||
| "bolt-cli-linux-x64.tar.gz", | ||
| ] | ||
| await $`rm -rf ./dist/brew` | ||
| await $`mkdir -p ./dist/brew` | ||
| await $`gh release download v${Script.version} --repo bolt-builder/bolt-cli --dir ./dist/brew ${assets.flatMap((a) => ["-p", a])}` | ||
| const sha = async (asset: string) => (await $`sha256sum ./dist/brew/${asset} | cut -d' ' -f1`.text()).trim() | ||
|
|
||
| const url = (asset: string) => | ||
| `https://github.com/bolt-builder/bolt-cli/releases/download/v${Script.version}/${asset}` | ||
| const block = async (asset: string) => | ||
| [ | ||
| ` url "${url(asset)}"`, | ||
| ` sha256 "${await sha(asset)}"`, | ||
| "", | ||
| " def install", | ||
| ' bin.install "bolt"', | ||
| " end", | ||
| ].join("\n") | ||
|
|
||
| const formula = [ | ||
| "# typed: false", | ||
| "# frozen_string_literal: true", | ||
| "", | ||
| "# This file is generated by the bolt-cli publish script. DO NOT EDIT.", | ||
| "class BoltCli < Formula", | ||
| ' desc "Bolt: the AI coding agent built for the terminal."', | ||
| ' homepage "https://github.com/bolt-builder/bolt-cli"', | ||
| ` version "${Script.version.split("-")[0]}"`, | ||
| "", | ||
| " on_macos do", | ||
| " if Hardware::CPU.arm?", | ||
| await block("bolt-cli-darwin-arm64.zip"), | ||
| " end", | ||
| " if Hardware::CPU.intel?", | ||
| await block("bolt-cli-darwin-x64.zip"), | ||
| " end", | ||
| " end", | ||
| "", | ||
| " on_linux do", | ||
| " if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?", | ||
| await block("bolt-cli-linux-arm64.tar.gz"), | ||
| " end", | ||
| " if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?", | ||
| await block("bolt-cli-linux-x64.tar.gz"), | ||
| " end", | ||
| " end", | ||
| "end", | ||
| "", | ||
| ].join("\n") | ||
|
|
||
| const tap = `https://x-access-token:${token}@github.com/bolt-builder/homebrew-tap.git` | ||
| await $`rm -rf ./dist/homebrew-tap` | ||
| await $`git clone ${tap} ./dist/homebrew-tap` | ||
| await $`mkdir -p ./dist/homebrew-tap/Formula` | ||
| await Bun.file("./dist/homebrew-tap/Formula/bolt-cli.rb").write(formula) | ||
| await $`git add Formula/bolt-cli.rb`.cwd("./dist/homebrew-tap") | ||
| if ((await $`git diff --cached --quiet`.cwd("./dist/homebrew-tap").nothrow()).exitCode !== 0) { | ||
| await $`git commit -m "bolt-cli v${Script.version}"`.cwd("./dist/homebrew-tap") | ||
| await $`git push`.cwd("./dist/homebrew-tap") | ||
| } | ||
| } | ||
|
Comment on lines
+102
to
175
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -123,11 +123,11 @@ const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Serv | |
| ) | ||
|
|
||
| const getBrewFormula = Effect.fnUntraced(function* () { | ||
| const tapFormula = yield* text(["brew", "list", "--formula", "anomalyco/tap/opencode"]) | ||
| if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode" | ||
| const coreFormula = yield* text(["brew", "list", "--formula", "opencode"]) | ||
| if (coreFormula.includes("opencode")) return "opencode" | ||
| return "opencode" | ||
| const tapFormula = yield* text(["brew", "list", "--formula", "bolt-builder/tap/bolt-cli"]) | ||
| if (tapFormula.includes("bolt-cli")) return "bolt-builder/tap/bolt-cli" | ||
| const coreFormula = yield* text(["brew", "list", "--formula", "bolt-cli"]) | ||
| if (coreFormula.includes("bolt-cli")) return "bolt-cli" | ||
| return "bolt-cli" | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }) | ||
|
|
||
| const upgradeFailure = (method: Method, result?: { code: number; stdout: string; stderr: string }) => { | ||
|
|
@@ -187,7 +187,7 @@ const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Serv | |
| { name: "yarn", command: () => text(["yarn", "global", "list"]) }, | ||
| { name: "pnpm", command: () => text(["pnpm", "list", "-g", "--depth=0"]) }, | ||
| { name: "bun", command: () => text(["bun", "pm", "ls", "-g"]) }, | ||
| { name: "brew", command: () => text(["brew", "list", "--formula", "bolt"]) }, | ||
| { name: "brew", command: () => text(["brew", "list", "--formula", "bolt-cli"]) }, | ||
| { name: "scoop", command: () => text(["scoop", "list", "bolt"]) }, | ||
| { name: "choco", command: () => text(["choco", "list", "--limit-output", "bolt"]) }, | ||
| ] | ||
|
|
@@ -224,7 +224,7 @@ const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Serv | |
| return info.formulae[0].versions.stable | ||
| } | ||
| const response = yield* httpOk.execute( | ||
| HttpClientRequest.get("https://formulae.brew.sh/api/formula/opencode.json").pipe( | ||
| HttpClientRequest.get("https://formulae.brew.sh/api/formula/bolt-cli.json").pipe( | ||
| HttpClientRequest.acceptJson, | ||
| ), | ||
| ) | ||
|
|
@@ -289,12 +289,12 @@ const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Serv | |
| const formula = yield* getBrewFormula() | ||
| const env = { HOMEBREW_NO_AUTO_UPDATE: "1" } | ||
| if (formula.includes("/")) { | ||
| const tap = yield* run(["brew", "tap", "anomalyco/tap"], { env }) | ||
| const tap = yield* run(["brew", "tap", "bolt-builder/tap"], { env }) | ||
| if (tap.code !== 0) { | ||
| upgradeResult = tap | ||
| break | ||
| } | ||
| const repo = yield* text(["brew", "--repo", "anomalyco/tap"]) | ||
| const repo = yield* text(["brew", "--repo", "bolt-builder/tap"]) | ||
|
Comment on lines
+290
to
+295
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Refresh the tap before the target-version check. For a stale Resolve the target from release metadata for tap installs, or refresh the tap before the caller performs its equality guard. Add a regression test for stale tap metadata. 🤖 Prompt for AI Agents |
||
| const dir = repo.trim() | ||
| if (dir) { | ||
| const pull = yield* run(["git", "pull", "--ff-only"], { cwd: dir, env }) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.