Skip to content

🤏 Smol Binaries #113

🤏 Smol Binaries

🤏 Smol Binaries #113

Workflow file for this run

name: 🤏 Smol Binaries
on:
workflow_call:
inputs:
force:
description: 'Force rebuild (ignore cache)'
required: false
type: boolean
default: false
build-linux:
description: 'Build Linux smol binaries'
required: false
type: boolean
default: true
build-macos:
description: 'Build macOS smol binaries'
required: false
type: boolean
default: true
build-windows:
description: 'Build Windows smol binaries'
required: false
type: boolean
default: true
workflow_dispatch:
inputs:
force:
description: 'Force rebuild (ignore cache)'
required: false
type: boolean
default: false
build-linux:
description: 'Build Linux smol binaries'
required: false
type: boolean
default: true
build-macos:
description: 'Build macOS smol binaries'
required: false
type: boolean
default: true
build-windows:
description: 'Build Windows smol binaries'
required: false
type: boolean
default: true
# Removed push/pull_request triggers to prevent automatic builds.
# Run manually via workflow_dispatch or via workflow_call from build-socketbin.yml.
permissions:
contents: read
concurrency:
group: build-smol-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-deps:
name: ⚡ Dependencies
runs-on: ubuntu-latest
timeout-minutes: 15
outputs:
deps-hash: ${{ steps.deps-cache-key.outputs.hash }}
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22.11.0
- name: Setup pnpm
uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
with:
version: 10.20.0
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate build-deps cache key
id: deps-cache-key
shell: bash
run: |
# Include pnpm-lock.yaml to detect dependency changes (e.g., @socketsecurity/lib updates).
HASH=$(find packages/bootstrap packages/socket -type f \( -name "*.mts" -o -name "*.ts" -o -name "*.mjs" -o -name "*.js" -o -name "*.json" \) ! -path "*/node_modules/*" ! -path "*/dist/*" ! -path "*/build/*" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
LOCK_HASH=$(sha256sum pnpm-lock.yaml | cut -d' ' -f1)
COMBINED_HASH=$(echo "$HASH-$LOCK_HASH" | sha256sum | cut -d' ' -f1)
echo "hash=$COMBINED_HASH" >> $GITHUB_OUTPUT
- name: Restore build-deps cache
id: deps-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
packages/bootstrap/dist/
packages/socket/dist/
key: build-deps-smol-${{ steps.deps-cache-key.outputs.hash }}
restore-keys: build-deps-smol-
- name: Build bootstrap package
if: steps.deps-cache.outputs.cache-hit != 'true'
run: pnpm --filter @socketsecurity/bootstrap run build
- name: Build socket package bootstrap
if: steps.deps-cache.outputs.cache-hit != 'true'
run: pnpm --filter socket run build
- name: Upload build artifacts
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: build-deps-smol-${{ github.sha }}
path: packages/
retention-days: 1
build-smol:
needs: build-deps
name: ⚡ Smol - ${{ matrix.platform }}-${{ matrix.arch }}
runs-on: ${{ matrix.runner }}
timeout-minutes: ${{ inputs.force && 180 || 150 }}
strategy:
fail-fast: true
max-parallel: 8
matrix:
include:
# Linux builds.
- runner: ubuntu-latest
os: linux
platform: linux
arch: x64
- runner: ubuntu-24.04-arm
os: linux
platform: linux
arch: arm64
# Alpine Linux builds - use musl binaries from unofficial-builds.nodejs.org.
- runner: ubuntu-latest
os: linux
platform: alpine
arch: x64
- runner: ubuntu-24.04-arm
os: linux
platform: alpine
arch: arm64
# macOS builds.
- runner: macos-latest
os: darwin
platform: darwin
arch: x64
- runner: macos-latest
os: darwin
platform: darwin
arch: arm64
# Windows builds.
- runner: windows-latest
os: windows
platform: win32
arch: x64
- runner: windows-latest
os: windows
platform: win32
arch: arm64
steps:
- name: Check if platform is enabled
id: check-platform
shell: bash
run: |
SHOULD_RUN="false"
if [ "${{ matrix.platform }}" = "linux" ] || [ "${{ matrix.platform }}" = "alpine" ]; then
if [ "${{ inputs.build-linux }}" != "false" ]; then
SHOULD_RUN="true"
fi
elif [ "${{ matrix.platform }}" = "darwin" ]; then
if [ "${{ inputs.build-macos }}" != "false" ]; then
SHOULD_RUN="true"
fi
elif [ "${{ matrix.platform }}" = "win32" ]; then
if [ "${{ inputs.build-windows }}" != "false" ]; then
SHOULD_RUN="true"
fi
fi
echo "should-run=$SHOULD_RUN" >> $GITHUB_OUTPUT
if [ "$SHOULD_RUN" = "true" ]; then
echo -e "\033[35m⚡\033[0m Socket building ${{ matrix.platform }}-${{ matrix.arch }}"
else
echo "⊘ Skipping ${{ matrix.platform }}-${{ matrix.arch }} (disabled by inputs)"
fi
- name: Checkout
if: steps.check-platform.outputs.should-run == 'true'
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download build artifacts
if: steps.check-platform.outputs.should-run == 'true'
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: build-deps-smol-${{ github.sha }}
path: packages/
- name: Verify bootstrap artifacts
if: steps.check-platform.outputs.should-run == 'true'
shell: bash
run: |
echo -e "\033[35m⚡\033[0m Verifying Socket bootstrap artifacts"
echo ""
ls -lh packages/bootstrap/dist/bootstrap-smol.js && echo -e "\033[32m✓\033[0m bootstrap-smol.js present" || (echo -e "\033[31m✗\033[0m bootstrap-smol.js missing" && exit 1)
ls -lh packages/socket/dist/bootstrap.js && echo -e "\033[32m✓\033[0m socket bootstrap.js present" || (echo -e "\033[31m✗\033[0m socket bootstrap.js missing" && exit 1)
echo ""
echo -e "\033[35m⚡\033[0m Bootstrap artifacts verified"
- name: Setup Node.js
if: steps.check-platform.outputs.should-run == 'true'
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: 22.11.0
- name: Setup pnpm
if: steps.check-platform.outputs.should-run == 'true'
uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
with:
version: 10.20.0
- name: Install dependencies
if: steps.check-platform.outputs.should-run == 'true'
run: pnpm install --frozen-lockfile
- name: Record build start time
if: steps.check-platform.outputs.should-run == 'true'
id: build-start
shell: bash
run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT
- name: Generate smol build cache key
if: steps.check-platform.outputs.should-run == 'true'
id: smol-cache-key
shell: bash
run: |
PATCHES_HASH=$(find patches packages/node-smol-builder/patches packages/node-smol-builder/additions scripts -type f \( -name "*.patch" -o -name "*.template.patch" -o -name "*.mjs" -o -name "*.template.mjs" -o -name "*.h" -o -name "*.c" -o -name "*.cc" \) | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
# Include bootstrap/socket dependencies in cache key to invalidate when they change.
COMBINED_HASH=$(echo "$PATCHES_HASH-${{ needs.build-deps.outputs.deps-hash }}" | sha256sum | cut -d' ' -f1)
echo "hash=$COMBINED_HASH" >> $GITHUB_OUTPUT
- name: Setup ccache (Linux/macOS)
if: matrix.os != 'windows'
uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14
with:
key: build-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.hash }}
max-size: 2G
- name: Restore smol build cache
if: inputs.force != true
id: smol-build-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: packages/node-smol-builder/build
key: node-smol-build-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.hash }}
restore-keys: |
node-smol-build-${{ matrix.platform }}-${{ matrix.arch }}-
- name: Restore smol stripped binary cache
if: inputs.force != true
id: smol-stripped-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: packages/node-smol-builder/build/out/Stripped
key: node-smol-stripped-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.hash }}
restore-keys: node-smol-stripped-${{ matrix.platform }}-${{ matrix.arch }}-
- name: Restore smol binary cache
if: inputs.force != true
id: smol-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: packages/node-smol-builder/dist/socket-smol-${{ matrix.platform }}-${{ matrix.arch }}
key: node-smol-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.hash }}
restore-keys: node-smol-${{ matrix.platform }}-${{ matrix.arch }}-
- name: Validate build cache integrity
if: steps.check-platform.outputs.should-run == 'true' && steps.smol-build-cache.outputs.cache-hit == 'true'
shell: bash
run: |
CACHE_VALID="true"
# Check checkpoint files exist.
if [ ! -f "packages/node-smol-builder/build/.checkpoints/cloned" ]; then
echo -e "\033[33m⚠\033[0m Missing 'cloned' checkpoint"
CACHE_VALID="false"
fi
# If final binary exists, all checkpoints should exist.
BINARY_PATH="packages/node-smol-builder/dist/socket-smol-${{ matrix.platform }}-${{ matrix.arch }}"
if [ "${{ matrix.os }}" = "windows" ]; then
BINARY_PATH="${BINARY_PATH}.exe"
fi
if [ -f "$BINARY_PATH" ]; then
for checkpoint in cloned built complete; do
if [ ! -f "packages/node-smol-builder/build/.checkpoints/$checkpoint" ]; then
echo -e "\033[31m✗\033[0m Incomplete cache: missing $checkpoint checkpoint but binary exists"
CACHE_VALID="false"
fi
done
fi
# Invalidate corrupted cache.
if [ "$CACHE_VALID" = "false" ]; then
echo "::warning::Invalidating corrupted build cache..."
rm -rf packages/node-smol-builder/build
else
echo -e "\033[32m✓\033[0m Build cache integrity validated"
fi
- name: Verify smol binary cache
if: steps.check-platform.outputs.should-run == 'true'
id: smol-cache-valid
shell: bash
run: |
BINARY_PATH="packages/node-smol-builder/dist/socket-smol-${{ matrix.platform }}-${{ matrix.arch }}"
if [ "${{ matrix.os }}" = "windows" ]; then
BINARY_PATH="${BINARY_PATH}.exe"
fi
if [ -f "$BINARY_PATH" ]; then
echo "valid=true" >> $GITHUB_OUTPUT
echo -e "\033[35m⚡\033[0m Socket smol binary cached: $BINARY_PATH"
# Smoke test: verify binary can execute --version.
if "$BINARY_PATH" --version >/dev/null 2>&1; then
echo -e "\033[32m✓\033[0m Smoke test passed (--version)"
else
echo "::warning::Binary exists but failed smoke test, rebuilding"
echo "valid=false" >> $GITHUB_OUTPUT
rm -f "$BINARY_PATH"
fi
else
echo "valid=false" >> $GITHUB_OUTPUT
echo -e "\033[31m✗\033[0m Socket smol binary cache miss: $BINARY_PATH"
fi
- name: Setup Python
if: steps.smol-cache-valid.outputs.valid != 'true' || inputs.force
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
# Use 3.11 to match Alpine 3.19 (Docker builds) which ships Python 3.11.14.
# Python 3.13 breaks gyp with: TypeError: Strings must be encoded before hashing
# At: tools/gyp/pylib/gyp/generator/ninja.py:813 hashlib.md5(outputs[0])
# Python 3.13 requires .encode() for hashlib, but gyp doesn't support it yet.
# Using 3.11 ensures consistency across standard and Alpine builds.
python-version: '3.11'
- name: Verify Python installation
if: steps.smol-cache-valid.outputs.valid != 'true' || inputs.force
shell: bash
run: |
echo "Python version:"
python3 --version
which python3
echo "PATH=$PATH"
- name: Install build dependencies (Ubuntu)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y build-essential gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- name: Cache Ninja (Ubuntu)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'linux'
id: ninja-cache-ubuntu
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: /usr/bin/ninja
key: ninja-ubuntu-${{ runner.os }}-${{ runner.arch }}
- name: Install Ninja (Ubuntu)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'linux' && steps.ninja-cache-ubuntu.outputs.cache-hit != 'true'
run: sudo apt-get install -y ninja-build
- name: Cache Ninja (macOS)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'darwin'
id: ninja-cache-macos
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
/usr/local/bin/ninja
/opt/homebrew/bin/ninja
key: ninja-macos-${{ runner.os }}-${{ runner.arch }}
- name: Install Ninja (macOS)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'darwin' && steps.ninja-cache-macos.outputs.cache-hit != 'true'
run: brew install ninja
- name: Cache Ninja (Windows)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32'
id: ninja-cache-windows
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: C:\ProgramData\chocolatey\bin\ninja.exe
key: ninja-windows-${{ runner.os }}-${{ runner.arch }}
- name: Install Ninja (Windows)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32' && steps.ninja-cache-windows.outputs.cache-hit != 'true'
run: choco install ninja
- name: Ensure Git for Windows is installed
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32'
shell: pwsh
run: |
# ═══════════════════════════════════════════════════════════════════
# CRITICAL: Verify Git for Windows Installation
# ═══════════════════════════════════════════════════════════════════
# WHY: Git for Windows provides Unix tools needed for patching Node.js source.
# Specifically, we need the 'patch' command to apply Socket patches.
#
# WHAT: Check if Git for Windows is installed. If missing, install it.
#
# NOTE: GitHub Actions runners have Git pre-installed, but this ensures
# compatibility with custom runners or local testing.
# ═══════════════════════════════════════════════════════════════════
$gitPath = "C:\Program Files\Git"
if (-not (Test-Path $gitPath)) {
Write-Host "⚠ Git for Windows not found at $gitPath"
Write-Host "→ Installing Git for Windows via Chocolatey..."
choco install git -y --no-progress
refreshenv
} else {
Write-Host "✓ Git for Windows found: $gitPath"
}
- name: Add Git Unix tools to PATH (Windows)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32'
shell: pwsh
run: |
# ═══════════════════════════════════════════════════════════════════
# CRITICAL: Add Unix Tools to PATH
# ═══════════════════════════════════════════════════════════════════
# WHY: Socket patches are in unified diff format and require the 'patch'
# command to apply. Git for Windows bundles Unix tools (patch, diff,
# etc.) in usr/bin but doesn't add them to PATH by default.
#
# WHAT: Add Git's usr/bin directory to PATH so 'patch' is available.
#
# LOCATION: C:\Program Files\Git\usr\bin\patch.exe
# ═══════════════════════════════════════════════════════════════════
$gitUnixPath = "C:\Program Files\Git\usr\bin"
# Add to GITHUB_PATH so subsequent steps inherit this PATH change.
Write-Host "→ Adding Unix tools to PATH: $gitUnixPath"
Add-Content -Path $env:GITHUB_PATH -Value $gitUnixPath
# Verify patch is now accessible (update PATH in current shell).
$env:PATH = "$gitUnixPath;$env:PATH"
Write-Host ""
Write-Host "→ Verifying patch command:"
Get-Command patch -ErrorAction SilentlyContinue
& patch --version
- name: Ensure Visual Studio is installed
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32'
shell: pwsh
run: |
# ═══════════════════════════════════════════════════════════════════
# CRITICAL: Verify Visual Studio Installation
# ═══════════════════════════════════════════════════════════════════
# WHY: Node.js builds require MSVC compiler (cl.exe), linker (link.exe),
# and Windows SDK. These come with Visual Studio or Build Tools.
#
# WHAT: Check if VS is installed. If missing, install Build Tools 2022.
#
# NOTE: GitHub Actions runners have VS pre-installed, but this ensures
# compatibility with custom runners or local testing.
# ═══════════════════════════════════════════════════════════════════
$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (Test-Path $vswherePath) {
# vswhere found, check if VS is installed.
$vsPath = & $vswherePath -latest -property installationPath
if ($vsPath) {
Write-Host "✓ Visual Studio found: $vsPath"
} else {
# vswhere exists but found no VS installation.
Write-Host "⚠ vswhere.exe found but no VS installation detected"
Write-Host "→ Installing Visual Studio 2022 Build Tools..."
choco install visualstudio2022buildtools --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --passive" -y --no-progress
refreshenv
}
} else {
# No vswhere means no VS installer.
Write-Host "⚠ vswhere.exe not found (Visual Studio not installed)"
Write-Host "→ Installing Visual Studio 2022 Build Tools..."
choco install visualstudio2022buildtools --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --passive" -y --no-progress
refreshenv
}
- name: Setup Visual Studio environment (Windows)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32'
shell: pwsh
run: |
# ═══════════════════════════════════════════════════════════════════
# CRITICAL: Configure MSVC Build Environment
# ═══════════════════════════════════════════════════════════════════
# WHY: Node.js configure.py requires MSVC compiler environment variables
# (PATH, INCLUDE, LIB, etc.) to locate the C++ compiler and linker.
#
# WHAT: Run vcvarsall.bat to set up the Visual Studio build environment,
# then export all changed environment variables to GITHUB_ENV so
# subsequent build steps can use the MSVC toolchain.
#
# FLOW:
# 1. Find VS installation → vswhere.exe
# 2. Locate vcvarsall.bat → VS\VC\Auxiliary\Build\
# 3. Capture env BEFORE → cmd.exe: set
# 4. Run vcvarsall.bat → Sets up MSVC environment
# 5. Capture env AFTER → cmd.exe: set
# 6. Export changes → GITHUB_ENV (for next steps)
#
# REFERENCE: Same technique as ilammy/msvc-dev-cmd action
# ═══════════════════════════════════════════════════════════════════
# ───────────────────────────────────────────────────────────────────
# STEP 1: Find Visual Studio installation using vswhere
# ───────────────────────────────────────────────────────────────────
# vswhere.exe is Microsoft's official tool for locating VS installations.
# It's included with VS 2017+ and GitHub Actions runners.
#
$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (-not (Test-Path $vswherePath)) {
Write-Error "vswhere.exe not found at $vswherePath"
exit 1
}
# Query vswhere for the latest VS installation path.
# -products * : Include all VS products (Enterprise, Professional, Community, BuildTools)
# -latest : Get the newest version
# -prerelease : Include preview/RC versions
#
$vsPath = & $vswherePath -products * -latest -prerelease -property installationPath
if (-not $vsPath) {
Write-Error "Visual Studio installation not found"
exit 1
}
# ───────────────────────────────────────────────────────────────────
# STEP 2: Locate vcvarsall.bat (MSVC environment setup script)
# ───────────────────────────────────────────────────────────────────
# vcvarsall.bat is the master script that sets up the MSVC build environment.
# It sets PATH, INCLUDE, LIB, and other variables needed by the compiler.
#
# Location: <VS Install>\VC\Auxiliary\Build\vcvarsall.bat
#
$vcvarsallPath = Join-Path $vsPath "VC\Auxiliary\Build\vcvarsall.bat"
if (-not (Test-Path $vcvarsallPath)) {
Write-Error "vcvarsall.bat not found at $vcvarsallPath"
exit 1
}
Write-Host "Found vcvarsall.bat: $vcvarsallPath"
# ───────────────────────────────────────────────────────────────────
# STEP 3-5: Capture environment before/after running vcvarsall.bat
# ───────────────────────────────────────────────────────────────────
# TECHNIQUE: Run three commands in sequence, separated by cls (clear screen):
#
# ┌──────────────────────────────────────────────────────────────┐
# │ cmd.exe /c "set && cls && vcvarsall.bat arch && cls && set" │
# │ │
# │ Output sections (split by cls): │
# │ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐│
# │ │ Section [0] │ cls │ Section [1] │ cls │ Section [2] ││
# │ │ (env BEFORE)│ │ (vcvars msgs)│ │ (env AFTER) ││
# │ └─────────────┘ └──────────────┘ └────────────────┘│
# └──────────────────────────────────────────────────────────────┘
#
# cls (clear screen) outputs form feed (0x0C), making it a reliable delimiter.
#
$arch = "${{ matrix.arch }}"
Write-Host "Setting up MSVC environment for: $arch"
# Build the command for cmd.exe with proper nested quoting.
# PowerShell -> cmd.exe requires careful quote escaping:
# - Outer quotes for cmd.exe /c argument
# - Inner quotes around vcvarsall.bat path (has spaces)
# - Escape inner quotes with backtick for PowerShell
#
# Result: cmd.exe receives -> set && cls && "C:\...\vcvarsall.bat" x64 && cls && set
#
$cmdArgs = @(
'/c'
"set && cls && `"$vcvarsallPath`" $arch && cls && set"
)
# Invoke cmd.exe with properly structured arguments.
$output = & cmd.exe $cmdArgs 2>&1
# Split output by form feed character (0x0C produced by cls).
$sections = ($output -join "`n") -split "`f"
if ($sections.Count -lt 3) {
Write-Host "ERROR: Failed to parse vcvarsall.bat output"
Write-Host "Expected 3 sections (before, messages, after), got $($sections.Count)"
Write-Host ""
Write-Host "Raw output:"
Write-Host $output
exit 1
}
# ───────────────────────────────────────────────────────────────────
# STEP 6: Parse environment variables (before and after)
# ───────────────────────────────────────────────────────────────────
# Convert "NAME=VALUE" lines into PowerShell hashtables for comparison.
#
# Parse BEFORE environment (section 0).
$oldEnv = @{}
foreach ($line in ($sections[0] -split "`n")) {
if ($line -match '^([^=]+)=(.*)$') {
$oldEnv[$matches[1]] = $matches[2]
}
}
# Parse AFTER environment (section 2).
$newEnv = @{}
foreach ($line in ($sections[2] -split "`n")) {
if ($line -match '^([^=]+)=(.*)$') {
$newEnv[$matches[1]] = $matches[2]
}
}
# ───────────────────────────────────────────────────────────────────
# STEP 7: Export only CHANGED variables to GITHUB_ENV
# ───────────────────────────────────────────────────────────────────
# GITHUB_ENV format (heredoc for multiline values):
# VARNAME<<EOF_12345
# value with
# multiple lines
# EOF_12345
#
# This makes variables available to ALL subsequent steps in the job.
#
Write-Host "Exporting changed environment variables:"
$exportCount = 0
foreach ($name in $newEnv.Keys) {
$newValue = $newEnv[$name]
$oldValue = $oldEnv[$name]
# Export only variables that changed or are new.
if ($newValue -ne $oldValue) {
Write-Host " $name"
$exportCount++
# Use heredoc format with random delimiter to safely handle special characters.
$delimiter = "EOF_$(Get-Random)"
Add-Content -Path $env:GITHUB_ENV -Value "$name<<$delimiter"
Add-Content -Path $env:GITHUB_ENV -Value $newValue
Add-Content -Path $env:GITHUB_ENV -Value $delimiter
}
}
Write-Host ""
Write-Host "Visual Studio environment configured ($exportCount variables exported)"
# ───────────────────────────────────────────────────────────────────
# STEP 7.5: Export gyp-specific environment variables
# ───────────────────────────────────────────────────────────────────
# gyp (used by configure.py) needs these to bypass registry detection.
# GYP_MSVS_VERSION: Tells gyp which VS version to use.
# GYP_MSVS_OVERRIDE_PATH: Bypasses registry check, uses provided path.
#
Write-Host ""
Write-Host "Exporting gyp environment variables:"
# GYP_MSVS_VERSION
$gypVersion = "2022"
Add-Content -Path $env:GITHUB_ENV -Value "GYP_MSVS_VERSION=$gypVersion"
Write-Host " GYP_MSVS_VERSION = $gypVersion"
# GYP_MSVS_OVERRIDE_PATH
Add-Content -Path $env:GITHUB_ENV -Value "GYP_MSVS_OVERRIDE_PATH=$vsPath"
Write-Host " GYP_MSVS_OVERRIDE_PATH = $vsPath"
# ───────────────────────────────────────────────────────────────────
# STEP 8: Verify critical VS environment variables were set
# ───────────────────────────────────────────────────────────────────
# CRITICAL: Ensure vcvarsall.bat actually configured the environment.
# configure.py needs these variables to find the MSVC compiler.
#
Write-Host ""
Write-Host "Verifying critical MSVC environment variables:"
$criticalVars = @(
'VCINSTALLDIR'
'WindowsSDKVersion'
'INCLUDE'
'LIB'
'PATH'
)
$allSet = $true
foreach ($var in $criticalVars) {
# Check if variable was exported to newEnv (will be in GITHUB_ENV).
$value = $newEnv[$var]
if ($value) {
$preview = $value.Substring(0, [Math]::Min(60, $value.Length))
if ($value.Length -gt 60) { $preview += "..." }
Write-Host " ✓ $var = $preview"
} else {
Write-Host " ✗ $var is NOT SET"
$allSet = $false
}
}
Write-Host ""
if (-not $allSet) {
Write-Error "vcvarsall.bat did not set required environment variables!"
Write-Host ""
Write-Host "This means:"
Write-Host " 1. vcvarsall.bat failed to run, OR"
Write-Host " 2. vcvarsall.bat ran but didn't configure the environment"
Write-Host ""
Write-Host "Debug: Showing vcvarsall.bat messages (section 1):"
Write-Host $sections[1]
exit 1
}
Write-Host "✓ MSVC environment verified"
Write-Host "Next build step will have access to MSVC compiler toolchain"
- name: Verify MSVC environment persisted (Windows)
if: (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'win32'
shell: pwsh
run: |
# ═══════════════════════════════════════════════════════════════════
# SECONDARY CHECK: Verify GITHUB_ENV worked
# ═══════════════════════════════════════════════════════════════════
# This verifies that environment variables exported via GITHUB_ENV
# in the previous step are actually available in this new step.
#
# If this fails, it means GITHUB_ENV heredoc format has issues.
# ═══════════════════════════════════════════════════════════════════
Write-Host "Verifying MSVC environment persisted to this step:"
Write-Host ""
$criticalVars = @('VCINSTALLDIR', 'WindowsSDKVersion', 'INCLUDE', 'LIB')
$allSet = $true
foreach ($var in $criticalVars) {
$value = [Environment]::GetEnvironmentVariable($var)
if ($value) {
Write-Host " ✓ $var is set"
} else {
Write-Host " ✗ $var is NOT SET"
$allSet = $false
}
}
Write-Host ""
if (-not $allSet) {
Write-Error "GITHUB_ENV failed to persist environment variables!"
Write-Host "Variables were set in previous step but didn't carry over."
Write-Host "This is likely an issue with heredoc formatting in GITHUB_ENV."
exit 1
}
Write-Host "✓ GITHUB_ENV worked - variables persisted correctly"
- name: Build smol binary (Standard platforms)
if: steps.check-platform.outputs.should-run == 'true' && (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform != 'alpine'
run: pnpm --filter @socketbin/node-smol-builder run build
- name: Set up Docker Buildx (Alpine)
if: steps.check-platform.outputs.should-run == 'true' && (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'alpine'
uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1
- name: Build Alpine builder image with caching
if: steps.check-platform.outputs.should-run == 'true' && (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'alpine'
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
context: .
file: ./packages/node-smol-builder/docker/Dockerfile.alpine
platforms: linux/${{ matrix.arch == 'x64' && 'amd64' || 'arm64' }}
push: false
load: true
tags: smol-alpine-builder:${{ matrix.arch }}
cache-from: type=gha,scope=alpine-builder-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=alpine-builder-${{ matrix.arch }}
- name: Build smol binary (Alpine - Docker)
if: steps.check-platform.outputs.should-run == 'true' && (steps.smol-cache-valid.outputs.valid != 'true' || inputs.force) && matrix.platform == 'alpine'
run: |
docker run --rm \
--platform linux/${{ matrix.arch == 'x64' && 'amd64' || 'arm64' }} \
-v $PWD:/workspace \
-w /workspace \
-e PNPM_HOME=/usr/local/bin \
smol-alpine-builder:${{ matrix.arch }} \
sh -c 'pnpm --filter @socketbin/node-smol-builder run build'
- name: Verify smol binary
if: steps.check-platform.outputs.should-run == 'true'
shell: bash
run: |
echo -e "\033[35m⚡\033[0m Socket smol binary verification"
mkdir -p packages/node-smol-builder/dist
ls -lh packages/node-smol-builder/dist/
echo ""
BINARY_PATH="packages/node-smol-builder/dist/socket-smol-${{ matrix.platform }}-${{ matrix.arch }}"
if [ -f "$BINARY_PATH" ] || [ -f "${BINARY_PATH}.exe" ]; then
if [ "${{ matrix.os }}" = "windows" ]; then
echo -e "\033[32m✓\033[0m socket-smol-${{ matrix.platform }}-${{ matrix.arch }}.exe: $(du -h ${BINARY_PATH}.exe | cut -f1)"
else
echo -e "\033[32m✓\033[0m socket-smol-${{ matrix.platform }}-${{ matrix.arch }}: $(du -h $BINARY_PATH | cut -f1)"
fi
else
echo -e "\033[33m⚠\033[0m Binary not found at expected path"
fi
- name: Upload smol binary
if: steps.check-platform.outputs.should-run == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: socket-smol-${{ matrix.platform }}-${{ matrix.arch }}
path: |
packages/node-smol-builder/dist/socket-smol-${{ matrix.platform }}-${{ matrix.arch }}
packages/node-smol-builder/dist/socket-smol-${{ matrix.platform }}-${{ matrix.arch }}.exe
retention-days: 7
if-no-files-found: warn
- name: Calculate and report build metrics
if: always() && steps.check-platform.outputs.should-run == 'true' && steps.build-start.outputs.timestamp
shell: bash
run: |
# Calculate build duration.
END_TIME=$(date +%s)
DURATION=$((END_TIME - ${{ steps.build-start.outputs.timestamp }}))
DURATION_MIN=$((DURATION / 60))
DURATION_SEC=$((DURATION % 60))
# Determine if cache was used.
CACHE_STATUS="${{ steps.smol-cache-valid.outputs.valid == 'true' && 'Hit' || 'Miss' }}"
BUILD_STATUS="${{ job.status }}"
# Create metrics JSON.
cat > build-metrics.json <<EOF
{
"platform": "${{ matrix.platform }}",
"arch": "${{ matrix.arch }}",
"duration_seconds": $DURATION,
"duration_formatted": "${DURATION_MIN}m ${DURATION_SEC}s",
"cache_status": "$CACHE_STATUS",
"build_status": "$BUILD_STATUS",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF
# Report to step summary.
echo "## Build Metrics: ${{ matrix.platform }}-${{ matrix.arch }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Duration: **${DURATION_MIN}m ${DURATION_SEC}s**" >> $GITHUB_STEP_SUMMARY
echo "- Cache: **$CACHE_STATUS**" >> $GITHUB_STEP_SUMMARY
echo "- Status: **$BUILD_STATUS**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
- name: Upload build metrics
if: always() && steps.check-platform.outputs.should-run == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: build-metrics-${{ matrix.platform }}-${{ matrix.arch }}
path: build-metrics.json
retention-days: 30
if-no-files-found: ignore
summary:
name: ⚡ Build Summary
needs: [build-deps, build-smol]
if: always() && !cancelled()
runs-on: ubuntu-latest
steps:
- name: Generate summary
run: |
echo "# ⚡ Socket Smol Build" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Check status of dependencies.
DEPS_STATUS="${{ needs.build-deps.result }}"
BUILD_STATUS="${{ needs.build-smol.result }}"
if [ "$DEPS_STATUS" = "failure" ] || [ "$BUILD_STATUS" = "failure" ]; then
echo "## ✗ Build Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "One or more builds failed. Check the logs above for details." >> $GITHUB_STEP_SUMMARY
elif [ "$DEPS_STATUS" = "skipped" ] && [ "$BUILD_STATUS" = "skipped" ]; then
echo "## ⊘ Builds Skipped" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All builds were skipped (likely due to workflow inputs)." >> $GITHUB_STEP_SUMMARY
elif [ "$BUILD_STATUS" = "skipped" ]; then
echo "## ⊘ Builds Skipped" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Builds were skipped (platform builds disabled by workflow inputs)." >> $GITHUB_STEP_SUMMARY
else
echo "## ✓ Build Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Socket smol binaries (compressed Node.js + CLI) built successfully." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ⚡ Socket Smol Method" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Method | Description | Size |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------------|------|" >> $GITHUB_STEP_SUMMARY
echo "| ⚡ Socket Smol | Compressed Node.js + Socket CLI | ~18 MB |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🎯 Platforms" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Linux (x64, arm64)" >> $GITHUB_STEP_SUMMARY
echo "- macOS (x64, arm64)" >> $GITHUB_STEP_SUMMARY
echo "- Windows (x64, arm64)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### → Next Steps" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$BUILD_STATUS" = "success" ]; then
echo "- Binaries cached for E2E tests" >> $GITHUB_STEP_SUMMARY
echo "- Use \`publish-socketbin.yml\` to publish" >> $GITHUB_STEP_SUMMARY
echo "- Cache invalidated on patch/script changes" >> $GITHUB_STEP_SUMMARY
elif [ "$BUILD_STATUS" = "skipped" ]; then
echo "- Re-run workflow with platform builds enabled" >> $GITHUB_STEP_SUMMARY
echo "- Or use \`workflow_dispatch\` to trigger specific platforms" >> $GITHUB_STEP_SUMMARY
else
echo "- Check build logs for error details" >> $GITHUB_STEP_SUMMARY
echo "- Fix issues and re-run workflow" >> $GITHUB_STEP_SUMMARY
fi