Files
dev/scripts/generate-homebrew-formula.sh
zeekay b31cb86dad chore: nothing in the root says whose agent this used to be
`codex-cli/` published `@hanzo/dev`, so the directory carrying our npm
package was named after someone else's product. It is `npm/`.
`patches/codex.json` is `patches/upstream.json` — it anchors edits against
upstream, and the file should say so rather than name one of them.

Deleted outright: `justfile`, whose first line was
`set working-directory := "codex-rs"` for a directory this repository has
never had; `announcement_tip.toml`, an example file for a TUI we do not
ship; and `.codex/` and `.code/`, which held upstream's own process skills
— babysit-pr, codex-bug, pushing-ci-changes — that were never ours.

Three branding leaks a real run turned up, none of which the help text
showed: `dev exec` announced "OpenAI Codex v0.7.0-dev.1" as its first
line, the status card was missed when the session header was anchored,
and both bubblewrap warnings said "Codex's Linux sandbox".

README: say what ships. It claimed the agent drives a browser, which is
the port that has not landed, and showed a screenshot of it doing so.
Both are gone until the bridge is back. The model count is "hundreds" —
`/v1/models` answers 458 today, an earlier line here said "over 500", and
hanzo.ai derives its own count precisely because typed ones drift.
Document the npm install beside the curl one; both give the same build.
2026-09-07 20:32:32 -07:00

189 lines
5.7 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# Generate the Homebrew formula from the latest GitHub release.
# Writes Formula/hanzo-dev.rb into the repo root (not a tap); release.yml copies
# it into hanzoai/homebrew-tap as Formula/hanzo-dev.rb.
owner_repo="hanzoai/dev"
version="${1:-}"
# Helper to treat placeholder versions as unset
sanitize_version() {
case "$1" in
""|"0.0.0"|"0.0.0-dev") echo "" ;;
*) echo "$1" ;;
esac
}
version="$(sanitize_version "$version")"
if [ -z "$version" ] && [ -f "Cargo.toml" ]; then
raw="$(awk -F '"' '/^\[workspace.package\]/{f=1; next} f && $1 ~ /version/ {print $2; exit}' Cargo.toml)"
version="$(sanitize_version "$raw")"
fi
if [ -z "$version" ] && [ -f "npm/package.json" ]; then
raw="$(jq -r .version npm/package.json)"
version="$(sanitize_version "$raw")"
fi
if [ -z "$version" ] && git rev-parse --git-dir >/dev/null 2>&1; then
tag="$(git describe --tags --abbrev=0 2>/dev/null || true)"
if [ -n "$tag" ]; then
# Strip optional leading 'v'
tag="${tag#v}"
version="$(sanitize_version "$tag")"
fi
fi
if [ -z "$version" ]; then
cat <<'ERR' >&2
Unable to infer release version.
Provide it as the first argument or ensure a tagged release exists.
The script no longer defaults to 0.0.0 to avoid publishing broken formulas.
ERR
exit 1
fi
# Optional directory where CI placed artifacts (step: Prepare release assets)
RELEASE_ASSETS_DIR=${RELEASE_ASSETS_DIR:-"release-assets"}
# Every platform the tap formula serves. Dropping one silently narrows the
# formula, so all four are required to produce a publishable file.
assets=(
"dev-aarch64-apple-darwin.tar.gz"
"dev-x86_64-apple-darwin.tar.gz"
"dev-aarch64-unknown-linux-musl.tar.gz"
"dev-x86_64-unknown-linux-musl.tar.gz"
)
placeholder_for() {
case "$1" in
dev-aarch64-apple-darwin.tar.gz) echo "__SHA_MACOS_ARM__" ;;
dev-x86_64-apple-darwin.tar.gz) echo "__SHA_MACOS_INTEL__" ;;
dev-aarch64-unknown-linux-musl.tar.gz) echo "__SHA_LINUX_ARM__" ;;
dev-x86_64-unknown-linux-musl.tar.gz) echo "__SHA_LINUX_INTEL__" ;;
*) echo "" ;;
esac
}
sha256_file() {
local f="$1"
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$f" | awk '{print $1}'
elif command -v sha256sum >/dev/null 2>&1; then
sha256sum "$f" | awk '{print $1}'
else
echo ""; return 1
fi
}
# Retry a command with backoff: retry <max_tries> <sleep_seconds> <cmd...>
retry() {
local max="$1"; shift
local sleep_s="$1"; shift
local n=1
while :; do
if "$@"; then return 0; fi
if [ "$n" -ge "$max" ]; then return 1; fi
n=$((n+1))
sleep "$sleep_s"
done
}
mkdir -p Formula
formula="Formula/hanzo-dev.rb"
cat > "$formula" <<'RUBY'
class HanzoDev < Formula
desc "Local AI coding agent for your terminal, powered by Hanzo AI"
homepage "https://github.com/hanzoai/dev"
version "__VERSION__"
license "Apache-2.0"
on_macos do
on_arm do
url "https://github.com/hanzoai/dev/releases/download/v#{version}/dev-aarch64-apple-darwin.tar.gz"
sha256 "__SHA_MACOS_ARM__"
end
on_intel do
url "https://github.com/hanzoai/dev/releases/download/v#{version}/dev-x86_64-apple-darwin.tar.gz"
sha256 "__SHA_MACOS_INTEL__"
end
end
on_linux do
on_arm do
url "https://github.com/hanzoai/dev/releases/download/v#{version}/dev-aarch64-unknown-linux-musl.tar.gz"
sha256 "__SHA_LINUX_ARM__"
end
on_intel do
url "https://github.com/hanzoai/dev/releases/download/v#{version}/dev-x86_64-unknown-linux-musl.tar.gz"
sha256 "__SHA_LINUX_INTEL__"
end
end
livecheck do
url :stable
strategy :github_latest
end
def install
# Installs `dev` ONLY. It must not also write a `hanzo` shim: `hanzo` is the
# Hanzo CLI (Formula/hanzo.rb), a different program. A shim here would make
# `hanzo` mean "the dev coding assistant" for anyone who installed this
# formula first, and would collide with hanzo.rb on the same filename.
bin.install Dir["dev-*"].first => "dev"
end
test do
system "#{bin}/dev", "--help"
end
end
RUBY
sed -i.bak "s#__VERSION__#${version}#" "$formula"
for a in "${assets[@]}"; do
url="https://github.com/${owner_repo}/releases/download/v${version}/${a}"
tmp="${TMPDIR:-/tmp}/${a}"
sha=""
# Prefer local artifact if available to avoid CDN propagation races
local_path="${RELEASE_ASSETS_DIR}/${a}"
if [ -f "$local_path" ]; then
echo "Using local asset for sha256: ${local_path}" >&2
sha=$(sha256_file "$local_path") || sha=""
fi
# Fallback to remote download (with retries) if local missing or sha empty
if [ -z "$sha" ]; then
echo "Downloading ${url} (fallback for sha256)..." >&2
if ! retry 12 5 curl -fsSL "${url}" -o "${tmp}"; then
echo "ERROR: could not download ${url} to compute sha256." >&2
else
sha=$(sha256_file "$tmp" || true)
fi
fi
# A formula shipped with an unresolved placeholder is a broken tap: `brew
# install` fails on every machine until the next release. Refuse to emit one.
if [ -z "$sha" ]; then
echo "ERROR: no sha256 for ${a}; refusing to write a formula with an unresolved placeholder." >&2
rm -f "$formula" "$formula.bak"
exit 1
fi
ph="$(placeholder_for "$a")"
sed -i.bak "s#${ph}#${sha}#" "$formula"
done
rm -f "$formula.bak"
echo "Wrote ${formula} for v${version}" >&2
# Optional: best-effort HEAD check to surface propagation status without failing CI
for a in "${assets[@]}"; do
url="https://github.com/${owner_repo}/releases/download/v${version}/${a}"
if ! retry 6 5 bash -c "curl -fsI \"$url\" >/dev/null"; then
echo "WARN: ${a} not yet available at ${url} (HEAD 404). Likely CDN propagation; continuing." >&2
fi
done