From e9ccd4b40630d55b46c5bda9752eeac418585d96 Mon Sep 17 00:00:00 2001 From: Greg Pstrucha <875316+Gricha@users.noreply.github.com> Date: Tue, 6 Jan 2026 06:49:22 +0000 Subject: [PATCH 1/3] Add binary distribution via curl install script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add install.sh for curl-based installation - Add scripts/build-binaries.ts for cross-platform compilation - Update release workflow with parallel binary builds (5 platforms) - Binaries uploaded to GitHub Releases on tag push - Update docs and README to use curl install method - Update checker now queries GitHub releases API - Web UI assets installed to ~/.perry/web Installation: curl -fsSL https://raw.githubusercontent.com/gricha/perry/main/install.sh | bash 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/release.yml | 113 +++++++++++++++++++ README.md | 4 +- TODO.md | 59 +++++++++- docs/docs/installation.md | 20 +++- install.sh | 207 ++++++++++++++++++++++++++++++++++ scripts/build-binaries.ts | 80 +++++++++++++ src/agent/static.ts | 24 ++-- src/update-checker.ts | 25 ++-- 8 files changed, 510 insertions(+), 22 deletions(-) create mode 100755 install.sh create mode 100755 scripts/build-binaries.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 852a6be1..9ff5b61e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -167,3 +167,116 @@ jobs: - name: Publish to npm run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + binaries: + strategy: + fail-fast: false + matrix: + include: + - target: bun-linux-x64 + name: linux-x64 + archive: tar.gz + - target: bun-linux-arm64 + name: linux-arm64 + archive: tar.gz + - target: bun-darwin-x64 + name: darwin-x64 + archive: tar.gz + - target: bun-darwin-arm64 + name: darwin-arm64 + archive: tar.gz + - target: bun-windows-x64 + name: windows-x64 + archive: zip + + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + + steps: + - uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: | + bun install + cd web && bun install + + - name: Set version from tag + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/v} + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Build TypeScript and Web UI + run: | + bun run build:ts + bun run build:web + + - name: Compile binary + run: | + mkdir -p dist-binaries + BINARY_NAME="perry" + if [[ "${{ matrix.name }}" == windows-* ]]; then + BINARY_NAME="perry.exe" + fi + bun build ./src/index.ts --compile --target=${{ matrix.target }} --minify --outfile=dist-binaries/$BINARY_NAME + + - name: Create archive + run: | + VERSION=${{ steps.version.outputs.version }} + ARCHIVE_DIR="perry-${VERSION}-${{ matrix.name }}" + mkdir -p "$ARCHIVE_DIR" + + if [[ "${{ matrix.name }}" == windows-* ]]; then + cp dist-binaries/perry.exe "$ARCHIVE_DIR/" + else + cp dist-binaries/perry "$ARCHIVE_DIR/" + fi + cp -r dist/agent/web "$ARCHIVE_DIR/" + + if [[ "${{ matrix.archive }}" == "tar.gz" ]]; then + tar -czvf "dist-binaries/perry-${VERSION}-${{ matrix.name }}.tar.gz" "$ARCHIVE_DIR" + else + zip -r "dist-binaries/perry-${VERSION}-${{ matrix.name }}.zip" "$ARCHIVE_DIR" + fi + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: binary-${{ matrix.name }} + path: dist-binaries/perry-*.* + retention-days: 1 + + release: + needs: binaries + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist-binaries + pattern: binary-* + merge-multiple: true + + - name: Generate checksums + run: | + cd dist-binaries + sha256sum perry-*.tar.gz perry-*.zip > checksums.txt + cat checksums.txt + + - name: Upload to GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: | + dist-binaries/*.tar.gz + dist-binaries/*.zip + dist-binaries/checksums.txt + fail_on_unmatched_files: true diff --git a/README.md b/README.md index daed7eed..9dafb7da 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

Tests - npm version + Release License: MIT

@@ -29,7 +29,7 @@ Perry is designed to run on a machine within a **secure private network** such a ### Install ```bash -npm install -g @gricha/perry +curl -fsSL https://raw.githubusercontent.com/gricha/perry/main/install.sh | bash ``` ### Start Agent diff --git a/TODO.md b/TODO.md index 17052329..ba831b46 100644 --- a/TODO.md +++ b/TODO.md @@ -16,7 +16,64 @@ ## Tasks -*No active tasks* +### Binary Distribution via Curl Install Script + +Switch from npm-based distribution to standalone binary distribution with curl install script (like OpenCode/Claude Code). + +**Benefits:** +- No runtime dependency (currently requires Bun installed globally) +- Single binary, faster cold starts with bytecode compilation +- Simpler: `curl -fsSL https://raw.githubusercontent.com/gricha/perry/main/install.sh | bash` + +#### Phase 1: Binary Build System + +- [ ] Create binary build script using Bun's `--compile` flag for all platforms: + - `perry-linux-x64` (glibc) + - `perry-linux-arm64` (glibc) + - `perry-darwin-x64` (Intel Mac) + - `perry-darwin-arm64` (Apple Silicon) + - `perry-windows-x64.exe` +- [ ] Use `--minify --bytecode` flags for optimized binaries +- [ ] Handle web UI assets embedding (investigate Bun file embedding) +- [ ] Add `build:binaries` script to package.json +- [ ] Test compiled binary runs basic commands locally + +#### Phase 2: Install Script + +- [ ] Create `install.sh` at repository root with: + - Platform detection (Darwin/Linux via uname) + - Architecture detection (x64/arm64) + - GitHub releases API to fetch latest version + - Download binary from GitHub releases + - Install to `$HOME/.perry/bin` (or `$PERRY_INSTALL_DIR`) + - PATH modification (.bashrc, .zshrc, config.fish, .profile) + - Post-install verification (`perry --version`) +- [ ] Support `--version` flag for specific version install +- [ ] Support `--no-modify-path` flag +- [ ] GitHub Actions detection (add to `$GITHUB_PATH`) + +#### Phase 3: Release Workflow + +- [ ] Add `binaries` job to `.github/workflows/release.yml`: + - Cross-compile for all targets using Bun + - Create archives (tar.gz for Linux/macOS, zip for Windows) + - Upload to GitHub Releases + - Generate SHA256 checksums +- [ ] Keep npm publish as alternative install method + +#### Phase 4: Update Checker + +- [ ] Modify `src/update-checker.ts`: + - Query GitHub releases API instead of npm registry + - Update upgrade message to show curl command +- [ ] (Optional) Add `perry upgrade` self-update command + +#### Phase 5: Documentation + +- [ ] Update `docs/docs/installation.md` with curl install as primary method +- [ ] Update README.md +- [ ] Document manual download from GitHub Releases +- [ ] Document uninstall process --- diff --git a/docs/docs/installation.md b/docs/docs/installation.md index fb6e9eb2..e33359e0 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -7,16 +7,28 @@ sidebar_position: 2 ## Prerequisites - Docker -- Node.js 18+ or Bun - SSH client -## Install +## Quick Install (Recommended) ```bash -npm install -g @gricha/perry +curl -fsSL https://raw.githubusercontent.com/gricha/perry/main/install.sh | bash ``` -From source: +This downloads and installs the pre-built binary for your platform to `~/.perry/bin`. + +### Options + +```bash +# Install specific version +curl -fsSL https://raw.githubusercontent.com/gricha/perry/main/install.sh | bash -s -- --version 0.1.8 + +# Don't modify PATH +curl -fsSL https://raw.githubusercontent.com/gricha/perry/main/install.sh | bash -s -- --no-modify-path +``` + +## From Source + ```bash git clone https://github.com/gricha/perry.git cd perry diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..292cb14f --- /dev/null +++ b/install.sh @@ -0,0 +1,207 @@ +#!/bin/bash +set -euo pipefail + +REPO="gricha/perry" +INSTALL_DIR="${PERRY_INSTALL_DIR:-${XDG_BIN_HOME:-$HOME/.perry}}" +BIN_DIR="$INSTALL_DIR/bin" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +info() { echo -e "${BLUE}==>${NC} $1"; } +success() { echo -e "${GREEN}==>${NC} $1"; } +warn() { echo -e "${YELLOW}==>${NC} $1"; } +error() { echo -e "${RED}Error:${NC} $1" >&2; exit 1; } + +VERSION="" +NO_MODIFY_PATH=false + +while [[ $# -gt 0 ]]; do + case $1 in + -v|--version) VERSION="$2"; shift 2 ;; + --no-modify-path) NO_MODIFY_PATH=true; shift ;; + -h|--help) + echo "Perry installer" + echo "" + echo "Usage: install.sh [OPTIONS]" + echo "" + echo "Options:" + echo " -v, --version VERSION Install specific version (default: latest)" + echo " --no-modify-path Don't modify shell PATH" + echo " -h, --help Show this help" + exit 0 + ;; + *) error "Unknown option: $1" ;; + esac +done + +detect_platform() { + local os arch + + case "$(uname -s)" in + Linux*) os="linux" ;; + Darwin*) os="darwin" ;; + MINGW*|MSYS*|CYGWIN*) os="windows" ;; + *) error "Unsupported operating system: $(uname -s)" ;; + esac + + case "$(uname -m)" in + x86_64|amd64) arch="x64" ;; + arm64|aarch64) arch="arm64" ;; + *) error "Unsupported architecture: $(uname -m)" ;; + esac + + echo "${os}-${arch}" +} + +get_latest_version() { + local url="https://api.github.com/repos/${REPO}/releases/latest" + local version + + if command -v curl &>/dev/null; then + version=$(curl -fsSL "$url" | grep '"tag_name"' | sed -E 's/.*"v([^"]+)".*/\1/') + elif command -v wget &>/dev/null; then + version=$(wget -qO- "$url" | grep '"tag_name"' | sed -E 's/.*"v([^"]+)".*/\1/') + else + error "curl or wget is required" + fi + + if [[ -z "$version" ]]; then + error "Failed to fetch latest version" + fi + + echo "$version" +} + +download_and_install() { + local version="$1" + local platform="$2" + local archive_ext="tar.gz" + local binary_name="perry" + + if [[ "$platform" == windows-* ]]; then + archive_ext="zip" + binary_name="perry.exe" + fi + + local archive_name="perry-${version}-${platform}.${archive_ext}" + local download_url="https://github.com/${REPO}/releases/download/v${version}/${archive_name}" + + info "Downloading perry v${version} for ${platform}..." + + local tmp_dir + tmp_dir=$(mktemp -d) + trap "rm -rf '$tmp_dir'" EXIT + + local archive_path="$tmp_dir/$archive_name" + + if command -v curl &>/dev/null; then + curl -fsSL --progress-bar "$download_url" -o "$archive_path" || error "Download failed. Check if version v${version} exists." + else + wget -q --show-progress "$download_url" -O "$archive_path" || error "Download failed. Check if version v${version} exists." + fi + + info "Extracting..." + + mkdir -p "$BIN_DIR" + mkdir -p "$INSTALL_DIR/web" + + if [[ "$archive_ext" == "tar.gz" ]]; then + tar -xzf "$archive_path" -C "$tmp_dir" + else + unzip -q "$archive_path" -d "$tmp_dir" + fi + + local extracted_dir="$tmp_dir/perry-${version}-${platform}" + + cp "$extracted_dir/$binary_name" "$BIN_DIR/perry" + chmod +x "$BIN_DIR/perry" + + if [[ -d "$extracted_dir/web" ]]; then + rm -rf "$INSTALL_DIR/web" + cp -r "$extracted_dir/web" "$INSTALL_DIR/web" + fi + + success "Installed perry to $BIN_DIR/perry" +} + +update_path() { + if [[ "$NO_MODIFY_PATH" == "true" ]]; then + return + fi + + if [[ -n "${GITHUB_PATH:-}" ]]; then + echo "$BIN_DIR" >> "$GITHUB_PATH" + info "Added $BIN_DIR to GITHUB_PATH" + return + fi + + local shell_config="" + local path_export="export PATH=\"$BIN_DIR:\$PATH\"" + + if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$SHELL" == */zsh ]]; then + shell_config="$HOME/.zshrc" + elif [[ -n "${BASH_VERSION:-}" ]] || [[ "$SHELL" == */bash ]]; then + if [[ -f "$HOME/.bashrc" ]]; then + shell_config="$HOME/.bashrc" + elif [[ -f "$HOME/.bash_profile" ]]; then + shell_config="$HOME/.bash_profile" + fi + elif [[ "$SHELL" == */fish ]]; then + shell_config="$HOME/.config/fish/config.fish" + path_export="set -gx PATH $BIN_DIR \$PATH" + fi + + if [[ -n "$shell_config" ]]; then + if ! grep -q "$BIN_DIR" "$shell_config" 2>/dev/null; then + echo "" >> "$shell_config" + echo "# Perry" >> "$shell_config" + echo "$path_export" >> "$shell_config" + info "Added $BIN_DIR to PATH in $shell_config" + fi + fi + + for rc in "$HOME/.profile" "$HOME/.bash_profile" "$HOME/.bashrc" "$HOME/.zshrc"; do + if [[ -f "$rc" ]] && [[ "$rc" != "$shell_config" ]]; then + if ! grep -q "$BIN_DIR" "$rc" 2>/dev/null; then + echo "" >> "$rc" + echo "# Perry" >> "$rc" + echo "export PATH=\"$BIN_DIR:\$PATH\"" >> "$rc" + fi + fi + done +} + +main() { + info "Perry installer" + echo "" + + local platform + platform=$(detect_platform) + info "Detected platform: $platform" + + if [[ -z "$VERSION" ]]; then + VERSION=$(get_latest_version) + fi + + download_and_install "$VERSION" "$platform" + update_path + + echo "" + success "Perry v${VERSION} installed successfully!" + echo "" + echo "To get started, run:" + echo "" + + if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then + echo " export PATH=\"$BIN_DIR:\$PATH\"" + fi + + echo " perry --help" + echo "" +} + +main diff --git a/scripts/build-binaries.ts b/scripts/build-binaries.ts new file mode 100755 index 00000000..6c7e9ca6 --- /dev/null +++ b/scripts/build-binaries.ts @@ -0,0 +1,80 @@ +#!/usr/bin/env bun + +import { $ } from 'bun'; +import { mkdir, cp, rm } from 'fs/promises'; +import { join } from 'path'; + +const TARGETS = [ + { target: 'bun-linux-x64', name: 'perry-linux-x64', archive: 'tar.gz' }, + { target: 'bun-linux-arm64', name: 'perry-linux-arm64', archive: 'tar.gz' }, + { target: 'bun-darwin-x64', name: 'perry-darwin-x64', archive: 'tar.gz' }, + { target: 'bun-darwin-arm64', name: 'perry-darwin-arm64', archive: 'tar.gz' }, + { target: 'bun-windows-x64', name: 'perry-windows-x64.exe', archive: 'zip' }, +]; + +const ROOT = join(import.meta.dir, '..'); +const DIST_DIR = join(ROOT, 'dist-binaries'); +const WEB_DIR = join(ROOT, 'dist', 'agent', 'web'); + +async function main() { + const version = process.env.VERSION || (await getVersion()); + console.log(`Building binaries for version ${version}`); + + await rm(DIST_DIR, { recursive: true, force: true }); + await mkdir(DIST_DIR, { recursive: true }); + + await $`bun run build:ts`.cwd(ROOT); + await $`bun run build:web`.cwd(ROOT); + + for (const { target, name, archive } of TARGETS) { + console.log(`\nBuilding ${name}...`); + + const stagingDir = join(DIST_DIR, `perry-${version}-${target.replace('bun-', '')}`); + await mkdir(stagingDir, { recursive: true }); + + const binaryPath = join(stagingDir, name.replace('.exe', '') + (name.endsWith('.exe') ? '.exe' : '')); + + await $`bun build ./src/index.ts --compile --target=${target} --minify --outfile=${binaryPath}`.cwd(ROOT); + + await cp(WEB_DIR, join(stagingDir, 'web'), { recursive: true }); + + const archiveName = + archive === 'tar.gz' + ? `perry-${version}-${target.replace('bun-', '')}.tar.gz` + : `perry-${version}-${target.replace('bun-', '')}.zip`; + + const archivePath = join(DIST_DIR, archiveName); + + if (archive === 'tar.gz') { + await $`tar -czvf ${archivePath} -C ${DIST_DIR} ${`perry-${version}-${target.replace('bun-', '')}`}`; + } else { + await $`zip -r ${archivePath} ${`perry-${version}-${target.replace('bun-', '')}`}`.cwd(DIST_DIR); + } + + console.log(` Created ${archiveName}`); + } + + await generateChecksums(version); + + console.log(`\nBuild complete! Archives in ${DIST_DIR}`); +} + +async function getVersion(): Promise { + const pkg = await Bun.file(join(ROOT, 'package.json')).json(); + return pkg.version; +} + +async function generateChecksums(version: string) { + console.log('\nGenerating checksums...'); + const checksumFile = join(DIST_DIR, `perry-${version}-checksums.txt`); + + const result = await $`sha256sum perry-${version}-*.tar.gz perry-${version}-*.zip 2>/dev/null || shasum -a 256 perry-${version}-*.tar.gz perry-${version}-*.zip`.cwd(DIST_DIR).text(); + + await Bun.write(checksumFile, result); + console.log(` Created perry-${version}-checksums.txt`); +} + +main().catch((err) => { + console.error('Build failed:', err); + process.exit(1); +}); diff --git a/src/agent/static.ts b/src/agent/static.ts index ba84cf0e..c139b963 100644 --- a/src/agent/static.ts +++ b/src/agent/static.ts @@ -1,6 +1,7 @@ import { IncomingMessage, ServerResponse } from 'http'; import { promises as fs } from 'fs'; import path from 'path'; +import { homedir } from 'os'; const MIME_TYPES: Record = { '.html': 'text/html', @@ -20,15 +21,24 @@ const MIME_TYPES: Record = { }; function getWebDir(): string { - const distWeb = path.join(__dirname, 'web'); - const rootDistWeb = path.resolve(__dirname, '../../dist/agent/web'); + const candidates = [ + path.join(__dirname, 'web'), + path.resolve(__dirname, '../../dist/agent/web'), + path.join(path.dirname(process.execPath), 'web'), + path.join(path.dirname(process.argv[0]), 'web'), + path.join(homedir(), '.perry', 'web'), + ]; - try { - require('fs').accessSync(path.join(distWeb, 'index.html')); - return distWeb; - } catch { - return rootDistWeb; + for (const dir of candidates) { + try { + require('fs').accessSync(path.join(dir, 'index.html')); + return dir; + } catch { + continue; + } } + + return candidates[0]; } export async function serveStatic( diff --git a/src/update-checker.ts b/src/update-checker.ts index a9bea0f3..f6aeacba 100644 --- a/src/update-checker.ts +++ b/src/update-checker.ts @@ -2,7 +2,7 @@ import { homedir } from 'os'; import { join } from 'path'; import { mkdir, readFile, writeFile } from 'fs/promises'; -const PACKAGE_NAME = '@gricha/perry'; +const GITHUB_REPO = 'gricha/perry'; const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours interface UpdateCache { @@ -37,12 +37,17 @@ async function writeCache(cache: UpdateCache): Promise { async function fetchLatestVersion(): Promise { try { - const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { + const response = await fetch(`https://api.github.com/repos/${GITHUB_REPO}/releases/latest`, { signal: AbortSignal.timeout(3000), + headers: { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'perry-update-checker', + }, }); if (!response.ok) return null; - const data = (await response.json()) as { version?: string }; - return data.version || null; + const data = (await response.json()) as { tag_name?: string }; + const tag = data.tag_name || null; + return tag ? tag.replace(/^v/, '') : null; } catch { return null; } @@ -77,14 +82,18 @@ export async function checkForUpdates(currentVersion: string): Promise { if (latestVersion && compareVersions(currentVersion, latestVersion) > 0) { console.log(''); - console.log(`\x1b[33m╭─────────────────────────────────────────────────────────╮\x1b[0m`); console.log( - `\x1b[33m│\x1b[0m Update available: \x1b[90m${currentVersion}\x1b[0m → \x1b[32m${latestVersion}\x1b[0m \x1b[33m│\x1b[0m` + `\x1b[33m╭──────────────────────────────────────────────────────────────────────────────────╮\x1b[0m` ); console.log( - `\x1b[33m│\x1b[0m Run \x1b[36mnpm install -g ${PACKAGE_NAME}\x1b[0m to update \x1b[33m│\x1b[0m` + `\x1b[33m│\x1b[0m Update available: \x1b[90m${currentVersion}\x1b[0m → \x1b[32m${latestVersion}\x1b[0m \x1b[33m│\x1b[0m` + ); + console.log( + `\x1b[33m│\x1b[0m Run: \x1b[36mcurl -fsSL https://raw.githubusercontent.com/${GITHUB_REPO}/main/install.sh | bash\x1b[0m \x1b[33m│\x1b[0m` + ); + console.log( + `\x1b[33m╰──────────────────────────────────────────────────────────────────────────────────╯\x1b[0m` ); - console.log(`\x1b[33m╰─────────────────────────────────────────────────────────╯\x1b[0m`); console.log(''); } } catch { From 1458c2d634779637ac0ab2df7b19699ef4b618fb Mon Sep 17 00:00:00 2001 From: Greg Pstrucha <875316+Gricha@users.noreply.github.com> Date: Tue, 6 Jan 2026 06:49:45 +0000 Subject: [PATCH 2/3] Add binary compilation test to CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validates that: - Binary compiles successfully - Binary runs --version and --help - Binary works with web assets in expected location 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/test.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb10adf3..6b43a81c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,6 +82,39 @@ jobs: path: dist/ retention-days: 1 + binary: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Compile binary + run: | + bun build ./src/index.ts --compile --minify --outfile=perry-test + + - name: Test binary runs + run: | + ./perry-test --version + ./perry-test --help + + - name: Test binary with web assets + run: | + mkdir -p test-install/bin + cp perry-test test-install/bin/perry + cp -r dist/agent/web test-install/ + ./test-install/bin/perry --version + test: runs-on: ubuntu-latest needs: build From 434a175000684aecc4cca14170b6cdac36cfb2bc Mon Sep 17 00:00:00 2001 From: Greg Pstrucha <875316+Gricha@users.noreply.github.com> Date: Tue, 6 Jan 2026 06:57:34 +0000 Subject: [PATCH 3/3] Fix binary CI job: add missing bun install --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b43a81c..8e735bc3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -99,6 +99,9 @@ jobs: with: bun-version: latest + - name: Install dependencies + run: bun install + - name: Compile binary run: | bun build ./src/index.ts --compile --minify --outfile=perry-test