Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,15 @@ jobs:
- name: Build TypeScript
run: npm run build

- name: Create esbuild bundle
run: |
node scripts/build-bundle.mjs
echo "=== Bundle size ==="
ls -lh release/awf-bundle.js
echo "=== Bundle smoke test ==="
node release/awf-bundle.js --version
node release/awf-bundle.js --help | head -5
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Piping --help output into head -5 can intermittently fail the step due to EPIPE (the consumer exits early while Node is still writing). To keep the smoke test reliable, redirect help output to a file (or /dev/null) and then head the file, or otherwise avoid truncating via a pipe that closes early.

Suggested change
node release/awf-bundle.js --help | head -5
node release/awf-bundle.js --help > awf-help.txt
head -5 awf-help.txt

Copilot uses AI. Check for mistakes.

- name: Install pkg for binary creation
run: npm install -g pkg

Expand Down Expand Up @@ -550,6 +559,7 @@ jobs:
release/awf-linux-arm64
release/awf-darwin-x64
release/awf-darwin-arm64
release/awf-bundle.js
release/awf.tgz
release/checksums.txt
env:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ _codeql_detected_source_root

# Design docs (working drafts, not checked in)
design-docs/

# Release build artifacts
release/
114 changes: 83 additions & 31 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ REPO="github/gh-aw-firewall"
BINARY_NAME="" # Set dynamically by check_platform
INSTALL_DIR="/usr/local/bin"
INSTALL_NAME="awf"
USE_BUNDLE=false # Set by check_node

# Colors for output
RED='\033[0;31m'
Expand Down Expand Up @@ -142,6 +143,28 @@ check_platform() {
info "Detected platform: $os $arch (binary: $BINARY_NAME)"
}

# Check for Node.js >= 20.12.0 to decide between bundle and pkg binary
# (matches engines.node requirement in package.json)
check_node() {
if [ "${AWF_FORCE_BINARY:-}" = "1" ]; then
info "AWF_FORCE_BINARY=1 set, using standalone binary"
USE_BUNDLE=false
return
fi
if command -v node &> /dev/null; then
NODE_VERSION=$(node -v | sed 's/^v//')
NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1)
NODE_MINOR=$(echo "$NODE_VERSION" | cut -d. -f2)
if [ "$NODE_MAJOR" -gt 20 ] || { [ "$NODE_MAJOR" -eq 20 ] && [ "$NODE_MINOR" -ge 12 ]; }; then
info "Node.js v${NODE_VERSION} detected (>= 20.12.0), using lightweight bundle"
USE_BUNDLE=true
return
fi
warn "Node.js v${NODE_VERSION} detected but < 20.12.0, using standalone binary"
Comment on lines +154 to +163
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_node runs under sudo/root (the script exits if not root). On environments where sudo resets PATH (common in CI and when Node is installed via nvm/asdf or GitHub Actions toolcache), command -v node may fail even though Node is available to the invoking user, causing the installer to incorrectly pick the pkg binary. Consider detecting Node using the original user context (e.g., if $SUDO_USER is set, probe via sudo -u "$SUDO_USER" -H command -v node / node -v), or restructure so Node detection happens before privilege escalation.

Copilot uses AI. Check for mistakes.
fi
USE_BUNDLE=false
}

# Validate version format (should be like v1.0.0, v1.2.3, etc.)
validate_version() {
local version="$1"
Expand Down Expand Up @@ -263,54 +286,83 @@ main() {
check_sudo
check_requirements
check_platform

check_node

# Get version (from argument, env var, or fetch latest)
set_version "$1"

# Create temp directory with prefix for identification
# mktemp creates secure temporary directories with proper permissions (0700)
TEMP_DIR=$(mktemp -d -t awf-install.XXXXXX)

# Validate temp directory was created
if [ -z "$TEMP_DIR" ] || [ ! -d "$TEMP_DIR" ]; then
error "Failed to create temporary directory"
exit 1
fi

# Set up cleanup trap (mktemp already ensures secure location)
trap 'rm -rf "$TEMP_DIR"' EXIT

# Download URLs
BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}"
BINARY_URL="${BASE_URL}/${BINARY_NAME}"
CHECKSUMS_URL="${BASE_URL}/checksums.txt"

# Download binary and checksums
download_file "$BINARY_URL" "$TEMP_DIR/$BINARY_NAME"
download_file "$CHECKSUMS_URL" "$TEMP_DIR/checksums.txt"

# Verify checksum
verify_checksum "$TEMP_DIR/$BINARY_NAME" "$TEMP_DIR/checksums.txt"

# Make binary executable
chmod +x "$TEMP_DIR/$BINARY_NAME"

# Test if it's a valid executable (ELF on Linux, Mach-O on macOS)
local file_type
file_type=$(file "$TEMP_DIR/$BINARY_NAME")
if echo "$file_type" | grep -q "ELF.*executable"; then
info "Valid Linux ELF executable"
elif echo "$file_type" | grep -q "Mach-O 64-bit"; then
info "Valid macOS Mach-O executable"

if [ "$USE_BUNDLE" = true ]; then
# Lightweight bundle path — requires Node.js >= 20
ASSET_NAME="awf-bundle.js"
ASSET_URL="${BASE_URL}/${ASSET_NAME}"

download_file "$ASSET_URL" "$TEMP_DIR/$ASSET_NAME"
download_file "$CHECKSUMS_URL" "$TEMP_DIR/checksums.txt"

# Verify checksum (reuse BINARY_NAME for the checksum lookup)
BINARY_NAME="$ASSET_NAME"
verify_checksum "$TEMP_DIR/$ASSET_NAME" "$TEMP_DIR/checksums.txt"

# Validate the file starts with the expected shebang
if head -c 20 "$TEMP_DIR/$ASSET_NAME" | grep -q '#!/usr/bin/env node'; then
info "Valid Node.js bundle"
else
error "Downloaded file does not appear to be a valid Node.js bundle"
exit 1
fi

# Make executable and install
chmod +x "$TEMP_DIR/$ASSET_NAME"
info "Installing bundle to $INSTALL_DIR/$INSTALL_NAME..."
mv "$TEMP_DIR/$ASSET_NAME" "$INSTALL_DIR/$INSTALL_NAME"
else
error "Downloaded file is not a valid executable: $file_type"
exit 1
# Standalone pkg binary path
BINARY_URL="${BASE_URL}/${BINARY_NAME}"

# Download binary and checksums
download_file "$BINARY_URL" "$TEMP_DIR/$BINARY_NAME"
download_file "$CHECKSUMS_URL" "$TEMP_DIR/checksums.txt"

# Verify checksum
verify_checksum "$TEMP_DIR/$BINARY_NAME" "$TEMP_DIR/checksums.txt"

# Make binary executable
chmod +x "$TEMP_DIR/$BINARY_NAME"

# Test if it's a valid executable (ELF on Linux, Mach-O on macOS)
local file_type
file_type=$(file "$TEMP_DIR/$BINARY_NAME")
if echo "$file_type" | grep -q "ELF.*executable"; then
info "Valid Linux ELF executable"
elif echo "$file_type" | grep -q "Mach-O 64-bit"; then
info "Valid macOS Mach-O executable"
else
error "Downloaded file is not a valid executable: $file_type"
exit 1
fi

# Install binary
info "Installing to $INSTALL_DIR/$INSTALL_NAME..."
mv "$TEMP_DIR/$BINARY_NAME" "$INSTALL_DIR/$INSTALL_NAME"
fi

# Install binary
info "Installing to $INSTALL_DIR/$INSTALL_NAME..."
mv "$TEMP_DIR/$BINARY_NAME" "$INSTALL_DIR/$INSTALL_NAME"


# Verify installation
if [ -x "$INSTALL_DIR/$INSTALL_NAME" ]; then
info "Installation successful! ✓"
Expand Down
Loading
Loading