mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
Launcher: engine-probe PATH validation, working .cmd download path; CI: drop stale path, add oracle job
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).
This commit is contained in:
+51
-11
@@ -2,9 +2,29 @@
|
||||
# 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,
|
||||
# then `impeccable` on PATH (the npm shim). Never needs Node.
|
||||
# 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
|
||||
@@ -40,24 +60,42 @@ if [ -f "$bin" ]; then
|
||||
# Lost the executable bit in transit (zip extraction, some copiers).
|
||||
chmod +x "$bin" 2>/dev/null && exec "$bin" "$@"
|
||||
fi
|
||||
if [ -x "${HOME:-/nonexistent}/.impeccable/bin/impeccable" ]; then
|
||||
exec "${HOME}/.impeccable/bin/impeccable" "$@"
|
||||
# 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"
|
||||
cached="$cache_root/bin/$version/impeccable$exe"
|
||||
if [ -n "$version" ] && [ -x "$cached" ]; then
|
||||
exec "$cached" "$@"
|
||||
fi
|
||||
if command -v impeccable >/dev/null 2>&1; then
|
||||
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"
|
||||
@@ -66,10 +104,12 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
tmp="$cache_root/bin/$version/.impeccable.part.$$"
|
||||
mkdir -p "$cache_root/bin/$version" 2>/dev/null
|
||||
fetched=0
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --retry 2 -o "$tmp" "$url" 2>/dev/null && fetched=1
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp" "$url" 2>/dev/null && fetched=1
|
||||
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
|
||||
@@ -90,6 +130,6 @@ if [ -n "$version" ] && [ "$os" != unknown ] && [ "$arch" != unknown ]; then
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
fi
|
||||
|
||||
echo "impeccable: no binary for $os-$arch found (looked in $bin, $cached, PATH)." >&2
|
||||
echo "Install one: npm i -g impeccable, or download impeccable-$os-$arch from https://github.com/renaissance-geek-inc/impeccable-dist/releases into $cache_root/bin/$version/impeccable" >&2
|
||||
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
|
||||
|
||||
+124
-32
@@ -1,44 +1,136 @@
|
||||
@echo off
|
||||
setlocal
|
||||
rem Impeccable launcher (Windows). Runs bin\windows-x64\impeccable.exe next to this file.
|
||||
if defined IMPECCABLE_BIN if exist "%IMPECCABLE_BIN%" (
|
||||
"%IMPECCABLE_BIN%" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
rem Impeccable launcher (Windows). Runs bin\windows-<arch>\impeccable.exe next
|
||||
rem to this file, else a cached or freshly downloaded engine binary.
|
||||
rem
|
||||
rem Structure notes (this file is exercised by dry parsing and string-level
|
||||
rem tests, not yet on a real Windows machine):
|
||||
rem - No multi-line parenthesized blocks: cmd expands %var% at block parse
|
||||
rem time, which made the old download path read back empty %url%/%cached%.
|
||||
rem Linear goto flow keeps every expansion on its own line, and avoids
|
||||
rem delayed expansion eating ! characters in user arguments.
|
||||
rem - The unversioned user binary and the PATH candidate are validated with
|
||||
rem the engine-probe handshake (see :probe) so the retired 3.x npm CLI,
|
||||
rem whose bin is also named impeccable, is never exec'd. IMPECCABLE_BIN,
|
||||
rem the sibling binary, and the version-pinned cache stay trusted.
|
||||
rem - Downloads are verified against the .sha256 sidecar via certutil when
|
||||
rem the sidecar is served; on ARM64 the arm64 asset is tried first and the
|
||||
rem x64 asset is the fallback (Windows on ARM runs x64 binaries).
|
||||
if not defined IMPECCABLE_SKILL_DIR set "IMPECCABLE_SKILL_DIR=%~dp0.."
|
||||
if not defined IMPECCABLE_SELF set "IMPECCABLE_SELF=%~f0"
|
||||
set "arch=x64"
|
||||
if /I "%PROCESSOR_ARCHITECTURE%"=="ARM64" set "arch=arm64"
|
||||
|
||||
if not defined IMPECCABLE_BIN goto no_env_bin
|
||||
if not exist "%IMPECCABLE_BIN%" goto no_env_bin
|
||||
set "run=%IMPECCABLE_BIN%"
|
||||
goto run
|
||||
:no_env_bin
|
||||
|
||||
set "bin=%~dp0bin\windows-%arch%\impeccable.exe"
|
||||
if exist "%bin%" (
|
||||
"%bin%" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
if exist "%USERPROFILE%\.impeccable\bin\impeccable.exe" (
|
||||
"%USERPROFILE%\.impeccable\bin\impeccable.exe" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
if not exist "%bin%" goto no_sibling
|
||||
set "run=%bin%"
|
||||
goto run
|
||||
:no_sibling
|
||||
|
||||
set "home_bin=%USERPROFILE%\.impeccable\bin\impeccable.exe"
|
||||
if not exist "%home_bin%" goto no_home_bin
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE goto no_home_bin
|
||||
call :probe "%home_bin%"
|
||||
if not "%probe_ok%"=="1" goto no_home_bin
|
||||
set "run=%home_bin%"
|
||||
goto run
|
||||
:no_home_bin
|
||||
|
||||
set "version="
|
||||
if exist "%~dp0VERSION" set /p version=<"%~dp0VERSION"
|
||||
if not defined IMPECCABLE_HOME set "IMPECCABLE_HOME=%USERPROFILE%\.impeccable"
|
||||
set "cached=%IMPECCABLE_HOME%\bin\%version%\impeccable.exe"
|
||||
if defined version if exist "%cached%" (
|
||||
"%cached%" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
where impeccable >nul 2>nul && (
|
||||
impeccable %*
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
if defined version (
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/renaissance-geek-inc/impeccable-dist/releases/download"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/v%version%/impeccable-windows-%arch%.exe"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
where curl.exe >nul 2>nul && curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul && move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if exist "%cached%" (
|
||||
"%cached%" %*
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
)
|
||||
echo impeccable: no binary found (looked in %bin%, %cached%, PATH). Install one: npm i -g impeccable 1>&2
|
||||
if not defined version goto no_cache
|
||||
if not exist "%cached%" goto no_cache
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
:no_cache
|
||||
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE goto download
|
||||
where impeccable >nul 2>nul
|
||||
if errorlevel 1 goto download
|
||||
call :probe impeccable
|
||||
if not "%probe_ok%"=="1" goto download
|
||||
impeccable %*
|
||||
exit /b
|
||||
|
||||
:download
|
||||
rem Last resort: fetch this version's binary from the release channel into
|
||||
rem the version-pinned user cache, verify it, then run it. Never inside
|
||||
rem another launcher's probe: fail fast and quiet instead.
|
||||
if defined IMPECCABLE_LAUNCHER_PROBE exit /b 127
|
||||
if not defined version goto fail
|
||||
where curl.exe >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
if not defined IMPECCABLE_DOWNLOAD_BASE set "IMPECCABLE_DOWNLOAD_BASE=https://github.com/renaissance-geek-inc/impeccable-dist/releases/download"
|
||||
if not exist "%IMPECCABLE_HOME%\bin\%version%" mkdir "%IMPECCABLE_HOME%\bin\%version%" >nul 2>nul
|
||||
set "asset=impeccable-windows-%arch%.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if not errorlevel 1 goto verify
|
||||
if not "%arch%"=="arm64" goto fail
|
||||
set "asset=impeccable-windows-x64.exe"
|
||||
set "url=%IMPECCABLE_DOWNLOAD_BASE%/v%version%/%asset%"
|
||||
curl.exe -fsSL -o "%cached%.part" "%url%" >nul 2>nul
|
||||
if errorlevel 1 goto fail
|
||||
|
||||
:verify
|
||||
rem Mirrors the sh launcher: a missing sidecar or hash tool skips
|
||||
rem verification; a mismatch is fatal.
|
||||
curl.exe -fsSL -o "%cached%.sha256" "%url%.sha256" >nul 2>nul
|
||||
if not errorlevel 1 goto have_sidecar
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
goto place
|
||||
:have_sidecar
|
||||
set "expected="
|
||||
set /p expected=<"%cached%.sha256"
|
||||
for /f "tokens=1" %%h in ("%expected%") do set "expected=%%h"
|
||||
set "actual="
|
||||
for /f "skip=1 delims=" %%h in ('certutil -hashfile "%cached%.part" SHA256 2^>nul') do if not defined actual set "actual=%%h"
|
||||
del "%cached%.sha256" >nul 2>nul
|
||||
if not defined expected goto place
|
||||
if not defined actual goto place
|
||||
set "actual=%actual: =%"
|
||||
if /I "%actual%"=="%expected%" goto place
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: checksum mismatch downloading %url% 1>&2
|
||||
exit /b 127
|
||||
|
||||
:place
|
||||
move /y "%cached%.part" "%cached%" >nul 2>nul
|
||||
if not exist "%cached%" goto fail
|
||||
set "run=%cached%"
|
||||
goto run
|
||||
|
||||
:run
|
||||
"%run%" %*
|
||||
exit /b
|
||||
|
||||
:probe
|
||||
rem Sets probe_ok=1 when %1 answers the engine handshake: prints
|
||||
rem "impeccable-engine <version>" and exits 0. The 3.x npm CLI answers any
|
||||
rem unknown verb with "Unknown command", exit 1, so it never passes.
|
||||
set "probe_ok="
|
||||
set "probe_tmp=%TEMP%\impeccable-probe-%RANDOM%%RANDOM%.txt"
|
||||
set "IMPECCABLE_LAUNCHER_PROBE=1"
|
||||
"%~1" engine-probe >"%probe_tmp%" 2>nul
|
||||
set "probe_err=%ERRORLEVEL%"
|
||||
set "IMPECCABLE_LAUNCHER_PROBE="
|
||||
if not "%probe_err%"=="0" goto probe_done
|
||||
findstr /b /c:"impeccable-engine" "%probe_tmp%" >nul 2>nul
|
||||
if not errorlevel 1 set "probe_ok=1"
|
||||
:probe_done
|
||||
del "%probe_tmp%" >nul 2>nul
|
||||
exit /b 0
|
||||
|
||||
:fail
|
||||
del "%cached%.part" >nul 2>nul
|
||||
echo impeccable: no engine binary found (looked in %bin%, %cached%, PATH). 1>&2
|
||||
echo Download impeccable-windows-%arch%.exe from https://github.com/renaissance-geek-inc/impeccable-dist/releases and save it as %cached%, or set IMPECCABLE_BIN to a preinstalled engine binary. Docs: https://impeccable.style 1>&2
|
||||
exit /b 127
|
||||
|
||||
Reference in New Issue
Block a user