Browse Source
- run no longer launches the last runtime alphabetically: resolution is PUNK_ACTIVE_RUNTIME env override -> bin/runtime/<platform>/active.toml (constrained single-key toml, written by the new 'use <name>' subcommand, marked with * in list, covered by the existing bin/* VCS ignores) -> sole installed candidate -> error listing candidates. First fetch sets active only when none recorded; later fetches never steal it. Stale active (file removed) reported with reselect guidance. - bash/zsh payload fetch reaches checksum parity with the powershell payload: fetches sha1sums.txt, locates the stored hash, skips when the local copy matches, downloads to .tmp and installs only on sha1 match; sha1 tool detection (sha1sum/shasum/sha1/openssl) with refusal of unverified downloads when none present; optional runtime-name argument added (fetch <name>) - fixes: powershell "stored hash from sha1sums.txt" printed an undefined variable ($storedhash -> $stored_sha1); bash MSYS branch invalid assignment (interp = ...); candidate listings for run/list/use exclude .tmp leftovers and non-runtime files (a stray .txt could previously be selected by run) - runtime scriptset sources relocated to src/scriptapps/bin (proper home alongside getzig.*); bin/runtime.cmd regenerated via scriptwrap from the new location - roundtrip test path updated, suite green on 9.0.3 (8 pass + unix-gated skip), runtime.bash syntax-checked under bash and zsh - verified live on windows: multiple-runtimes-no-active errors with candidates (previously silently launched tksfe-twapi-x64), use/list/run against tclsfe-x64 and env override to tclsh902z, fetch no-steal + first-fetch auto-set, checksum-match no-download path - docs: bin/AGENTS.md documents the fetch/use/run contract and updated generated-polyglot workflow paths; src/scriptapps/AGENTS.md scriptset-home wording updated Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.commaster
10 changed files with 788 additions and 219 deletions
@ -1,3 +1,3 @@
|
||||
[project] |
||||
name = "punkshell" |
||||
version = "0.6.0" |
||||
version = "0.7.0" |
||||
|
||||
@ -0,0 +1,309 @@
|
||||
|
||||
wdir="$(pwd)"; [ "$(pwd)" = "/" ] && wdir="" |
||||
case "$0" in |
||||
/*) scriptpath="${0}";; |
||||
*) scriptpath="$wdir/${0#./}";; |
||||
esac |
||||
scriptdir="${scriptpath%/*}" |
||||
scriptdir=$(realpath $scriptdir) |
||||
scriptpath=$(realpath $scriptpath) |
||||
basename=$(basename "$scriptpath") #e.g fetchruntime.bash |
||||
scriptroot="${basename%.*}" #e.g "fetchruntime" |
||||
|
||||
url_kitbase="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" |
||||
runtime_available=0 |
||||
#$OSTYPE varies in capitalization across for example zsh and bash |
||||
#uname probably a more consistent bet |
||||
arch=$(uname -m) #machine/architecture |
||||
plat=$(uname -s) #platform/system |
||||
#even though most of the platform prongs are very similar, |
||||
#we keep the code separate so it can be tweaked easily for unexpected differences |
||||
#each prong sets archdir (local folder) and, where a runtime is published for the |
||||
#platform, rt_default (default runtime name). archtail (server folder) is derived |
||||
#from archdir below. |
||||
if [[ "$plat" = "Linux"* ]]; then |
||||
if [[ "$arch" = "x86_64"* ]]; then |
||||
archdir="${scriptdir}/runtime/linux-x86_64" |
||||
rt_default="tclkit-902-Linux64-intel-dyn" |
||||
runtime_available=1 |
||||
elif [[ "$arch" = "arm"* ]]; then |
||||
archdir="${scriptdir}/runtime/linux-arm" |
||||
rt_default="tclkit-902-Linux64-arm-dyn" |
||||
runtime_available=1 |
||||
else |
||||
archdir="${scriptdir}/runtime/linux-$arch" |
||||
fi |
||||
os="linux" |
||||
elif [[ "$plat" = "Darwin"* ]]; then |
||||
os="macosx" |
||||
#assumed to be Mach-O 'universal binaries' for both x86-64 and arm? - REVIEW |
||||
archdir="${scriptdir}/runtime/macosx" |
||||
rt_default="tclkit-902-Darwin64-dyn" |
||||
runtime_available=1 |
||||
elif [[ "$plat" = "FreeBSD"* ]]; then |
||||
archdir="${scriptdir}/runtime/freebsd-amd64" |
||||
os="freebsd" |
||||
elif [[ "$plat" == "DragonFly"* ]]; then |
||||
archdir="${scriptdir}/runtime/dragonflybsd-$arch" |
||||
os="dragonflybsd" |
||||
elif [[ "$plat" == "NetBSD"* ]]; then |
||||
archdir="${scriptdir}/runtime/netbsd-$arch" |
||||
os="netbsd" |
||||
elif [[ "$plat" == "OpenBSD"* ]]; then |
||||
archdir="${scriptdir}/runtime/openbsd-amd64" |
||||
os="openbsd" |
||||
elif [[ "$plat" == "MINGW32"* ]]; then |
||||
#REVIEW |
||||
os="win32" |
||||
archdir="${scriptdir}/runtime/win32-x86_64" |
||||
rt_default="tclsh902z.exe" |
||||
runtime_available=1 |
||||
elif [[ "$plat" == "MINGW64"* ]]; then |
||||
#REVIEW |
||||
os="win32" |
||||
archdir="${scriptdir}/runtime/win32-x86_64" |
||||
rt_default="tclsh902z.exe" |
||||
runtime_available=1 |
||||
elif [[ "$plat" == "CYGWIN_NT"* ]]; then |
||||
os="win32" |
||||
archdir="${scriptdir}/runtime/win32-x86_64" |
||||
rt_default="tclsh902z.exe" |
||||
runtime_available=1 |
||||
elif [[ "$plat" == "MSYS_NT"* ]]; then |
||||
echo MSYS |
||||
os="win32" |
||||
#use 'command -v' (shell builtin preferred over external which) |
||||
interp=`ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` |
||||
shellpath=`command -v $interp` |
||||
shellfolder="${shellpath%/*}" #avoid dependency on basename or dirname |
||||
#"c:/windows/system32/" is quite likely in the path ahead of msys,git etc. |
||||
#This breaks calls to various unix utils such as sed etc (wsl related?) |
||||
export PATH="$shellfolder${PATH:+:${PATH}}" |
||||
archdir="${scriptdir}/runtime/win32-x86_64" |
||||
rt_default="tclsh902z.exe" |
||||
runtime_available=1 |
||||
else |
||||
archdir="${scriptdir}/runtime/other" |
||||
os="other" |
||||
fi |
||||
archtail="${archdir##*/}" #server folder name e.g linux-x86_64 |
||||
active_file="${archdir}/active.toml" |
||||
|
||||
#sha1 of a file - tool varies by platform (sha1sum: linux/coreutils, shasum: macosx, |
||||
#sha1: BSDs, openssl: common fallback). Empty result means no tool available. |
||||
sha1_of() { |
||||
if command -v sha1sum >/dev/null 2>&1; then |
||||
sha1sum "$1" | awk '{print $1}' |
||||
elif command -v shasum >/dev/null 2>&1; then |
||||
shasum -a 1 "$1" | awk '{print $1}' |
||||
elif command -v sha1 >/dev/null 2>&1; then |
||||
sha1 -q "$1" |
||||
elif command -v openssl >/dev/null 2>&1; then |
||||
openssl dgst -sha1 -r "$1" | awk '{print $1}' |
||||
else |
||||
echo "" |
||||
fi |
||||
} |
||||
lcase() { printf '%s' "$1" | tr 'A-F' 'a-f'; } |
||||
|
||||
#active runtime marker: constrained single-key toml, per platform folder |
||||
#(local per-machine state - ignored by git and fossil) |
||||
get_active() { |
||||
if [[ -f "$active_file" ]]; then |
||||
sed -n 's/^[[:space:]]*active[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$active_file" | head -n 1 |
||||
fi |
||||
} |
||||
set_active() { |
||||
printf 'active = "%s"\n' "$1" > "$active_file" |
||||
echo "active runtime for $archtail set to: $1 (recorded in $active_file)" |
||||
} |
||||
#installed runtime candidates - exclude build copies and non-runtime files |
||||
list_candidates() { |
||||
if [[ -d "$archdir" ]]; then |
||||
ls -1 "$archdir" | grep -v '_BUILDCOPY$' | grep -v '\.txt$' | grep -v '\.toml$' | grep -v '\.tm$' | grep -v '\.tmp$' |
||||
fi |
||||
} |
||||
|
||||
case "$1" in |
||||
"fetch") |
||||
runtime="$rt_default" |
||||
if [[ -n "$2" ]]; then |
||||
runtime="$2" |
||||
fi |
||||
if [[ ( "$runtime_available" -eq 1 || -n "$2" ) && "$archtail" != "other" && -n "$runtime" ]]; then |
||||
url="${url_kitbase}/${archtail}/${runtime}" |
||||
output="${archdir}/${runtime}" |
||||
sha1url="${url_kitbase}/${archtail}/sha1sums.txt" |
||||
sha1local="${archdir}/sha1sums.txt" |
||||
mkdir -p "$archdir" |
||||
echo "Fetching $sha1url" |
||||
if ! curl -fsSL --output "$sha1local" "$sha1url"; then |
||||
echo "An error occurred while downloading $sha1url" |
||||
if [[ -f "$output" ]]; then |
||||
echo "A runtime is available at $output - but we failed to retrieve the list of sha1sums from the server" |
||||
echo "Unable to check for updated version at this time." |
||||
else |
||||
echo "Please retry - or manually download a runtime from ${url_kitbase}/${archtail} and verify checksums" |
||||
fi |
||||
exit 1 |
||||
fi |
||||
#sha1sums.txt line format: <sha1> *<filename> (binary indicator) |
||||
#(dots in $runtime are regex-any here - acceptable for these filenames) |
||||
stored_sha1=$(grep -E "^[0-9a-fA-F]{40} \*${runtime}\$" "$sha1local" | head -n 1 | cut -d' ' -f1) |
||||
if [[ -z "$stored_sha1" ]]; then |
||||
echo "Unable to locate hash for $runtime in $sha1local - Aborting" |
||||
echo "Please download and verify manually (or check available names with: $0 list)" |
||||
exit 1 |
||||
fi |
||||
probe=$(sha1_of "$sha1local") |
||||
if [[ -z "$probe" ]]; then |
||||
echo "No sha1 tool found (tried sha1sum, shasum, sha1, openssl) - refusing unverified download." |
||||
echo "Please install one (e.g coreutils or openssl) and retry." |
||||
exit 1 |
||||
fi |
||||
need_download=1 |
||||
if [[ -f "$output" ]]; then |
||||
echo "Runtime already found at $output" |
||||
echo "Checking sha1 checksum of local file versus sha1 of server file" |
||||
local_sha1=$(sha1_of "$output") |
||||
if [[ "$(lcase "$local_sha1")" == "$(lcase "$stored_sha1")" ]]; then |
||||
need_download=0 |
||||
echo "Local copy of runtime at $output matches sha1 checksum of file on server." |
||||
echo "No download required" |
||||
else |
||||
echo "$runtime on server has different sha1 hash - Download required" |
||||
fi |
||||
else |
||||
echo "$runtime not found locally - Download required" |
||||
fi |
||||
if [[ "$need_download" -eq 1 ]]; then |
||||
echo "Downloading from $url ..." |
||||
if ! curl -fSL --output "${output}.tmp" "$url"; then |
||||
echo "Error: Failed to download to ${output}.tmp" |
||||
exit 1 |
||||
fi |
||||
echo "comparing sha1 checksum of downloaded file with data in sha1sums.txt" |
||||
new_sha1=$(sha1_of "${output}.tmp") |
||||
if [[ "$(lcase "$new_sha1")" == "$(lcase "$stored_sha1")" ]]; then |
||||
echo "sha1 checksum ok" |
||||
mv -f "${output}.tmp" "$output" |
||||
chmod +x "$output" |
||||
echo "Runtime is available at $output" |
||||
if [[ "$plat" == "Linux"* ]]; then |
||||
echo "Please ensure libxFt.so.2 is available" |
||||
echo "e.g on Ubuntu: sudo apt-get install libxft2" |
||||
fi |
||||
else |
||||
echo "WARNING! sha1 of downloaded file at ${output}.tmp does not match stored sha1 from sha1sums.txt" |
||||
echo "The .tmp file has been left in place for inspection - it has NOT been installed." |
||||
exit 1 |
||||
fi |
||||
fi |
||||
#first fetch establishes the active runtime; later fetches never steal it |
||||
if [[ -z "$(get_active)" ]]; then |
||||
set_active "$runtime" |
||||
fi |
||||
else |
||||
echo "No default runtime currently published for $os ($archtail)" |
||||
echo "If one exists on the server, name it explicitly: $0 fetch <runtimename>" |
||||
fi |
||||
;; |
||||
"list") |
||||
if [[ -d "$archdir" ]]; then |
||||
candidates=$(list_candidates) |
||||
active=$(get_active) |
||||
count=$(printf '%s\n' "$candidates" | grep -c . ) |
||||
echo "$count runtime(s) in $archdir" |
||||
for f in $candidates; do |
||||
if [[ "$f" == "$active" ]]; then |
||||
echo "* $f (active)" |
||||
else |
||||
echo " $f" |
||||
fi |
||||
done |
||||
if [[ -n "$active" && ! -f "$archdir/$active" ]]; then |
||||
echo "WARNING: active runtime '$active' (from $active_file) is not present - use '$0 use <name>' to reselect" |
||||
fi |
||||
else |
||||
echo "No runtimes available in $archdir" |
||||
echo " Use '$0 fetch' to install." |
||||
fi |
||||
;; |
||||
"use") |
||||
if [[ -z "$2" ]]; then |
||||
echo "Usage: $0 use <runtimename>" |
||||
echo "Installed candidates:" |
||||
for f in $(list_candidates); do |
||||
echo " $f" |
||||
done |
||||
active=$(get_active) |
||||
if [[ -n "$active" ]]; then |
||||
echo "Currently active: $active" |
||||
fi |
||||
exit 1 |
||||
fi |
||||
case "$2" in |
||||
*_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp) |
||||
echo "'$2' is not a selectable runtime" |
||||
exit 1 |
||||
;; |
||||
esac |
||||
if [[ ! -f "$archdir/$2" ]]; then |
||||
echo "No runtime named '$2' found in $archdir" |
||||
echo "Installed candidates:" |
||||
for f in $(list_candidates); do |
||||
echo " $f" |
||||
done |
||||
exit 1 |
||||
fi |
||||
set_active "$2" |
||||
;; |
||||
"run") |
||||
#resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single |
||||
#installed candidate - otherwise error with candidates (no last-in-list guessing) |
||||
activeruntime="" |
||||
if [[ -n "$PUNK_ACTIVE_RUNTIME" ]]; then |
||||
activeruntime="$PUNK_ACTIVE_RUNTIME" |
||||
if [[ ! -f "$archdir/$activeruntime" ]]; then |
||||
echo "PUNK_ACTIVE_RUNTIME '$activeruntime' not found in $archdir" |
||||
exit 1 |
||||
fi |
||||
else |
||||
activeruntime=$(get_active) |
||||
if [[ -n "$activeruntime" && ! -f "$archdir/$activeruntime" ]]; then |
||||
echo "active runtime '$activeruntime' (from $active_file) is not present in $archdir" |
||||
echo "Reselect with: $0 use <name> (or fetch it: $0 fetch $activeruntime)" |
||||
exit 1 |
||||
fi |
||||
if [[ -z "$activeruntime" ]]; then |
||||
candidates=$(list_candidates) |
||||
count=$(printf '%s\n' "$candidates" | grep -c . ) |
||||
if [[ "$count" -eq 1 ]]; then |
||||
activeruntime="$candidates" |
||||
elif [[ "$count" -eq 0 ]]; then |
||||
echo "No runtimes seem to be installed in $archdir" |
||||
echo "Please use '$0 fetch' to install." |
||||
exit 1 |
||||
else |
||||
echo "Multiple runtimes installed and no active runtime selected." |
||||
echo "Select one with: $0 use <name>" |
||||
echo "Installed candidates:" |
||||
for f in $candidates; do |
||||
echo " $f" |
||||
done |
||||
exit 1 |
||||
fi |
||||
fi |
||||
fi |
||||
activeruntime_fullpath="$archdir/$activeruntime" |
||||
#echo "using $activeruntime_fullpath" |
||||
shift |
||||
#echo "args: $@" |
||||
$activeruntime_fullpath "$@" |
||||
;; |
||||
*) |
||||
echo "Usage: $0 {fetch|list|use|run}" |
||||
echo "received $@" |
||||
exit 1 |
||||
;; |
||||
esac |
||||
@ -1,139 +0,0 @@
|
||||
|
||||
wdir="$(pwd)"; [ "$(pwd)" = "/" ] && wdir="" |
||||
case "$0" in |
||||
/*) scriptpath="${0}";; |
||||
*) scriptpath="$wdir/${0#./}";; |
||||
esac |
||||
scriptdir="${scriptpath%/*}" |
||||
scriptdir=$(realpath $scriptdir) |
||||
scriptpath=$(realpath $scriptpath) |
||||
basename=$(basename "$scriptpath") #e.g fetchruntime.bash |
||||
scriptroot="${basename%.*}" #e.g "fetchruntime" |
||||
|
||||
url_kitbase="https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" |
||||
runtime_available=0 |
||||
#$OSTYPE varies in capitalization across for example zsh and bash |
||||
#uname probably a more consistent bet |
||||
arch=$(uname -m) #machine/architecture |
||||
plat=$(uname -s) #platform/system |
||||
#even though most of the platform prongs are very similar, |
||||
#we keep the code separate so it can be tweaked easily for unexpected differences |
||||
if [[ "$plat" = "Linux"* ]]; then |
||||
if [[ "$arch" = "x86_64"* ]]; then |
||||
url="${url_kitbase}/linux-x86_64/tclkit-902-Linux64-intel-dyn" |
||||
archdir="${scriptdir}/runtime/linux-x86_64" |
||||
output="${archdir}/tclkit-902-Linux64-intel-dyn" |
||||
runtime_available=1 |
||||
elif [[ "$arch" = "arm"* ]]; then |
||||
url="${url_kitbase}/linux-arm/tclkit-902-Linux64-arm-dyn" |
||||
archdir="${scriptdir}/runtime/linux-arm" |
||||
output="${archdir}/tclkit-902-Linux64-arm-dyn" |
||||
runtime_available=1 |
||||
else |
||||
archdir="${scriptdir}/runtime/linux-$arch" |
||||
fi |
||||
os="linux" |
||||
elif [[ "$plat" = "Darwin"* ]]; then |
||||
os="macosx" |
||||
#assumed to be Mach-O 'universal binaries' for both x86-64 and arm? - REVIEW |
||||
url="${url_kitbase}/macosx/tclkit-902-Darwin64-dyn" |
||||
archdir="${scriptdir}/runtime/macosx/" |
||||
output="${archdir}/tclkit-902-Darwin64-dyn" |
||||
runtime_available=1 |
||||
elif [[ "$plat" = "FreeBSD"* ]]; then |
||||
archdir="${scriptdir}/runtime/freebsd-amd64" |
||||
os="freebsd" |
||||
elif [[ "$plat" == "DragonFly"* ]]; then |
||||
archdir="${scriptdir}/runtime/dragonflybsd-$arch" |
||||
os="dragonflybsd" |
||||
elif [[ "$plat" == "NetBSD"* ]]; then |
||||
archdir="${scriptdir}/runtime/netbsd-$arch" |
||||
os="netbsd" |
||||
elif [[ "$plat" == "OpenBSD"* ]]; then |
||||
archdir="${scriptdir}/runtime/openbsd-amd64" |
||||
os="openbsd" |
||||
elif [[ "$plat" == "MINGW32"* ]]; then |
||||
#REVIEW |
||||
os="win32" |
||||
url="${url_kitbase}/win32-x86_64/tclsh902z.exe" |
||||
archdir="${scriptdir}/runtime/win32-x86_64/" |
||||
output="${archdir}/tclsh902z.exe" |
||||
runtime_available=1 |
||||
elif [[ "$plat" == "MINGW64"* ]]; then |
||||
#REVIEW |
||||
os="win32" |
||||
url="${url_kitbase}/win32-x86_64/tclsh902z.exe" |
||||
archdir="${scriptdir}/runtime/win32-x86_64/" |
||||
output="${archdir}/tclsh902z.exe" |
||||
runtime_available=1 |
||||
elif [[ "$plat" == "CYGWIN_NT"* ]]; then |
||||
os="win32" |
||||
url="${url_kitbase}/win32-x86_64/tclsh902z.exe" |
||||
archdir="${scriptdir}/runtime/win32-x86_64/" |
||||
output="${archdir}/tclsh902z.exe" |
||||
runtime_available=1 |
||||
elif [[ "$plat" == "MSYS_NT"* ]]; then |
||||
echo MSYS |
||||
os="win32" |
||||
#use 'command -v' (shell builtin preferred over external which) |
||||
interp = `ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` |
||||
shellpath=`command -v $interp` |
||||
shellfolder="${shellpath%/*}" #avoid dependency on basename or dirname |
||||
#"c:/windows/system32/" is quite likely in the path ahead of msys,git etc. |
||||
#This breaks calls to various unix utils such as sed etc (wsl related?) |
||||
export PATH="$shellfolder${PATH:+:${PATH}}" |
||||
url="${url_kitbase}/win32-x86_64/tclsh902z.exe" |
||||
archdir="${scriptdir}/runtime/win32-x86_64" |
||||
output="${archdir}/tclsh902z.exe" |
||||
runtime_available=1 |
||||
else |
||||
archdir="${scriptdir}/runtime/other" |
||||
os="other" |
||||
fi |
||||
|
||||
case "$1" in |
||||
"fetch") |
||||
|
||||
if [[ "$runtime_available" -eq 1 ]]; then |
||||
#test win32 |
||||
mkdir -p $archdir |
||||
echo "Attempting to download $url" |
||||
#wget $url -O $output |
||||
curl -SL --output "$output" "$url" |
||||
if [[ $? -eq 0 ]]; then |
||||
echo "File downloaded to $output" |
||||
chmod +x $output |
||||
if [[ "$plat" == "Linux" ]]; then |
||||
echo "Please ensure libxFt.so.2 is available" |
||||
echo "e.g on Ubuntu: sudo apt-get install libxft2" |
||||
fi |
||||
else |
||||
echo "Error: Failed to download to $output" |
||||
fi |
||||
else |
||||
echo "No runtime currently available for $os" |
||||
fi |
||||
;; |
||||
"list") |
||||
if [[ -d "$archdir" ]]; then |
||||
echo "$(ls $archdir -1 | grep -v '_BUILDCOPY$' | wc -l) files in $archdir" |
||||
echo $(ls $archdir -1 | grep -v '_BUILDCOPY$') |
||||
else |
||||
echo -e "No runtimes available in $archdir\n Use '$0 fetch' to install." |
||||
fi |
||||
;; |
||||
"run") |
||||
#todo - lookup active runtime for os-arch from .toml file |
||||
activeruntime=$(ls $archdir -1 | grep -v '_BUILDCOPY$' | tail -n 1) |
||||
activeruntime_fullpath="$archdir/$activeruntime" |
||||
#echo "using $activeruntime_fullpath" |
||||
shift |
||||
#echo "args: $@" |
||||
$activeruntime_fullpath "$@" |
||||
;; |
||||
*) |
||||
echo "Usage: $0 {fetch|list|run}" |
||||
echo "received $@" |
||||
exit 1 |
||||
;; |
||||
esac |
||||
Loading…
Reference in new issue