Skip to content

Commit 3f2d716

Browse files
Thezone-1claude
andauthored
Harden CI for the harvester binary and add a one-line install script (#2)
- CI now explicitly builds cmd/trainpulse-harvester and cross-compiles the whole module for every release target (linux/darwin/windows, amd64/arm64) on every PR, so a build-tag mistake like the harvest package's unix/stub executor split fails fast instead of only at release time. - scripts/install.sh downloads the latest (or pinned) release binary for the current OS/arch from GitHub Releases, verifies it against SHA256SUMS, and installs it — README documents the one-line curl usage. The existing release workflow (triggered on v* tags) already cross-compiles both binaries via `make release` and attaches them with checksums; this just makes sure CI would have caught the harvester binary being unbuildable before a tag went out, and makes "download and run" the default install path documented in the README. Claude-Session: https://claude.ai/code/session_01175LgteX3Pm1Vg7GQNPGrG Co-authored-by: Claude <noreply@anthropic.com>
1 parent e4bd55c commit 3f2d716

3 files changed

Lines changed: 150 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,34 @@ jobs:
2525
- name: go test
2626
run: go test -race ./...
2727
- name: go build
28-
run: go build ./cmd/trainpulse
28+
run: |
29+
go build ./cmd/trainpulse
30+
go build ./cmd/trainpulse-harvester
31+
32+
cross-build:
33+
name: Cross-compile sanity (${{ matrix.goos }}/${{ matrix.goarch }})
34+
runs-on: ubuntu-latest
35+
strategy:
36+
matrix:
37+
include:
38+
- goos: linux
39+
goarch: amd64
40+
- goos: linux
41+
goarch: arm64
42+
- goos: darwin
43+
goarch: arm64
44+
- goos: windows
45+
goarch: amd64
46+
env:
47+
GOOS: ${{ matrix.goos }}
48+
GOARCH: ${{ matrix.goarch }}
49+
steps:
50+
- uses: actions/checkout@v4
51+
- uses: actions/setup-go@v5
52+
with:
53+
go-version-file: go.mod
54+
- name: go build (all packages)
55+
run: go build ./...
2956

3057
python-client:
3158
name: Python client tests

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# TrainPulse
22

3+
[![CI](https://github.com/Thezone-1/trainpulse/actions/workflows/ci.yml/badge.svg)](https://github.com/Thezone-1/trainpulse/actions/workflows/ci.yml)
4+
[![Release](https://img.shields.io/github/v/release/Thezone-1/trainpulse)](https://github.com/Thezone-1/trainpulse/releases/latest)
5+
36
TrainPulse is a lightweight Go daemon for predictive diagnostics of AI and LLM training systems. It collects runtime telemetry, runs low-latency anomaly checks, scores training health, infers likely root causes, and exposes real-time terminal-native stats.
47

58
## First slice
@@ -12,6 +15,29 @@ TrainPulse is a lightweight Go daemon for predictive diagnostics of AI and LLM t
1215
- Early diagnostic rules for dataloader starvation, GPU underutilization, sync imbalance, memory pressure, thermal instability, and throughput collapse.
1316
- LLM-native signals for tokens/sec, MFU, sequence padding, tokenizer stalls, all-reduce waits, checkpoint stalls, pipeline bubbles, and per-rank stragglers.
1417

18+
## Install
19+
20+
Prebuilt binaries (linux/amd64, linux/arm64, darwin/arm64, windows/amd64) are
21+
published on [GitHub Releases](https://github.com/Thezone-1/trainpulse/releases)
22+
for every tagged version, alongside a `SHA256SUMS` file.
23+
24+
One-line install (downloads the latest release, verifies its checksum, and
25+
installs to `/usr/local/bin`):
26+
27+
```sh
28+
curl -fsSL https://raw.githubusercontent.com/Thezone-1/trainpulse/main/scripts/install.sh | sh
29+
```
30+
31+
```sh
32+
# the coordinator binary, or a pinned version, or a different install dir:
33+
curl -fsSL .../install.sh | sh -s -- trainpulse-harvester
34+
curl -fsSL .../install.sh | VERSION=v0.3.0 sh
35+
curl -fsSL .../install.sh | INSTALL_DIR="$HOME/.local/bin" sh
36+
```
37+
38+
Or download the binary for your platform directly and `chmod +x` it — no
39+
install step, no dependencies.
40+
1541
## Build
1642

1743
```sh

scripts/install.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/sh
2+
# Installs a TrainPulse binary from GitHub Releases.
3+
#
4+
# Usage:
5+
# curl -fsSL https://raw.githubusercontent.com/Thezone-1/trainpulse/main/scripts/install.sh | sh
6+
# curl -fsSL .../install.sh | sh -s -- trainpulse-harvester
7+
#
8+
# Env vars:
9+
# VERSION release tag to install, e.g. v0.3.0 (default: latest)
10+
# INSTALL_DIR where to put the binary (default: /usr/local/bin)
11+
set -eu
12+
13+
REPO="Thezone-1/trainpulse"
14+
BINARY="${1:-trainpulse}"
15+
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
16+
VERSION="${VERSION:-latest}"
17+
18+
case "$BINARY" in
19+
trainpulse | trainpulse-harvester) ;;
20+
*)
21+
echo "usage: install.sh [trainpulse|trainpulse-harvester]" >&2
22+
exit 1
23+
;;
24+
esac
25+
26+
os=$(uname -s)
27+
case "$os" in
28+
Linux) os=linux ;;
29+
Darwin) os=darwin ;;
30+
*)
31+
echo "error: no prebuilt binary for OS '$os'; download manually from https://github.com/$REPO/releases" >&2
32+
exit 1
33+
;;
34+
esac
35+
36+
arch=$(uname -m)
37+
case "$arch" in
38+
x86_64 | amd64) arch=amd64 ;;
39+
arm64 | aarch64) arch=arm64 ;;
40+
*)
41+
echo "error: no prebuilt binary for architecture '$arch'; download manually from https://github.com/$REPO/releases" >&2
42+
exit 1
43+
;;
44+
esac
45+
46+
if [ "$os" = "darwin" ] && [ "$arch" = "amd64" ]; then
47+
echo "error: only darwin/arm64 (Apple Silicon) is published for macOS; build from source for Intel Macs" >&2
48+
exit 1
49+
fi
50+
51+
if [ "$VERSION" = "latest" ]; then
52+
VERSION=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" \
53+
| grep '"tag_name"' | head -n1 | cut -d '"' -f4)
54+
if [ -z "$VERSION" ]; then
55+
echo "error: could not resolve the latest release tag" >&2
56+
exit 1
57+
fi
58+
fi
59+
60+
asset="${BINARY}-${os}-${arch}"
61+
base_url="https://github.com/$REPO/releases/download/${VERSION}"
62+
63+
tmp=$(mktemp -d)
64+
trap 'rm -rf "$tmp"' EXIT
65+
66+
echo "Downloading $asset ($VERSION)..."
67+
curl -fsSL "$base_url/$asset" -o "$tmp/$asset"
68+
69+
if curl -fsSL "$base_url/SHA256SUMS" -o "$tmp/SHA256SUMS" 2>/dev/null; then
70+
expected=$(grep " ${asset}\$" "$tmp/SHA256SUMS" | awk '{print $1}')
71+
if [ -n "$expected" ]; then
72+
if command -v sha256sum >/dev/null 2>&1; then
73+
actual=$(sha256sum "$tmp/$asset" | awk '{print $1}')
74+
else
75+
actual=$(shasum -a 256 "$tmp/$asset" | awk '{print $1}')
76+
fi
77+
if [ "$expected" != "$actual" ]; then
78+
echo "error: checksum mismatch for $asset (expected $expected, got $actual)" >&2
79+
exit 1
80+
fi
81+
echo "Checksum verified."
82+
fi
83+
fi
84+
85+
chmod +x "$tmp/$asset"
86+
87+
install_path="$INSTALL_DIR/$BINARY"
88+
if [ -w "$INSTALL_DIR" ]; then
89+
mv "$tmp/$asset" "$install_path"
90+
else
91+
echo "Elevated permissions needed to write to $INSTALL_DIR"
92+
sudo mv "$tmp/$asset" "$install_path"
93+
fi
94+
95+
echo "Installed $BINARY $VERSION to $install_path"
96+
"$install_path" version || true

0 commit comments

Comments
 (0)