mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
Byte-identical copies of the engine repo's launchers (engine main af7572c): the retired 3.x npm CLI on PATH or in ~/.impeccable/bin is rejected by the engine-probe handshake instead of hijacking every verb; impeccable.cmd's download path is rewritten as straight-line goto flow (the parenthesized blocks expanded %url%/%cached% at parse time, making it dead code) with certutil sha256 verification and a windows-arm64 -> x64 asset fallback; the final error points at the release download instead of npm i -g (npm still serves the 3.x CLI). ci.yml: the generated-output check no longer diffs the deleted cli/engine/detect-antipatterns-browser.js, and a new oracle job fetches the pinned engine (bun run fetch:engine) and replays tests/oracle/ against it. The job is continue-on-error with a loud warning until the first engine release exists; flipping it to required is a release-time toggle, documented in the workflow. Verified here: sh -n on both launcher copies, bun run build green, full oracle replay against the rebuilt engine binary green (770 pass, 0 fail), and a launcher behavior test proving a fake 3.x CLI on PATH is skipped while the download + checksum chain completes against a local file server. Prepared with AI assistance (Claude Code).
136 lines
5.3 KiB
Bash
Executable File
136 lines
5.3 KiB
Bash
Executable File
#!/bin/sh
|
|
# Impeccable launcher. Runs the platform binary shipped next to this script:
|
|
# <this dir>/bin/<os>-<arch>/impeccable
|
|
# Order: $IMPECCABLE_BIN, the sibling binary, ~/.impeccable/bin/impeccable,
|
|
# the version-pinned cache, then `impeccable` on PATH. Never needs Node.
|
|
# The unversioned home binary and the PATH candidate are validated with the
|
|
# engine-probe handshake first: the retired 3.x npm CLI also installed a bin
|
|
# named `impeccable`, and exec'ing it would fail every verb with
|
|
# "Unknown command". Trusted candidates (IMPECCABLE_BIN, the sibling binary,
|
|
# the version-pinned cache) are exec'd without a probe: hooks run them on
|
|
# every edit and must stay fast.
|
|
set -eu
|
|
|
|
# True when the candidate answers the engine handshake (prints
|
|
# "impeccable-engine <version>", exit 0). Quiet and fast (<100ms).
|
|
# IMPECCABLE_LAUNCHER_PROBE marks the child as a probe: a copy of this
|
|
# launcher reached recursively (e.g. symlinked onto PATH as `impeccable`)
|
|
# then skips its own probes and refuses to download, so probing stays cheap
|
|
# and can never loop.
|
|
probe_ok() {
|
|
case "$(IMPECCABLE_LAUNCHER_PROBE=1 "$1" engine-probe 2>/dev/null || true)" in
|
|
impeccable-engine*) return 0 ;;
|
|
esac
|
|
return 1
|
|
}
|
|
probing=${IMPECCABLE_LAUNCHER_PROBE:-}
|
|
|
|
if [ -n "${IMPECCABLE_BIN:-}" ] && [ -x "${IMPECCABLE_BIN}" ]; then
|
|
exec "${IMPECCABLE_BIN}" "$@"
|
|
fi
|
|
|
|
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
|
|
# What the binary needs to know about its home: the skill directory (for
|
|
# reference/*.md and command-metadata.json) and how to name itself in the
|
|
# commands it prints.
|
|
: "${IMPECCABLE_SKILL_DIR:=$(CDPATH= cd -- "$dir/.." && pwd)}"
|
|
: "${IMPECCABLE_SELF:=$0}"
|
|
export IMPECCABLE_SKILL_DIR IMPECCABLE_SELF
|
|
|
|
case "$(uname -s 2>/dev/null || echo unknown)" in
|
|
Darwin) os=darwin ;;
|
|
Linux) os=linux ;;
|
|
MINGW*|MSYS*|CYGWIN*|Windows_NT) os=windows ;;
|
|
*) os=unknown ;;
|
|
esac
|
|
case "$(uname -m 2>/dev/null || echo unknown)" in
|
|
arm64|aarch64) arch=arm64 ;;
|
|
x86_64|amd64) arch=x64 ;;
|
|
*) arch=unknown ;;
|
|
esac
|
|
|
|
bin="$dir/bin/$os-$arch/impeccable"
|
|
[ "$os" = windows ] && bin="$bin.exe"
|
|
|
|
if [ -x "$bin" ]; then
|
|
exec "$bin" "$@"
|
|
fi
|
|
if [ -f "$bin" ]; then
|
|
# Lost the executable bit in transit (zip extraction, some copiers).
|
|
chmod +x "$bin" 2>/dev/null && exec "$bin" "$@"
|
|
fi
|
|
# On Windows (an MSYS/Git Bash shell) the cached names carry .exe so this
|
|
# launcher and impeccable.cmd share one cache.
|
|
exe=""
|
|
[ "$os" = windows ] && exe=".exe"
|
|
home_bin="${HOME:-/nonexistent}/.impeccable/bin/impeccable$exe"
|
|
if [ -z "$probing" ] && [ -x "$home_bin" ] && probe_ok "$home_bin"; then
|
|
exec "$home_bin" "$@"
|
|
fi
|
|
# Version-pinned user cache, filled by the download below or by `impeccable update`.
|
|
version=""
|
|
[ -f "$dir/VERSION" ] && version=$(tr -d '[:space:]' < "$dir/VERSION")
|
|
cache_root="${IMPECCABLE_HOME:-${HOME:-/nonexistent}/.impeccable}"
|
|
cached="$cache_root/bin/$version/impeccable$exe"
|
|
if [ -n "$version" ] && [ -x "$cached" ]; then
|
|
exec "$cached" "$@"
|
|
fi
|
|
if [ -z "$probing" ] && command -v impeccable >/dev/null 2>&1 && probe_ok impeccable; then
|
|
exec impeccable "$@"
|
|
fi
|
|
|
|
# Last resort: fetch this version's binary for the current platform from the
|
|
# public release channel into the user cache. Needs network; sandboxes without
|
|
# egress preinstall the binary on PATH instead.
|
|
fetch_url() {
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -fsSL --retry 2 -o "$tmp" "$1" 2>/dev/null
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
wget -q -O "$tmp" "$1" 2>/dev/null
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
if [ -n "$probing" ]; then
|
|
# Inside another launcher's probe: no download, fail fast and quiet.
|
|
exit 127
|
|
fi
|
|
if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
|
base="${IMPECCABLE_DOWNLOAD_BASE:-https://github.com/renaissance-geek-inc/impeccable-dist/releases/download}"
|
|
asset="impeccable-$os-$arch"
|
|
[ "$os" = windows ] && asset="$asset.exe"
|
|
url="$base/v$version/$asset"
|
|
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
|
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
|
fetched=0
|
|
if fetch_url "$url"; then
|
|
fetched=1
|
|
elif [ "$os" = windows ] && [ "$arch" = arm64 ]; then
|
|
# Windows on ARM runs x64 binaries; fall back when no arm64 asset exists.
|
|
url="$base/v$version/impeccable-windows-x64.exe"
|
|
fetch_url "$url" && fetched=1
|
|
fi
|
|
if [ "$fetched" = 1 ]; then
|
|
if command -v curl >/dev/null 2>&1 && curl -fsSL -o "$tmp.sha256" "$url.sha256" 2>/dev/null; then
|
|
expected=$(cut -d' ' -f1 < "$tmp.sha256")
|
|
actual=""
|
|
if command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp" | cut -d' ' -f1)
|
|
elif command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp" | cut -d' ' -f1); fi
|
|
if [ -n "$actual" ] && [ "$actual" != "$expected" ]; then
|
|
rm -f "$tmp" "$tmp.sha256"
|
|
echo "impeccable: checksum mismatch downloading $url" >&2
|
|
exit 127
|
|
fi
|
|
rm -f "$tmp.sha256"
|
|
fi
|
|
chmod +x "$tmp" 2>/dev/null
|
|
mv -f "$tmp" "$cached" && exec "$cached" "$@"
|
|
fi
|
|
rm -f "$tmp" 2>/dev/null
|
|
fi
|
|
|
|
echo "impeccable: no engine binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
|
echo "Download impeccable-$os-$arch from https://github.com/renaissance-geek-inc/impeccable-dist/releases into $cache_root/bin/$version/impeccable$exe (then chmod +x), or set IMPECCABLE_BIN to a preinstalled engine binary. Docs: https://impeccable.style" >&2
|
|
exit 127
|