| 1 | #!/usr/bin/env bash |
| 2 | # Update the Homebrew tap after a release. |
| 3 | # |
| 4 | # The tap GitHub repo is still Hmbown/homebrew-deepseek-tui until Hunter |
| 5 | # renames it. The formula users type is `codewhale`. The legacy |
| 6 | # `deepseek-tui` formula stays as a deprecated alias for one overlap release. |
| 7 | # |
| 8 | # Expected environment: |
| 9 | # TAG – git tag, e.g. "v0.8.31" |
| 10 | # MANIFEST – path to codewhale-artifacts-sha256.txt |
| 11 | # TAP_REPO – owner/repo of the Homebrew tap |
| 12 | # TOKEN – PAT with contents:write on TAP_REPO (optional; skips if unset) |
| 13 | # FORMULA_OUTPUT – optional local render path used by contract tests |
| 14 | # FORMULA_LEGACY_OUTPUT – optional local render path for the alias formula |
| 15 | |
| 16 | set -euo pipefail |
| 17 | |
| 18 | : "${TAG:?}" |
| 19 | : "${MANIFEST:?}" |
| 20 | : "${TAP_REPO:?}" |
| 21 | |
| 22 | if [ -z "${TOKEN:-}" ] && [ -z "${FORMULA_OUTPUT:-}" ]; then |
| 23 | echo "No Homebrew tap token configured; skipping." |
| 24 | exit 0 |
| 25 | fi |
| 26 | |
| 27 | VERSION="${TAG#v}" |
| 28 | |
| 29 | die() { echo "::error::${1}" >&2; exit 1; } |
| 30 | |
| 31 | sha() { |
| 32 | local file="${1:?}" |
| 33 | local val |
| 34 | val="$(awk -v f="${file}" '$2 == f {print $1; exit}' "${MANIFEST}")" |
| 35 | if [ -z "${val}" ]; then |
| 36 | die "Missing binary in checksum manifest: ${file}" |
| 37 | fi |
| 38 | echo "${val}" |
| 39 | } |
| 40 | |
| 41 | # --- read checksums --------------------------------------------------- |
| 42 | |
| 43 | # One compiled runtime exposed under the two supported command names. |
| 44 | SHA_COD_MACOS_ARM="$(sha codewhale-macos-arm64)" |
| 45 | SHA_CODEW_MACOS_ARM="$(sha codew-macos-arm64)" |
| 46 | SHA_COD_MACOS_X64="$(sha codewhale-macos-x64)" |
| 47 | SHA_CODEW_MACOS_X64="$(sha codew-macos-x64)" |
| 48 | SHA_COD_LINUX_ARM="$(sha codewhale-linux-arm64)" |
| 49 | SHA_CODEW_LINUX_ARM="$(sha codew-linux-arm64)" |
| 50 | SHA_COD_LINUX_X64="$(sha codewhale-linux-x64)" |
| 51 | SHA_CODEW_LINUX_X64="$(sha codew-linux-x64)" |
| 52 | readonly SHA_COD_MACOS_ARM SHA_CODEW_MACOS_ARM |
| 53 | readonly SHA_COD_MACOS_X64 SHA_CODEW_MACOS_X64 |
| 54 | readonly SHA_COD_LINUX_ARM SHA_CODEW_LINUX_ARM |
| 55 | readonly SHA_COD_LINUX_X64 SHA_CODEW_LINUX_X64 |
| 56 | |
| 57 | # --- temp dirs -------------------------------------------------------- |
| 58 | |
| 59 | FORMULA_FILE="$(mktemp)" |
| 60 | LEGACY_FILE="$(mktemp)" |
| 61 | TAP_DIR="$(mktemp -d)" |
| 62 | trap 'rm -rf "${TAP_DIR}" "${FORMULA_FILE}" "${LEGACY_FILE}"' EXIT |
| 63 | |
| 64 | readonly BASE_URL="https://github.com/Hmbown/CodeWhale/releases/download/${TAG}" |
| 65 | |
| 66 | render_formula() { |
| 67 | local class_name="${1:?}" |
| 68 | local extra_header="${2:-}" |
| 69 | cat << EOF |
| 70 | class ${class_name} < Formula |
| 71 | desc "Agentic terminal for open-source and open-weight coding models" |
| 72 | homepage "https://github.com/Hmbown/CodeWhale" |
| 73 | version "${VERSION}" |
| 74 | license "MIT" |
| 75 | depends_on "node" |
| 76 | ${extra_header} |
| 77 | on_macos do |
| 78 | if Hardware::CPU.arm? |
| 79 | url "${BASE_URL}/codewhale-macos-arm64", using: :nounzip |
| 80 | sha256 "${SHA_COD_MACOS_ARM}" |
| 81 | resource "codew" do |
| 82 | url "${BASE_URL}/codew-macos-arm64", using: :nounzip |
| 83 | sha256 "${SHA_CODEW_MACOS_ARM}" |
| 84 | end |
| 85 | else |
| 86 | url "${BASE_URL}/codewhale-macos-x64", using: :nounzip |
| 87 | sha256 "${SHA_COD_MACOS_X64}" |
| 88 | resource "codew" do |
| 89 | url "${BASE_URL}/codew-macos-x64", using: :nounzip |
| 90 | sha256 "${SHA_CODEW_MACOS_X64}" |
| 91 | end |
| 92 | end |
| 93 | end |
| 94 | |
| 95 | on_linux do |
| 96 | if Hardware::CPU.arm? |
| 97 | url "${BASE_URL}/codewhale-linux-arm64", using: :nounzip |
| 98 | sha256 "${SHA_COD_LINUX_ARM}" |
| 99 | resource "codew" do |
| 100 | url "${BASE_URL}/codew-linux-arm64", using: :nounzip |
| 101 | sha256 "${SHA_CODEW_LINUX_ARM}" |
| 102 | end |
| 103 | else |
| 104 | url "${BASE_URL}/codewhale-linux-x64", using: :nounzip |
| 105 | sha256 "${SHA_COD_LINUX_X64}" |
| 106 | resource "codew" do |
| 107 | url "${BASE_URL}/codew-linux-x64", using: :nounzip |
| 108 | sha256 "${SHA_CODEW_LINUX_X64}" |
| 109 | end |
| 110 | end |
| 111 | end |
| 112 | |
| 113 | def install |
| 114 | bin.install Dir["*"].first => "codewhale" |
| 115 | resource("codew").stage { bin.install Dir["*"].first => "codew" } |
| 116 | end |
| 117 | |
| 118 | test do |
| 119 | system "#{bin}/codewhale", "--version" |
| 120 | system "#{bin}/codew", "--version" |
| 121 | end |
| 122 | end |
| 123 | EOF |
| 124 | } |
| 125 | |
| 126 | render_formula "Codewhale" "" > "${FORMULA_FILE}" |
| 127 | render_formula "DeepseekTui" " deprecate! date: \"2026-08-14\", because: \"renamed to codewhale\" |
| 128 | " > "${LEGACY_FILE}" |
| 129 | |
| 130 | if [ -n "${FORMULA_OUTPUT:-}" ]; then |
| 131 | cp "${FORMULA_FILE}" "${FORMULA_OUTPUT}" |
| 132 | echo "Rendered Homebrew formula to ${FORMULA_OUTPUT}" |
| 133 | if [ -n "${FORMULA_LEGACY_OUTPUT:-}" ]; then |
| 134 | cp "${LEGACY_FILE}" "${FORMULA_LEGACY_OUTPUT}" |
| 135 | echo "Rendered legacy Homebrew formula to ${FORMULA_LEGACY_OUTPUT}" |
| 136 | fi |
| 137 | exit 0 |
| 138 | fi |
| 139 | |
| 140 | # --- push to tap repo -------------------------------------------------- |
| 141 | |
| 142 | ENCODED_TOKEN="$(printf '%s' "${TOKEN}" | python3 -c 'import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read(),safe=""))')" |
| 143 | TAP_URL="https://x-access-token:${ENCODED_TOKEN}@github.com/${TAP_REPO}.git" |
| 144 | |
| 145 | git clone --depth 1 "${TAP_URL}" "${TAP_DIR}" |
| 146 | |
| 147 | mkdir -p "${TAP_DIR}/Formula" |
| 148 | cp "${FORMULA_FILE}" "${TAP_DIR}/Formula/codewhale.rb" |
| 149 | cp "${LEGACY_FILE}" "${TAP_DIR}/Formula/deepseek-tui.rb" |
| 150 | |
| 151 | cd "${TAP_DIR}" |
| 152 | git config user.name "github-actions[bot]" |
| 153 | git config user.email "github-actions[bot]@users.noreply.github.com" |
| 154 | |
| 155 | git add Formula/codewhale.rb Formula/deepseek-tui.rb |
| 156 | |
| 157 | if git diff --cached --quiet; then |
| 158 | echo "Formula unchanged (already at ${VERSION}); nothing to push." |
| 159 | exit 0 |
| 160 | fi |
| 161 | |
| 162 | git commit -m "chore: bump formula to ${VERSION} |
| 163 | |
| 164 | Automated update from the release workflow." |
| 165 | |
| 166 | git push origin HEAD:main |
| 167 | echo "Pushed formula update to ${TAP_REPO} (v${VERSION})" |
| 168 |