Skip to content

Commit 2534800

Browse files
Chore/arbiter release (#9)
* chore(release): add arbiter homebrew release automation * feat(release): add curl-first installer script
1 parent 804bff1 commit 2534800

5 files changed

Lines changed: 181 additions & 3 deletions

File tree

.github/workflows/arbiter-release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ jobs:
7171
files: |
7272
dist/*.tar.gz
7373
dist/checksums.txt
74+
install.sh
7475
generate_release_notes: true
7576

7677
update-homebrew:

AGENTS.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ This directory contains release automation helpers for runtime distribution.
319319

320320
- `build_arbiter_artifacts.sh` builds versioned `arbiter` archives for macOS/Linux amd64+arm64 and writes `dist/checksums.txt`.
321321
- `render_homebrew_formula.sh` renders a Homebrew `Formula/arbiter.rb` from release checksums and GitHub release URLs.
322+
- Root `install.sh` is the curl-first installer that downloads the appropriate release artifact and verifies checksums before install.
322323

323324
### `.github/workflows/ci.yml`
324325

@@ -379,11 +380,8 @@ Update these files when request/response shapes change.
379380
- Use `context.Context` consistently for cancellation and deadlines.
380381
- Reject unknown or ambiguous payloads unless they normalize safely.
381382
- Version policy and data artifacts so every decision is traceable.
382-
<<<<<<< Updated upstream
383383
- Always switch back to `master` before checking out or creating a new branch.
384-
=======
385384
- Any change to an npm package must include a `package.json` version bump (`patch`, `minor`, or `major`) appropriate to the impact.
386-
>>>>>>> Stashed changes
387385
- When adding a new service or package, update `README.md` if the public setup or architecture changes.
388386
- When adding or changing a service boundary, update this file.
389387
- Keep examples aligned with `schema.CurrentSchemaVersion`.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The key design choice is that the control plane stays off the hot path. Enforcem
6666
Install options:
6767

6868
- Source run: `go run ./cmd/arbiter local ...` (available now in-repo)
69+
- Curl install script: `curl -fsSL https://raw.githubusercontent.com/yajasmalhotra/arbiter/master/install.sh | sh`
6970
- Homebrew (after formula publish): `brew tap yajasmalhotra/homebrew-tap && brew install arbiter`
7071

7172
### 1. Initialize local runtime config

docs/homebrew-release.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ On each run:
1919
3. Publishes GitHub Release assets:
2020
- `arbiter_<version>_<os>_<arch>.tar.gz`
2121
- `checksums.txt`
22+
- `install.sh`
2223
4. Optionally updates `Formula/arbiter.rb` in a Homebrew tap repository.
2324

2425
## Required Configuration
@@ -47,3 +48,16 @@ Render formula:
4748
/tmp/arbiter.rb
4849
```
4950

51+
## Curl-First Install
52+
53+
Users can install without Homebrew via:
54+
55+
```bash
56+
curl -fsSL https://raw.githubusercontent.com/yajasmalhotra/arbiter/master/install.sh | sh
57+
```
58+
59+
Pinning a version:
60+
61+
```bash
62+
curl -fsSL https://raw.githubusercontent.com/yajasmalhotra/arbiter/master/install.sh | ARBITER_VERSION=0.2.0 sh
63+
```

install.sh

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
REPO="${ARBITER_INSTALL_REPO:-yajasmalhotra/arbiter}"
5+
VERSION_INPUT="${ARBITER_VERSION:-latest}"
6+
INSTALL_DIR="${ARBITER_INSTALL_DIR:-}"
7+
8+
usage() {
9+
cat <<'EOF'
10+
Usage: install.sh [--version <semver>] [--install-dir <path>]
11+
12+
Environment variables:
13+
ARBITER_VERSION Version without leading v (default: latest release)
14+
ARBITER_INSTALL_DIR Install directory override
15+
ARBITER_INSTALL_REPO GitHub repo owner/name (default: yajasmalhotra/arbiter)
16+
EOF
17+
}
18+
19+
while [[ $# -gt 0 ]]; do
20+
case "$1" in
21+
--version)
22+
VERSION_INPUT="${2:-}"
23+
shift 2
24+
;;
25+
--install-dir)
26+
INSTALL_DIR="${2:-}"
27+
shift 2
28+
;;
29+
-h|--help)
30+
usage
31+
exit 0
32+
;;
33+
*)
34+
echo "unknown argument: $1" >&2
35+
usage
36+
exit 1
37+
;;
38+
esac
39+
done
40+
41+
if ! command -v curl >/dev/null 2>&1; then
42+
echo "curl is required." >&2
43+
exit 1
44+
fi
45+
46+
detect_os() {
47+
local os
48+
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
49+
case "${os}" in
50+
darwin|linux) echo "${os}" ;;
51+
*)
52+
echo "unsupported OS: ${os}" >&2
53+
exit 1
54+
;;
55+
esac
56+
}
57+
58+
detect_arch() {
59+
local arch
60+
arch="$(uname -m)"
61+
case "${arch}" in
62+
x86_64|amd64) echo "amd64" ;;
63+
arm64|aarch64) echo "arm64" ;;
64+
*)
65+
echo "unsupported architecture: ${arch}" >&2
66+
exit 1
67+
;;
68+
esac
69+
}
70+
71+
latest_version() {
72+
local api_url tag
73+
api_url="https://api.github.com/repos/${REPO}/releases/latest"
74+
tag="$(curl -fsSL "${api_url}" | grep -m1 '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/' || true)"
75+
if [[ -z "${tag}" ]]; then
76+
echo "failed to resolve latest release from ${api_url}" >&2
77+
echo "set ARBITER_VERSION or pass --version <semver>" >&2
78+
exit 1
79+
fi
80+
echo "${tag#arbiter-v}"
81+
}
82+
83+
if [[ "${VERSION_INPUT}" == "latest" ]]; then
84+
VERSION="$(latest_version)"
85+
else
86+
VERSION="${VERSION_INPUT#v}"
87+
fi
88+
89+
OS="$(detect_os)"
90+
ARCH="$(detect_arch)"
91+
ASSET="arbiter_${VERSION}_${OS}_${ARCH}.tar.gz"
92+
BASE_URL="https://github.com/${REPO}/releases/download/arbiter-v${VERSION}"
93+
94+
tmpdir="$(mktemp -d)"
95+
cleanup() {
96+
rm -rf "${tmpdir}"
97+
}
98+
trap cleanup EXIT
99+
100+
echo "Downloading ${ASSET}..."
101+
curl -fsSL "${BASE_URL}/${ASSET}" -o "${tmpdir}/${ASSET}"
102+
curl -fsSL "${BASE_URL}/checksums.txt" -o "${tmpdir}/checksums.txt"
103+
104+
expected_sha="$(awk -v name="${ASSET}" '{
105+
file=$2
106+
sub(/^\.\//, "", file)
107+
if (file == name) print $1
108+
}' "${tmpdir}/checksums.txt")"
109+
110+
if [[ -z "${expected_sha}" ]]; then
111+
echo "missing checksum for ${ASSET}" >&2
112+
exit 1
113+
fi
114+
115+
if command -v sha256sum >/dev/null 2>&1; then
116+
actual_sha="$(sha256sum "${tmpdir}/${ASSET}" | awk '{print $1}')"
117+
elif command -v shasum >/dev/null 2>&1; then
118+
actual_sha="$(shasum -a 256 "${tmpdir}/${ASSET}" | awk '{print $1}')"
119+
else
120+
echo "sha256sum or shasum is required for checksum verification." >&2
121+
exit 1
122+
fi
123+
124+
if [[ "${expected_sha}" != "${actual_sha}" ]]; then
125+
echo "checksum mismatch for ${ASSET}" >&2
126+
exit 1
127+
fi
128+
129+
tar -xzf "${tmpdir}/${ASSET}" -C "${tmpdir}"
130+
if [[ ! -f "${tmpdir}/arbiter" ]]; then
131+
echo "release archive missing arbiter binary" >&2
132+
exit 1
133+
fi
134+
135+
if [[ -z "${INSTALL_DIR}" ]]; then
136+
if [[ -w "/usr/local/bin" ]]; then
137+
INSTALL_DIR="/usr/local/bin"
138+
else
139+
INSTALL_DIR="${HOME}/.local/bin"
140+
fi
141+
fi
142+
143+
mkdir -p "${INSTALL_DIR}"
144+
target="${INSTALL_DIR}/arbiter"
145+
146+
if [[ -w "${INSTALL_DIR}" ]]; then
147+
install -m 755 "${tmpdir}/arbiter" "${target}"
148+
else
149+
if command -v sudo >/dev/null 2>&1; then
150+
sudo install -m 755 "${tmpdir}/arbiter" "${target}"
151+
else
152+
echo "install directory is not writable and sudo is unavailable: ${INSTALL_DIR}" >&2
153+
exit 1
154+
fi
155+
fi
156+
157+
echo "Installed arbiter ${VERSION} to ${target}"
158+
if [[ ":${PATH}:" != *":${INSTALL_DIR}:"* ]]; then
159+
echo "Add ${INSTALL_DIR} to PATH if needed."
160+
fi
161+
echo "Next:"
162+
echo " arbiter local init"
163+
echo " arbiter local start"
164+

0 commit comments

Comments
 (0)