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 punk-runtime.bash
scriptroot="${basename%.*}" #e.g "punk-runtime"
#artifact server base url - overridable for mirrors/testing
url_kitbase="${PUNKBIN_URL:-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 local_platform (the CANONICAL punkshell platform-DIR name - see
#punk::platform / 'help platforms': cpu tokens normalized amd64->x86_64,
#aarch64->arm64) and, where a runtime is published for the platform, rt_default
#(default runtime name).
narch="$arch"
case "$narch" in
amd64) narch="x86_64";;
aarch64|arm64) narch="arm64";;
esac
if [[ "$plat" = "Linux"* ]]; then
if [[ "$arch" = "x86_64"* ]]; then
local_platform="linux-x86_64"
rt_default="tclkit-902-Linux64-intel-dyn"
runtime_available=1
elif [[ "$narch" = "arm64" ]]; then
#canonical arm64 (aarch64). punkbin's existing arm kit predates the
#arm64 name and sits in linux-arm - no fetch default here until a
#linux-arm64 punkbin folder exists; name runtimes explicitly.
local_platform="linux-arm64"
elif [[ "$arch" = "arm"* ]]; then
#32-bit arm
local_platform="linux-arm"
rt_default="tclkit-902-Linux64-arm-dyn"
runtime_available=1
else
local_platform="linux-$narch"
fi
os="linux"
elif [[ "$plat" = "Darwin"* ]]; then
os="macosx"
#universal (multi-arch) binaries - the runtime tier keeps ONE macosx folder
#(per-arch macosx-x86_64/macosx-arm64 names serve the lib tree tier)
local_platform="macosx"
rt_default="tclkit-902-Darwin64-dyn"
runtime_available=1
elif [[ "$plat" = "FreeBSD"* ]]; then
#canonical names (2026-07-22): freebsd-x86_64 (punkbin's actual folder; this
#payload previously said freebsd-amd64) / freebsd-arm64
local_platform="freebsd-$narch"
os="freebsd"
elif [[ "$plat" == "DragonFly"* ]]; then
local_platform="dragonflybsd-$narch"
os="dragonflybsd"
elif [[ "$plat" == "NetBSD"* ]]; then
local_platform="netbsd-$narch"
os="netbsd"
elif [[ "$plat" == "OpenBSD"* ]]; then
local_platform="openbsd-$narch"
os="openbsd"
elif [[ "$plat" == "MINGW32"* ]]; then
#REVIEW
os="win32"
local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
elif [[ "$plat" == "MINGW64"* ]]; then
#REVIEW
os="win32"
local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
elif [[ "$plat" == "CYGWIN_NT"* ]]; then
os="win32"
local_platform="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}}"
local_platform="win32-x86_64"
rt_default="tclsh902z.exe"
runtime_available=1
else
local_platform="other"
os="other"
fi
#-- platform resolution (G-105 cross-build staging) ---------------------------
#The prongs above detect the LOCAL platform. fetch/list/use accept
#'-platform
' to aim at another punkbin platform-dir (cross-build staging /
#provisioning); resolution: -platform arg > PUNK_RUNTIME_PLATFORM env > local.
#Values are punkbin platform-dir names (e.g win32-x86_64, linux-x86_64, macosx)
#- NOT zig triples (the buildsuite maps triples to platform dirs at build time).
#Validation is shape-only: the server's sha1sums/404 is the truth for what
#exists. 'run' is LOCAL ONLY (foreign binaries are not runnable here) - only a
#LEADING -platform is rejected there; later args belong to the launched runtime.
action="${1:-}"
[[ $# -gt 0 ]] && shift
platform_opt=""
case "$action" in
fetch|list|use|platforms)
newargs=()
while [[ $# -gt 0 ]]; do
if [[ "$1" == "-platform" ]]; then
shift
if [[ $# -eq 0 ]]; then
echo "option -platform requires a value (a punkbin platform-dir name, e.g linux-x86_64)"
exit 1
fi
platform_opt="$1"
else
newargs+=("$1")
fi
shift
done
set -- ${newargs[@]+"${newargs[@]}"}
;;
run)
if [[ "${1:-}" == "-platform" ]]; then
echo "'run' launches the LOCAL platform's active runtime - it takes no -platform option"
echo "(foreign-platform folders are cross-build staging; deploy them to their platform to run)"
exit 1
fi
;;
esac
platform="${platform_opt:-${PUNK_RUNTIME_PLATFORM:-$local_platform}}"
if ! [[ "$platform" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
echo "invalid platform name '$platform' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)"
exit 1
fi
is_local=0
[[ "$platform" == "$local_platform" ]] && is_local=1
archdir="${scriptdir}/runtime/${platform}"
archtail="$platform" #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 directories, build copies and
#non-runtime files (ps1-payload parity: files only, same extension excludes)
list_candidates() {
local f
if [[ -d "$archdir" ]]; then
for f in "$archdir"/*; do
[[ -f "$f" ]] || continue
f="${f##*/}"
case "$f" in
*_BUILDCOPY*|*.txt|*.toml|*.tm|*.tmp|*.log) continue;;
esac
printf '%s\n' "$f"
done
fi
}
#name minus a .exe suffix only - dotted tcl patchlevels (tclsh9.0.5-punk) make
#generic last-dot extension stripping wrong for extensionless unix names
rootname_of() {
case "$1" in
*.exe|*.EXE) printf '%s' "${1%.???}";;
*) printf '%s' "$1";;
esac
}
#G-103 artifact metadata: a runtime may carry a .toml beside it (emitted
#by the buildsuite kit-family-artifacts step / fetched from punkbin). Prints a
#short "[variant=... tcl=... rN ...]" summary for list output, "" when absent.
metadata_summary() {
local exename="$1" tomlfile parts variant tclpatch revision piperepl artname target
tomlfile="$archdir/$(rootname_of "$exename").toml"
[[ -f "$tomlfile" ]] || return 0
variant=$(sed -n 's/^[[:space:]]*variant[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
tclpatch=$(sed -n 's/^[[:space:]]*tcl_patchlevel[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
revision=$(sed -n 's/^[[:space:]]*revision[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$tomlfile" | head -n 1)
piperepl=$(sed -n 's/^[[:space:]]*piperepl[[:space:]]*=[[:space:]]*\(true\|false\).*/\1/p' "$tomlfile" | head -n 1)
artname=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
target=$(sed -n 's/^[[:space:]]*target[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$tomlfile" | head -n 1)
parts=""
[[ -n "$variant" ]] && parts="$parts variant=$variant"
[[ -n "$tclpatch" ]] && parts="$parts tcl=$tclpatch"
[[ -n "$revision" ]] && parts="$parts r$revision"
if [[ "$piperepl" == "true" ]]; then parts="$parts piperepl=on"
elif [[ "$piperepl" == "false" ]]; then parts="$parts piperepl=off"; fi
#a materialized working copy records which immutable artifact it came from
[[ -n "$artname" && "$artname" != "$exename" ]] && parts="$parts from=$artname"
#integrity flag: a runtime filed under a platform folder its metadata says it
#was not built for (cross-platform fetch/staging misfiling)
[[ -n "$target" && "$target" != "$archtail" ]] && parts="$parts !TARGET-MISMATCH:$target"
[[ -n "$parts" ]] && printf '[%s]' "${parts# }"
}
#operator help (the 'help' action; the no-args case shows show_usage only).
#Keep in sync with the ps1 payload's Show-PunkRuntimeHelp/Show-PunkRuntimeUsage.
show_usage() {
echo "Usage: $0 {fetch|list|use|run|platforms|help}"
echo " fetch ?? ?-platform ? download+verify a runtime from punkbin"
echo " list ?-remote? ?-platform
? installed (or server) runtimes + metadata"
echo " use ?-platform ? select active / materialize -r artifact"
echo " run ?args...? launch the active local-platform runtime"
echo " platforms ?-remote? local platform folders / platforms the server serves"
echo " help full help (options, env vars, examples)"
}
show_help() {
echo "punk-runtime - manage the plain Tcl runtimes under bin/runtime//"
echo ""
show_usage
echo ""
echo "Actions:"
echo " fetch ?? ?-platform ?"
echo " Download a runtime from the punkbin artifact server into"
echo " bin/runtime/
/ with sha1 verification against the server's"
echo " sha1sums.txt. -r-named family artifacts also fetch their"
echo " .toml metadata record. Omitting fetches the"
echo " local platform's default runtime; a FOREIGN platform fetch"
echo " requires an explicit ."
echo " list ?-remote? ?-platform ?"
echo " List installed runtimes with artifact-metadata summaries"
echo " (variant, tcl patchlevel, revision, piperepl policy, source"
echo " artifact of a materialized copy; a !TARGET-MISMATCH flag marks"
echo " a runtime filed under the wrong platform folder). -remote"
echo " compares local runtimes against the server's sha1sums."
echo " use ?-platform ?"
echo " Select the runtime that 'run' launches (records active.toml in"
echo " the platform folder). An immutable -r ARTIFACT name is"
echo " MATERIALIZED into its WORKING name (name minus -r - what"
echo " mapvfs and projects reference), metadata toml copied alongside."
echo " run ?args...?"
echo " Launch the ACTIVE runtime of the LOCAL platform, passing args."
echo " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole"
echo " installed candidate. Takes no -platform (foreign binaries are"
echo " not runnable here)."
echo " platforms ?-remote?"
echo " List local platform folders under bin/runtime/. With -remote,"
echo " list the platforms the artifact server serves - read from the"
echo " server's platforms.txt discovery manifest (part of the punkbin"
echo " layout; third-party mirrors carry the same file), with local"
echo " presence marked. Servers without the manifest get an actionable"
echo " message (name platforms explicitly with -platform)."
echo ""
echo "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64,"
echo " macosx) - not zig triples. Default is the local platform; override"
echo " order: -platform argument > PUNK_RUNTIME_PLATFORM env var."
echo " Foreign-platform folders serve cross-build staging and provisioning:"
echo " active.toml travels with the folder when it is deployed to its real"
echo " platform (unix exec bits are restored at deploy time - e.g rsync/tar/"
echo " chmod on the receiving side)."
echo ""
echo "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM,"
echo " PUNK_ACTIVE_RUNTIME (run-time selection override)"
echo ""
echo "Examples:"
echo " $0 platforms -remote"
echo " $0 fetch tclsh9.0.5-punk-r1.exe"
echo " $0 use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)"
echo " $0 list -remote -platform linux-x86_64"
echo " $0 fetch tclsh9.0.5-punk-r1 -platform linux-x86_64"
echo " $0 use tclsh9.0.5-punk-r1 -platform linux-x86_64"
echo " $0 run myscript.tcl arg1 arg2"
}
#G-103 artifact-tier names carry -r before the (optional) .exe suffix; prints
#the WORKING name (artifact minus -r) when the name is artifact-tier, else ""
working_name_of() {
local w
w=$(printf '%s' "$1" | sed -E 's/^(.*)-r[0-9]+(\.[Ee][Xx][Ee])?$/\1\2/')
if [[ "$w" != "$1" ]]; then printf '%s' "$w"; fi
}
case "$action" in
"fetch")
runtime="$rt_default"
if [[ -n "${1:-}" ]]; then
runtime="$1"
elif [[ "$is_local" -ne 1 ]]; then
#fetch-name defaults only make sense for the platform we are standing
#on - a foreign-platform fetch (cross-build staging) names its runtime
echo "No default runtime for foreign platform '$platform' - name it explicitly:"
echo " $0 fetch -platform $platform"
echo " (see available names: $0 list -remote -platform $platform)"
exit 1
fi
if [[ ( "$runtime_available" -eq 1 || -n "${1:-}" ) && "$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: * (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
#G-103: family artifacts (-r names) carry a metadata toml alongside
#on the server - fetch it too; absence is fine (pre-family runtimes)
if [[ -n "$(working_name_of "$runtime")" ]]; then
tomlname="$(rootname_of "$runtime").toml"
if curl -fsSL --output "${archdir}/${tomlname}" "${url_kitbase}/${archtail}/${tomlname}"; then
echo "artifact metadata saved at ${archdir}/${tomlname}"
else
rm -f "${archdir}/${tomlname}"
echo "no artifact metadata toml on server for $runtime (ok for pre-family runtimes)"
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 "
fi
;;
"list")
if [[ "${1:-}" == "-remote" ]]; then
#compare local runtimes with those listed on the artifact server
#(functional parity with the powershell payload's 'list -remote')
sha1url="${url_kitbase}/${archtail}/sha1sums.txt"
sha1local="${archdir}/sha1sums.txt"
mkdir -p "$archdir"
if curl -fsSL --output "$sha1local" "$sha1url"; then
echo "Fetched $sha1url"
elif [[ -f "$sha1local" ]]; then
echo "WARNING: could not fetch $sha1url - using cached copy at $sha1local"
else
echo "Unable to fetch $sha1url and no cached copy available"
exit 1
fi
probe=$(sha1_of "$sha1local")
if [[ -z "$probe" ]]; then
echo "No sha1 tool found (tried sha1sum, shasum, sha1, openssl) - cannot compare local runtimes."
exit 1
fi
echo "-----------------------------------------------------------------------"
echo "Runtimes for $archtail"
echo "Local $archdir"
echo "Remote ${url_kitbase}/${archtail}"
echo "-----------------------------------------------------------------------"
echo "Local Remote"
echo "-----------------------------------------------------------------------"
for f in $(list_candidates); do
local_sha1=$(lcase "$(sha1_of "$archdir/$f")")
stored_sha1=$(grep -E "^[0-9a-fA-F]{40} \*${f}\$" "$sha1local" | head -n 1 | cut -d' ' -f1)
if [[ -z "$stored_sha1" ]]; then
rhs="(not listed on server)"
elif [[ "$local_sha1" == "$(lcase "$stored_sha1")" ]]; then
rhs="Same version"
else
rhs="UPDATE AVAILABLE"
fi
printf '%-35s %s\n' "$f" "$rhs"
done
#remote-only entries
while IFS= read -r line; do
rname=$(printf '%s' "$line" | sed -n 's/^[0-9a-fA-F]\{40\} \*\(.*\)$/\1/p')
if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then
printf '%-35s %s\n' "-" "$rname"
fi
done < "$sha1local"
echo "-----------------------------------------------------------------------"
else
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
meta=$(metadata_summary "$f")
if [[ "$f" == "$active" ]]; then
printf '* %-35s (active) %s\n' "$f" "$meta"
else
printf ' %-35s %s\n' "$f" "$meta"
fi
done
if [[ -n "$active" && ! -f "$archdir/$active" ]]; then
echo "WARNING: active runtime '$active' (from $active_file) is not present - use '$0 use ' to reselect"
fi
echo "Use: '$0 list -remote' to compare local runtimes with those available on the artifact server"
echo "Use: '$0 use ' to select the runtime that 'run' launches"
echo "Use: '$0 use -name>' to materialize an immutable -r artifact into its working name and select it"
else
echo "No runtimes available in $archdir"
echo " Use '$0 fetch' to install."
fi
fi
;;
"use")
#with -platform: manage THAT platform folder's selection/materialization
#(cross-build staging - active.toml travels with the folder when deployed;
#'run' on this machine only ever consults the local platform)
if [[ -z "${1:-}" ]]; then
echo "Usage: $0 use ?-platform ?"
echo "Installed candidates ($platform):"
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 "$1" in
*_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp)
echo "'$1' is not a selectable runtime"
exit 1
;;
esac
if [[ ! -f "$archdir/$1" ]]; then
echo "No runtime named '$1' found in $archdir"
echo "Installed candidates:"
for f in $(list_candidates); do
echo " $f"
done
exit 1
fi
#G-103 artifact-tier names (-r, immutable): 'use' MATERIALIZES the
#artifact into its WORKING name (name minus -r - what mapvfs and
#projects reference), copies its metadata toml alongside, and selects
#the working name. Republishing artifacts never churns consumers.
#(chmod is a no-op when staging unix runtimes from windows filesystems -
#exec bits are restored at deploy time, e.g rsync/tar/chmod on the guest.)
working=$(working_name_of "$1")
if [[ -n "$working" ]]; then
cp -f "$archdir/$1" "$archdir/$working"
chmod +x "$archdir/$working"
srctoml="$archdir/$(rootname_of "$1").toml"
desttoml="$archdir/$(rootname_of "$working").toml"
if [[ -f "$srctoml" ]]; then
cp -f "$srctoml" "$desttoml"
echo "materialized $working from artifact $1 (metadata toml copied alongside)"
else
echo "materialized $working from artifact $1 (no metadata toml found beside the artifact)"
fi
set_active "$working"
else
set_active "$1"
fi
;;
"run")
#resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single
#installed candidate - otherwise error with candidates (no last-in-list guessing).
#LOCAL platform only: the -platform arg is rejected in the scan above, and a
#PUNK_RUNTIME_PLATFORM env override is likewise ignored here (foreign
#binaries are not runnable; the env override serves list/fetch/use).
if [[ "$is_local" -ne 1 ]]; then
archdir="${scriptdir}/runtime/${local_platform}"
active_file="${archdir}/active.toml"
fi
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 (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 "
echo "Installed candidates:"
for f in $candidates; do
echo " $f"
done
exit 1
fi
fi
fi
activeruntime_fullpath="$archdir/$activeruntime"
#echo "using $activeruntime_fullpath"
#(the action was already shifted off during the option scan - "$@" is
#exactly the runtime's argument list)
$activeruntime_fullpath "$@"
;;
"platforms")
#enumerate platform folders: local (bin/runtime/*) and, with -remote, the
#server's platforms.txt discovery manifest (raw-file servers have no
#directory listing - the manifest is part of the punkbin layout contract;
#third-party mirrors carry the same file). -platform is ignored here
#(this action enumerates ALL platforms).
rtroot="${scriptdir}/runtime"
localdirs=""
if [[ -d "$rtroot" ]]; then
localdirs=$(ls -1 "$rtroot" 2>/dev/null | while read -r d; do [[ -d "$rtroot/$d" ]] && echo "$d"; done)
fi
if [[ "${1:-}" == "-remote" ]]; then
manifesturl="${url_kitbase}/platforms.txt"
manifestlocal="${rtroot}/platforms.txt"
mkdir -p "$rtroot"
if curl -fsSL --output "$manifestlocal" "$manifesturl"; then
echo "Fetched $manifesturl"
elif [[ -f "$manifestlocal" ]]; then
echo "WARNING: could not fetch $manifesturl - using cached copy at $manifestlocal"
else
echo "No platforms.txt available from $manifesturl"
echo "(pre-convention punkbin or third-party mirror without the discovery manifest)"
echo "Name platforms explicitly with -platform; canonical names: 'help platforms' in the punk shell"
exit 1
fi
echo "-----------------------------------------------------------------------"
echo "Platforms served by ${url_kitbase}"
echo "-----------------------------------------------------------------------"
remoteplatforms=""
while IFS= read -r line; do
line="${line#"${line%%[![:space:]]*}"}"
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
remoteplatforms="$remoteplatforms $line"
marks=""
[[ "$line" == "$local_platform" ]] && marks="local platform"
if printf '%s\n' $localdirs | grep -qx "$line"; then
[[ -n "$marks" ]] && marks="$marks, "
marks="${marks}local dir present"
fi
if [[ -n "$marks" ]]; then
printf ' %-25s (%s)\n' "$line" "$marks"
else
printf ' %-25s\n' "$line"
fi
done < "$manifestlocal"
for d in $localdirs; do
if ! printf '%s\n' $remoteplatforms | grep -qx "$d"; then
printf ' %-25s (local dir only - not served remotely)\n' "$d"
fi
done
echo "-----------------------------------------------------------------------"
echo "Use: '$0 list -remote -platform ' to see a platform's runtimes"
else
echo "-----------------------------------------------------------------------"
echo "Local platform folders under $rtroot"
echo "-----------------------------------------------------------------------"
if [[ -z "$localdirs" ]]; then
echo " (none - 'fetch' creates the local platform's folder)"
fi
for d in $localdirs; do
if [[ "$d" == "$local_platform" ]]; then
printf '* %-25s (local platform)\n' "$d"
else
printf ' %-25s\n' "$d"
fi
done
echo "-----------------------------------------------------------------------"
echo "Use: '$0 platforms -remote' to see platforms served by the artifact server"
echo "Canonical platform names: 'help platforms' in the punk shell"
fi
;;
"help")
show_help
;;
"")
#no action: short usage + pointer at the fuller help
show_usage
echo ""
echo "'$0 help' gives options, env vars and examples."
;;
*)
echo "unknown action '$action'"
show_usage
exit 1
;;
esac
#(512B spacer) byte-alignment padding so template labels beyond the payloads clear cmd's
#512-byte label-scan boundaries - resize this comment if scriptwrap checkfile reports a
#boundary-spanning label after a payload edit (see bin/AGENTS.md polyglot workflow)