You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
674 lines
32 KiB
674 lines
32 KiB
|
|
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 <p>' 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 <rootname>.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 ?<name>? ?-platform <p>? download+verify a runtime from punkbin" |
|
echo " list ?-remote? ?-platform <p>? installed (or server) runtimes + metadata" |
|
echo " use <name> ?-platform <p>? select active / materialize -r<N> 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/<platform>/" |
|
echo "" |
|
show_usage |
|
echo "" |
|
echo "Actions:" |
|
echo " fetch ?<name>? ?-platform <p>?" |
|
echo " Download a runtime from the punkbin artifact server into" |
|
echo " bin/runtime/<p>/ with sha1 verification against the server's" |
|
echo " sha1sums.txt. -r<N>-named family artifacts also fetch their" |
|
echo " <rootname>.toml metadata record. Omitting <name> fetches the" |
|
echo " local platform's default runtime; a FOREIGN platform fetch" |
|
echo " requires an explicit <name>." |
|
echo " list ?-remote? ?-platform <p>?" |
|
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 <name> ?-platform <p>?" |
|
echo " Select the runtime that 'run' launches (records active.toml in" |
|
echo " the platform folder). An immutable -r<N> ARTIFACT name is" |
|
echo " MATERIALIZED into its WORKING name (name minus -r<N> - 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<N> before the (optional) .exe suffix; prints |
|
#the WORKING name (artifact minus -r<N>) 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 <runtimename> -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: <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 |
|
#G-103: family artifacts (-r<N> 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 <runtimename>" |
|
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 <name>' to reselect" |
|
fi |
|
echo "Use: '$0 list -remote' to compare local runtimes with those available on the artifact server" |
|
echo "Use: '$0 use <name>' to select the runtime that 'run' launches" |
|
echo "Use: '$0 use <artifact-r<N>-name>' to materialize an immutable -r<N> 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 <runtimename> ?-platform <p>?" |
|
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<N>, immutable): 'use' MATERIALIZES the |
|
#artifact into its WORKING name (name minus -r<N> - 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 <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" |
|
#(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 <name>' 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)
|
|
|