master
John Lauer v2.0.685 (insiders): The pup exe is signed as Adom Industries, Inc. (Authenticode, timestamped), so Windows 11 Smart Ap… 8e62f9e 1mo ago
#!/usr/bin/env bash
# rust-build.sh — build the Rust pup exe and its release zip (Phase 4 of docs/PLAN-rust-port.md).
#
#   tools/rust-build.sh <version>            # -> dist/<version>/adom-bridge-puppeteer-v<version>.zip
#
# The zip is ab's bridge package: adom-pup-bridge.exe + bridge.json (spawn.kind exe) + BRIDGE_VERSION
# at the zip root. Nothing else: every browser asset, extension, icon and font is embedded in the exe.
# Gates before the build: border check (Win32 only in host-win), embedded assets regenerated, host
# tests green. The exe version is include_str!(src/BRIDGE_VERSION), so write that first (ship.sh does).
set -euo pipefail
VER="${1:?usage: tools/rust-build.sh <version>}"
REPO="$(cd "$(dirname "$0")/.." && pwd)"; cd "$REPO"
ARCH="dist/${VER}"; ZIP="adom-bridge-puppeteer-v${VER}.zip"; EXE="rust/target/x86_64-pc-windows-gnu/release/adom-pup-bridge.exe"
[ "$(cat src/BRIDGE_VERSION)" = "$VER" ] || { echo "src/BRIDGE_VERSION is $(cat src/BRIDGE_VERSION), not $VER (ship.sh writes it first)"; exit 1; }

echo "== rust 0. gates =="
python3 tools/gen-assets.py
tools/check-borders.sh
( cd rust && sed -i "s/^version = \"[0-9.]*\"/version = \"${VER}\"/" Cargo.toml && grep -q "^version = \"${VER}\"" Cargo.toml )
TEST_OUT=$(cd rust && cargo test --workspace -q 2>&1) || { echo "$TEST_OUT" | grep -E "error|FAILED|panicked" | head -20; echo "   TESTS FAILED"; exit 1; }
echo "$TEST_OUT" | grep -qE "test result: FAILED|panicked|error\[" && { echo "$TEST_OUT" | grep -E "error|FAILED|panicked" | head -20; echo "   TESTS FAILED"; exit 1; }
echo "   host tests green"

echo "== rust 1. release build (x86_64-pc-windows-gnu) =="
( cd rust && cargo build --release --target x86_64-pc-windows-gnu 2>&1 | grep -E "^(error|warning: unused)|Finished" )
[ -f "$EXE" ] || { echo "   exe missing"; exit 1; }
grep -q "Puppeteer Bridge v" "$EXE" || { echo "   exe lacks the boot banner string"; exit 1; }

echo "== rust 1b. sign as Adom Industries (never package an unsigned exe) =="
# Windows 11 Smart App Control blocks unsigned exes. The signing itself lives in a PRIVATE script outside this repo
# (John's build process); this public build only calls it and checks the result. No script, no package.
SIGNER="${ADOM_SIGN_EXE:-$HOME/.config/adom-signing/sign-exe.sh}"
[ -x "$SIGNER" ] || { echo "   no private signing script at $SIGNER: refusing to package an unsigned exe"; exit 1; }
"$SIGNER" "$(pwd)/$EXE" pup || { echo "   SIGNING FAILED: nothing packaged"; exit 1; }
python3 - "$EXE" <<'PYS' || { echo "   the exe carries no Authenticode signature after signing: nothing packaged"; exit 1; }
import struct, sys
b = open(sys.argv[1], 'rb').read(); pe = struct.unpack_from('<I', b, 0x3c)[0]; magic = struct.unpack_from('<H', b, pe + 24)[0]
off, size = struct.unpack_from('<II', b, pe + 24 + (112 if magic == 0x20b else 96) + 32)
sys.exit(0 if size > 0 and off + size <= len(b) else 1)
PYS
grep -q "Puppeteer Bridge v" "$EXE" || { echo "   the signed exe lacks the boot banner string"; exit 1; }

echo "== rust 2. package =="
mkdir -p "$ARCH"
python3 - "$VER" "$EXE" "$ARCH/$ZIP" <<'PY'
import json, sys, zipfile, hashlib, os
ver, exe, out = sys.argv[1:4]
d = json.load(open('src/bridge.json'))
assert d['version'] == ver, f"src/bridge.json is {d['version']}, not {ver}"
d['spawn'] = { 'kind': 'exe', 'entrypoint': 'adom-pup-bridge.exe', 'port': d['spawn'].get('port', 8851), 'healthEndpoint': '/status', 'stopMethod': 'kill', 'killImageName': 'adom-pup-bridge.exe', 'persistent': True,
    '_hint': 'spawn.kind exe: one sha256-verified binary, no Node, no npm, no postinstall (docs/PLAN-rust-port.md). Port pinned low per ab#83 (WinNAT exclusion ranges in the ephemeral band).',
    '_portNote': d['spawn'].get('_portNote', '') }
d.pop('runtime', None)
d['dependencies'] = { 'browser': d.get('dependencies', {}).get('browser', 'Chromium-family: installed Chrome or Edge, else Chrome for Testing is fetched as a last resort.') }
# The exe is Windows-only today (host-mac is stubs). Say so in the package so a Mac on the insiders
# tier is not handed a Windows binary; the Node build still serves macOS on the public tier.
d.setdefault('platforms', {})
d['platforms'] = {'windows': {'supported': True}, 'macos': {'supported': False, 'reason': 'The Rust exe is Windows-only for now; host-mac is stubbed for the Mac port. macOS runs the Node build (public tier).'}, 'linux': {'supported': False, 'reason': 'not yet'}}
d['shell'] = 'rust'
d['builtFor'] = 'x86_64-pc-windows-gnu' 
with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z:
    z.write(exe, 'adom-pup-bridge.exe')
    z.writestr('bridge.json', json.dumps(d, indent=2) + '\n')
    z.writestr('BRIDGE_VERSION', ver)
json.dump(d, open(os.path.join(os.path.dirname(out), 'bridge.json'), 'w'), indent=2)
print(f"   {out}  sha256={hashlib.sha256(open(out,'rb').read()).hexdigest()}  size={os.path.getsize(out)}  exe={os.path.getsize(exe)}")
PY