Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/workflows/src/defaults/bundled-defaults.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, it, expect } from 'bun:test';
import {
isBinaryBuild,
isBunVirtualFs,
isCompiledExecPath,
BUNDLED_COMMANDS,
BUNDLED_WORKFLOWS,
} from './bundled-defaults';
Expand Down Expand Up @@ -30,6 +31,30 @@ describe('bundled-defaults', () => {
});
});

describe('isCompiledExecPath', () => {
it('should return false for the bun interpreter', () => {
expect(isCompiledExecPath('/usr/local/bin/bun')).toBe(false);
expect(isCompiledExecPath('C:\\Users\\me\\.bun\\bin\\bun.exe')).toBe(false);
expect(isCompiledExecPath('bun')).toBe(false);
});

it('should return false for the node interpreter', () => {
expect(isCompiledExecPath('/usr/bin/node')).toBe(false);
expect(isCompiledExecPath('C:\\Program Files\\nodejs\\node.exe')).toBe(false);
});

it('should return true for a standalone compiled binary', () => {
expect(isCompiledExecPath('/usr/local/bin/archon')).toBe(true);
expect(isCompiledExecPath('/usr/local/bin/archon-linux-x64')).toBe(true);
expect(isCompiledExecPath('C:\\Program Files\\Archon\\archon.exe')).toBe(true);
});

it('should return false for empty or undefined execPath', () => {
expect(isCompiledExecPath('')).toBe(false);
expect(isCompiledExecPath(undefined)).toBe(false);
});
});

describe('isBinaryBuild', () => {
it('should return false when running in test environment (not compiled)', () => {
// The true path requires an actual compiled binary (import.meta.dir points to
Expand Down
24 changes: 23 additions & 1 deletion packages/workflows/src/defaults/bundled-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,29 @@ export function isBunVirtualFs(dir: string): boolean {
*
* Note: `process.versions.bun` is still set in compiled binaries as of Bun 1.3.5,
* so we use the virtual filesystem path prefix for detection instead.
*
* Detection signals (any one is sufficient):
* 1. `import.meta.dir` lives under Bun's virtual FS (`/$bunfs/` or `B:\~BUN\`).
* Works for ESM/JS compiled binaries.
* 2. `process.execPath` is not the bun/node interpreter. Works for compiled
* CJS bytecode binaries (`bun build --compile --bytecode`) where
* `import.meta.dir` may be undefined or empty.
*/
export function isBinaryBuild(): boolean {
return isBunVirtualFs(import.meta.dir);
if (isBunVirtualFs(import.meta.dir ?? '')) {
return true;
}
return isCompiledExecPath(process.execPath);
}

/**
* Check whether `execPath` looks like a standalone compiled binary rather than
* the `bun` or `node` interpreter. Exported for testability.
*/
export function isCompiledExecPath(execPath: string | undefined): boolean {
if (!execPath) return false;
const base = execPath.split(/[/\\]/).pop() ?? '';
// Strip extension on Windows (e.g. archon.exe → archon)
const withoutExt = base.replace(/\.exe$/i, '').toLowerCase();
return withoutExt !== 'bun' && withoutExt !== 'node';
}
101 changes: 101 additions & 0 deletions scripts/install-local.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# scripts/install-local.ps1
# LOCAL TEST harness for install.ps1 — installs from a file on disk instead of
# downloading from GitHub Releases. Used to validate the install flow against
# a binary built from the current branch before cutting a release.
#
# Usage (from repo root):
# # 1. Build a Windows binary first:
# bun build --compile --outfile dist\archon-windows-x64.exe packages\cli\src\cli.ts
#
# # 2. Run this script:
# powershell -ExecutionPolicy Bypass -File .\scripts\install-local.ps1
#
# Options (env vars):
# $env:LOCAL_BINARY - Path to local binary (default: .\dist\archon-windows-x64.exe)
# $env:INSTALL_DIR - Install dir (default: $env:USERPROFILE\.archon-test\bin)
# $env:SKIP_PATH - Set to "true" to NOT modify user PATH

#Requires -Version 5.1
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$REPO_ROOT = Split-Path -Parent $PSScriptRoot
$LOCAL_BINARY = if ($env:LOCAL_BINARY) { $env:LOCAL_BINARY } else { Join-Path $REPO_ROOT 'dist\archon-windows-x64.exe' }
$INSTALL_DIR = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { "$env:USERPROFILE\.archon-test\bin" }
$SKIP_PATH = ($env:SKIP_PATH -eq 'true')
$BINARY_NAME = 'archon'

function Write-Info { param([string]$Msg) Write-Host "[INFO] $Msg" -ForegroundColor Cyan }
function Write-Warn { param([string]$Msg) Write-Host "[WARN] $Msg" -ForegroundColor Yellow }
function Write-Err { param([string]$Msg) Write-Host "[ERROR] $Msg" -ForegroundColor Red }
function Write-Ok { param([string]$Msg) Write-Host "[OK] $Msg" -ForegroundColor Green }

Write-Host ""
Write-Host " +---------------------------------------+" -ForegroundColor Cyan
Write-Host " | Archon CLI Installer (LOCAL TEST) |" -ForegroundColor Cyan
Write-Host " +---------------------------------------+" -ForegroundColor Cyan
Write-Host ""

# --- Locate local binary ---
if (-not (Test-Path $LOCAL_BINARY)) {
Write-Err "Local binary not found: $LOCAL_BINARY"
Write-Err "Build it first with:"
Write-Err " bun build --compile --outfile dist\archon-windows-x64.exe packages\cli\src\cli.ts"
exit 1
}
$size = (Get-Item $LOCAL_BINARY).Length
Write-Ok "Found local binary: $LOCAL_BINARY ($size bytes)"

# --- Compute checksum (informational only — no verification against a remote file) ---
$hash = (Get-FileHash -Path $LOCAL_BINARY -Algorithm SHA256).Hash.ToLower()
Write-Info "SHA256: $hash"

# --- Create install directory ---
if (-not (Test-Path $INSTALL_DIR)) {
Write-Info "Creating install directory: $INSTALL_DIR"
New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
}

# --- Copy binary ---
$destBinary = Join-Path $INSTALL_DIR "$BINARY_NAME.exe"
Write-Info "Installing to $destBinary..."
Copy-Item -Path $LOCAL_BINARY -Destination $destBinary -Force
Write-Ok "Installed to $destBinary"

# --- Add to PATH (optional) ---
if (-not $SKIP_PATH) {
$currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
$pathParts = $currentPath -split ';' | Where-Object { $_ -ne '' }
if ($INSTALL_DIR -notin $pathParts) {
$pathParts += $INSTALL_DIR
[Environment]::SetEnvironmentVariable('Path', ($pathParts -join ';'), 'User')
Write-Ok "Added $INSTALL_DIR to user PATH (open a NEW terminal for it to take effect)"
} else {
Write-Info "$INSTALL_DIR already in user PATH"
}
} else {
Write-Info "Skipping PATH modification (SKIP_PATH=true)"
}

# --- Verify ---
Write-Host ""
Write-Info "Verifying installation (running '$destBinary version')..."
try {
$output = & $destBinary version 2>&1
Write-Host $output
if ($LASTEXITCODE -ne 0) {
Write-Err "Binary exited with code $LASTEXITCODE"
exit 1
}
Write-Ok "Installation complete!"
} catch {
Write-Err "Binary failed to run: $($_.Exception.Message)"
exit 1
}

Write-Host ""
Write-Host " Cleanup when done testing:" -ForegroundColor Yellow
Write-Host " `$p = [Environment]::GetEnvironmentVariable('Path','User') -split ';' | Where-Object { `$_ -ne '$INSTALL_DIR' }"
Write-Host " [Environment]::SetEnvironmentVariable('Path', (`$p -join ';'), 'User')"
Write-Host " Remove-Item -Recurse -Force '$INSTALL_DIR'"
Write-Host ""
117 changes: 117 additions & 0 deletions scripts/install-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env bash
# scripts/install-local.sh
# LOCAL TEST harness for install.sh — installs from a file on disk instead of
# downloading from GitHub Releases. Used to validate the install flow against
# a binary built from the current branch before cutting a release.
#
# Usage:
# # On your dev machine, cross-compile for linux:
# bun build --compile --target=bun-linux-x64 --outfile dist/archon-linux-x64 packages/cli/src/cli.ts
# scp dist/archon-linux-x64 user@vm:/tmp/archon-linux-x64
# scp scripts/install-local.sh user@vm:/tmp/install-local.sh
#
# # On the Linux VM:
# LOCAL_BINARY=/tmp/archon-linux-x64 bash /tmp/install-local.sh
#
# Options (env vars):
# LOCAL_BINARY - Path to local binary (REQUIRED)
# INSTALL_DIR - Install dir (default: /usr/local/bin, falls back to ~/.local/bin if not writable)
# SKIP_SUDO - Set to "true" to never use sudo (force ~/.local/bin)

set -euo pipefail

LOCAL_BINARY="${LOCAL_BINARY:-}"
INSTALL_DIR="${INSTALL_DIR:-}"
SKIP_SUDO="${SKIP_SUDO:-false}"
BINARY_NAME="archon"

c_info() { printf '\033[36m[INFO]\033[0m %s\n' "$*"; }
c_ok() { printf '\033[32m[OK]\033[0m %s\n' "$*"; }
c_warn() { printf '\033[33m[WARN]\033[0m %s\n' "$*"; }
c_err() { printf '\033[31m[ERROR]\033[0m %s\n' "$*" >&2; }

echo
echo " +---------------------------------------+"
echo " | Archon CLI Installer (LOCAL TEST) |"
echo " +---------------------------------------+"
echo

# --- Locate local binary ---
if [[ -z "$LOCAL_BINARY" ]]; then
c_err "LOCAL_BINARY env var is required"
c_err " Example: LOCAL_BINARY=/tmp/archon-linux-x64 bash $0"
exit 1
fi
if [[ ! -f "$LOCAL_BINARY" ]]; then
c_err "Local binary not found: $LOCAL_BINARY"
exit 1
fi

size=$(stat -c%s "$LOCAL_BINARY" 2>/dev/null || stat -f%z "$LOCAL_BINARY")
c_ok "Found local binary: $LOCAL_BINARY ($size bytes)"

# --- SHA256 (informational) ---
if command -v sha256sum >/dev/null 2>&1; then
hash=$(sha256sum "$LOCAL_BINARY" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
hash=$(shasum -a 256 "$LOCAL_BINARY" | awk '{print $1}')
else
hash="(no sha256 tool available)"
fi
c_info "SHA256: $hash"

# --- Pick install dir ---
if [[ -z "$INSTALL_DIR" ]]; then
if [[ "$SKIP_SUDO" == "true" ]]; then
INSTALL_DIR="$HOME/.local/bin"
elif [[ -w "/usr/local/bin" ]]; then
INSTALL_DIR="/usr/local/bin"
elif command -v sudo >/dev/null 2>&1; then
INSTALL_DIR="/usr/local/bin"
else
INSTALL_DIR="$HOME/.local/bin"
fi
fi
c_info "Install dir: $INSTALL_DIR"

# --- Create install dir ---
SUDO=""
if [[ ! -w "$(dirname "$INSTALL_DIR/.")" ]] && [[ "$SKIP_SUDO" != "true" ]]; then
if command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
c_info "Will use sudo to write to $INSTALL_DIR"
fi
fi
$SUDO mkdir -p "$INSTALL_DIR"

# --- Copy binary ---
DEST="$INSTALL_DIR/$BINARY_NAME"
c_info "Installing to $DEST..."
$SUDO cp "$LOCAL_BINARY" "$DEST"
$SUDO chmod +x "$DEST"
c_ok "Installed to $DEST"

# --- PATH hint ---
case ":$PATH:" in
*":$INSTALL_DIR:"*) : ;;
*)
c_warn "$INSTALL_DIR is NOT in your PATH"
c_warn "Add this to ~/.bashrc or ~/.zshrc:"
c_warn " export PATH=\"$INSTALL_DIR:\$PATH\""
;;
esac

# --- Verify ---
echo
c_info "Verifying installation (running '$DEST version')..."
if "$DEST" version; then
c_ok "Installation complete!"
else
c_err "Binary failed to run (exit $?)"
exit 1
fi

echo
echo " Cleanup when done testing:"
echo " $SUDO rm -f $DEST"
echo
Loading