From d81a78f437b80758dd7e51a269db4e5345b80882 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Wed, 22 Jul 2026 14:51:54 +1000 Subject: [PATCH] modpod template: sync punk-runtime.cmd canonical-platform work Bring the modpod-template copy of punk-runtime.cmd up to the canonical-platform punk-runtime work already landed in bin/ and the project_layouts copy: local_platform canonical punkshell platform-dir names (narch normalization, win32-ix86, per-BSD prongs), no-name fetch defaults from the server's curated defaults.txt instead of baked-in per-platform rt_default values, and the fetch/list/use -platform handling that goes with them. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- .../punk/project-0.1/bin/punk-runtime.cmd | 845 ++++++++++++++++-- 1 file changed, 760 insertions(+), 85 deletions(-) diff --git a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd index 963a5420..e2429a73 100644 --- a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd +++ b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd @@ -1292,64 +1292,73 @@ 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 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. +#each prong sets local_platform (the CANONICAL punkshell platform-DIR name - see +#punk::platform / 'help platforms': cpu tokens normalized amd64->x86_64, +#aarch64->arm64). Per-platform default fetch runtimes are NOT baked here - they +#are the server's curated defaults.txt (a punkbin release decision). +narch="$arch" +case "$narch" in + amd64) narch="x86_64";; + aarch64|arm64) narch="arm64";; +esac 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 + local_platform="linux-x86_64" + elif [[ "$narch" = "arm64" ]]; then + #canonical arm64 (aarch64). punkbin's existing arm kit predates the + #arm64 name and sits in linux-arm (the server's defaults.txt records + #per-platform recommendations). + local_platform="linux-arm64" elif [[ "$arch" = "arm"* ]]; then - archdir="${scriptdir}/runtime/linux-arm" - rt_default="tclkit-902-Linux64-arm-dyn" - runtime_available=1 + #32-bit arm + local_platform="linux-arm" else - archdir="${scriptdir}/runtime/linux-$arch" + local_platform="linux-$narch" 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 + #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" elif [[ "$plat" = "FreeBSD"* ]]; then - archdir="${scriptdir}/runtime/freebsd-amd64" + #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 - archdir="${scriptdir}/runtime/dragonflybsd-$arch" + local_platform="dragonflybsd-$narch" os="dragonflybsd" elif [[ "$plat" == "NetBSD"* ]]; then - archdir="${scriptdir}/runtime/netbsd-$arch" + local_platform="netbsd-$narch" os="netbsd" elif [[ "$plat" == "OpenBSD"* ]]; then - archdir="${scriptdir}/runtime/openbsd-amd64" + local_platform="openbsd-$narch" os="openbsd" elif [[ "$plat" == "MINGW32"* ]]; then - #REVIEW + #32-bit msys shell - a genuine 32-bit machine reports i*86; a 32-bit shell + #on a 64-bit OS is rare enough that the machine arch decides (REVIEW) os="win32" - archdir="${scriptdir}/runtime/win32-x86_64" - rt_default="tclsh902z.exe" - runtime_available=1 + case "$arch" in + i*86) local_platform="win32-ix86";; + *) local_platform="win32-x86_64";; + esac elif [[ "$plat" == "MINGW64"* ]]; then #REVIEW os="win32" - archdir="${scriptdir}/runtime/win32-x86_64" - rt_default="tclsh902z.exe" - runtime_available=1 + local_platform="win32-x86_64" elif [[ "$plat" == "CYGWIN_NT"* ]]; then os="win32" - archdir="${scriptdir}/runtime/win32-x86_64" - rt_default="tclsh902z.exe" - runtime_available=1 + case "$arch" in + i*86) local_platform="win32-ix86";; + *) local_platform="win32-x86_64";; + esac elif [[ "$plat" == "MSYS_NT"* ]]; then echo MSYS os="win32" @@ -1360,14 +1369,59 @@ elif [[ "$plat" == "MSYS_NT"* ]]; then #"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 + local_platform="win32-x86_64" else - archdir="${scriptdir}/runtime/other" + local_platform="other" os="other" fi -archtail="${archdir##*/}" #server folder name e.g linux-x86_64 + +#-- 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, @@ -1398,20 +1452,170 @@ 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 +#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 - ls -1 "$archdir" | grep -v '_BUILDCOPY$' | grep -v '\.txt$' | grep -v '\.toml$' | grep -v '\.tm$' | grep -v '\.tmp$' + 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 " platform's recommended default per the server's curated" + echo " defaults.txt (a punkbin release decision; works with -platform" + echo " too) - platforms without a recorded default need an explicit" + echo " ." + 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 "$1" in +case "$action" in "fetch") - runtime="$rt_default" - if [[ -n "$2" ]]; then - runtime="$2" + if [[ "$archtail" == "other" ]]; then + echo "Unrecognised local platform - name one explicitly with -platform" + echo "(canonical names: 'help platforms' in the punk shell)" + exit 1 + fi + runtime="${1:-}" + if [[ -z "$runtime" ]]; then + #no name given: consult the server's curated defaults.txt - the punkbin + #release recommendation (updated there as part of each publication + #change-set; see punkbin AGENTS.md). Per-platform server data, so it + #works for any -platform, not just local. + defaultsurl="${url_kitbase}/defaults.txt" + defaultslocal="${scriptdir}/runtime/defaults.txt" + mkdir -p "${scriptdir}/runtime" + if ! curl -fsSL --output "$defaultslocal" "$defaultsurl"; then + if [[ -f "$defaultslocal" ]]; then + echo "WARNING: could not fetch $defaultsurl - using cached copy at $defaultslocal" + else + echo "Could not fetch $defaultsurl" + echo "No server default available - name a runtime explicitly:" + echo " $0 fetch ?-platform $archtail?" + echo " (see available names: $0 list -remote -platform $archtail)" + exit 1 + fi + fi + runtime=$(awk -v p="$archtail" '$1==p {print $2; exit}' "$defaultslocal") + if [[ -z "$runtime" ]]; then + echo "No default recorded for platform '$archtail' in the server's defaults.txt - name a runtime explicitly:" + echo " $0 fetch ?-platform $archtail?" + echo " (see available names: $0 list -remote -platform $archtail)" + exit 1 + fi + echo "using server default for $archtail: $runtime" fi - if [[ ( "$runtime_available" -eq 1 || -n "$2" ) && "$archtail" != "other" && -n "$runtime" ]]; then + if [[ -n "$runtime" ]]; then url="${url_kitbase}/${archtail}/${runtime}" output="${archdir}/${runtime}" sha1url="${url_kitbase}/${archtail}/sha1sums.txt" @@ -1480,17 +1684,25 @@ case "$1" in 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 [[ "$2" == "-remote" ]]; then + 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" @@ -1543,16 +1755,19 @@ case "$1" in 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 - echo "* $f (active)" + printf '* %-35s (active) %s\n' "$f" "$meta" else - echo " $f" + 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." @@ -1560,9 +1775,12 @@ case "$1" in fi ;; "use") - if [[ -z "$2" ]]; then - echo "Usage: $0 use " - echo "Installed candidates:" + #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 @@ -1572,25 +1790,53 @@ case "$1" in fi exit 1 fi - case "$2" in + case "$1" in *_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp) - echo "'$2' is not a selectable runtime" + echo "'$1' is not a selectable runtime" exit 1 ;; esac - if [[ ! -f "$archdir/$2" ]]; then - echo "No runtime named '$2' found in $archdir" + 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 - set_active "$2" + #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) + #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" @@ -1627,13 +1873,93 @@ case "$1" in fi activeruntime_fullpath="$archdir/$activeruntime" #echo "using $activeruntime_fullpath" - shift - #echo "args: $@" + #(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 "Usage: $0 {fetch|list|use|run}" - echo "received $@" + echo "unknown action '$action'" + show_usage exit 1 ;; esac @@ -2004,7 +2330,158 @@ function Get-PunkRuntimeCandidates { if (-not (Test-Path -Path $archfolder -PathType Container)) { return @() } - return @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp") -contains $_.Extension) } | Sort-Object Name) + return @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp",".log") -contains $_.Extension) } | Sort-Object Name) +} +#operator help (the 'help' action; the no-args case shows the Usage block only). +#Keep in sync with the bash payload's show_help/show_usage. +function Show-PunkRuntimeUsage { + write-host "Usage: punk-runtime.cmd {fetch|list|use|run|platforms|help}" + write-host " fetch ?? ?-platform

? download+verify a runtime from punkbin" + write-host " list ?-remote? ?-platform

? installed (or server) runtimes + metadata" + write-host " use ?-platform

? select active / materialize -r artifact" + write-host " run ?args...? launch the active local-platform runtime" + write-host " platforms ?-remote? local platform folders / platforms the server serves" + write-host " help full help (options, env vars, examples)" +} +function Show-PunkRuntimeHelp { + write-host "punk-runtime - manage the plain Tcl runtimes under bin/runtime//" + write-host "" + Show-PunkRuntimeUsage + write-host "" + write-host "Actions:" + write-host " fetch ?? ?-platform

?" + write-host " Download a runtime from the punkbin artifact server into" + write-host " bin/runtime/

/ with sha1 verification against the server's" + write-host " sha1sums.txt. -r-named family artifacts also fetch their" + write-host " .toml metadata record. Omitting fetches the" + write-host " platform's recommended default per the server's curated" + write-host " defaults.txt (a punkbin release decision; works with -platform" + write-host " too) - platforms without a recorded default need an explicit" + write-host " ." + write-host " list ?-remote? ?-platform

?" + write-host " List installed runtimes with artifact-metadata summaries" + write-host " (variant, tcl patchlevel, revision, piperepl policy, source" + write-host " artifact of a materialized copy; a !TARGET-MISMATCH flag marks" + write-host " a runtime filed under the wrong platform folder). -remote" + write-host " compares local runtimes against the server's sha1sums." + write-host " use ?-platform

?" + write-host " Select the runtime that 'run' launches (records active.toml in" + write-host " the platform folder). An immutable -r ARTIFACT name is" + write-host " MATERIALIZED into its WORKING name (name minus -r - what" + write-host " mapvfs and projects reference), metadata toml copied alongside." + write-host " run ?args...?" + write-host " Launch the ACTIVE runtime of the LOCAL platform, passing args." + write-host " Resolution: PUNK_ACTIVE_RUNTIME env > active.toml > sole" + write-host " installed candidate. Takes no -platform (foreign binaries are" + write-host " not runnable here)." + write-host " platforms ?-remote?" + write-host " List local platform folders under bin/runtime/. With -remote," + write-host " list the platforms the artifact server serves - read from the" + write-host " server's platforms.txt discovery manifest (part of the punkbin" + write-host " layout; third-party mirrors carry the same file), with local" + write-host " presence marked. Servers without the manifest get an actionable" + write-host " message (name platforms explicitly with -platform)." + write-host "" + write-host "Platforms: punkbin platform-dir names (e.g win32-x86_64, linux-x86_64," + write-host " macosx) - not zig triples. Default is the local platform; override" + write-host " order: -platform argument > PUNK_RUNTIME_PLATFORM env var." + write-host " Foreign-platform folders serve cross-build staging and provisioning:" + write-host " active.toml travels with the folder when it is deployed to its real" + write-host " platform (unix exec bits are restored at deploy time - e.g rsync/tar/" + write-host " chmod on the receiving side)." + write-host "" + write-host "Env: PUNKBIN_URL (artifact server base url), PUNK_RUNTIME_PLATFORM," + write-host " PUNK_ACTIVE_RUNTIME (run-time selection override)" + write-host "" + write-host "Examples:" + write-host " punk-runtime.cmd platforms -remote" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1.exe" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1.exe (materializes tclsh9.0.5-punk.exe)" + write-host " punk-runtime.cmd list -remote -platform linux-x86_64" + write-host " punk-runtime.cmd fetch tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd use tclsh9.0.5-punk-r1 -platform linux-x86_64" + write-host " punk-runtime.cmd run myscript.tcl arg1 arg2" +} +#platform (target) resolution for fetch/list/use: -platform arg > PUNK_RUNTIME_PLATFORM +#env > the local default. Values are punkbin/bin-runtime 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; punk-runtime speaks only the punkbin tier). Validation is +#deliberately shape-only: the server's sha1sums/404 is the truth for what exists. +#This payload runs on windows - the local default is win32-x86_64, or win32-ix86 on +#a GENUINE 32-bit windows host (PROCESSOR_ARCHITECTURE x86 with no +#PROCESSOR_ARCHITEW6432 - a 32-bit shell on a 64-bit OS keeps the x86_64 default: +#the runtime store serves what the OS can run). A windows-arm default becomes a +#question when punkbin carries such a folder. +$script:PunkLocalPlatform = "win32-x86_64" +if ($env:PROCESSOR_ARCHITECTURE -eq 'x86' -and -not $env:PROCESSOR_ARCHITEW6432) { + $script:PunkLocalPlatform = "win32-ix86" +} +function Resolve-PunkRuntimePlatform { + param([string] $requested) + $plat = $script:PunkLocalPlatform + if ($requested) { + $plat = $requested + } elseif ($env:PUNK_RUNTIME_PLATFORM) { + $plat = $env:PUNK_RUNTIME_PLATFORM + } + #-cnotmatch: platform-dir names are lowercase and the artifact-server URL path + #is case-sensitive (default -notmatch is case-insensitive and would pass typos + #like Win32-X86_64 through to a server 404); bash payload parity (=~ is + #case-sensitive there) + if ($plat -cnotmatch '^[a-z0-9][a-z0-9_-]*$') { + write-host "invalid platform name '$plat' (expected a punkbin platform-dir name such as win32-x86_64, linux-x86_64, macosx)" + exit 1 + } + return $plat +} +#name minus a .exe suffix only - dotted tcl patchlevels (tclsh9.0.5-punk) make +#generic last-dot extension stripping wrong for extensionless unix names +function Get-PunkRuntimeRootName { + param([string] $name) + if ($name -match '(?i)\.exe$') { + return $name.Substring(0, $name.Length - 4) + } + return $name +} +#G-103 artifact metadata: a runtime may carry a .toml beside it (emitted +#by the buildsuite kit-family-artifacts step / fetched from punkbin). Returns a +#short "[variant=... tcl=... rN ...]" summary for list output, "" when absent. +function Get-PunkRuntimeMetadataSummary { + param([string] $archfolder, [string] $exename, [string] $expectedplatform = "") + $tomlfile = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $exename) + ".toml") + if (-not (Test-Path -Path $tomlfile -PathType Leaf)) { + return "" + } + $fields = @{} + foreach ($line in (Get-Content -Path $tomlfile)) { + $m = [regex]::Match($line, '^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"(.*)"\s*$') + if ($m.Success) { + $fields[$m.Groups[1].Value] = $m.Groups[2].Value + continue + } + $m = [regex]::Match($line, '^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([0-9]+|true|false)\s*$') + if ($m.Success) { + $fields[$m.Groups[1].Value] = $m.Groups[2].Value + } + } + $parts = @() + if ($fields.ContainsKey('variant')) { $parts += "variant=$($fields['variant'])" } + if ($fields.ContainsKey('tcl_patchlevel')) { $parts += "tcl=$($fields['tcl_patchlevel'])" } + if ($fields.ContainsKey('revision')) { $parts += "r$($fields['revision'])" } + if ($fields.ContainsKey('piperepl')) { + if ($fields['piperepl'] -eq 'true') { $parts += "piperepl=on" } else { $parts += "piperepl=off" } + } + #a materialized working copy records which immutable artifact it came from + if ($fields.ContainsKey('name') -and $fields['name'] -ne $exename) { $parts += "from=$($fields['name'])" } + #integrity flag: a runtime filed under a platform folder its metadata says it + #was not built for (cross-platform fetch/staging misfiling) + if ($expectedplatform -ne "" -and $fields.ContainsKey('target') -and $fields['target'] -ne $expectedplatform) { + $parts += "!TARGET-MISMATCH:$($fields['target'])" + } + if ($parts.Count -eq 0) { + return "" + } + return "[" + ($parts -join " ") + "]" } function psmain { @@ -2014,7 +2491,9 @@ function psmain { [Parameter(Mandatory=$false, Position = 0)][string] $action = "" ) dynamicparam { - if ($action -eq 'list') { + if ($action -eq 'list' -or $action -eq 'platforms') { + #shared dynamic params: -remote for both; -platform is meaningful to + #'list' and ignored by 'platforms' (which enumerates ALL platforms) $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ ParameterSetName = "listruntime" Mandatory = $false @@ -2024,8 +2503,19 @@ function psmain { $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( 'remote', [switch], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to list for + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "listruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('remote', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'fetch' -or $action -eq 'use') { #GetDynamicParamDictionary ParameterDefinitions @@ -2041,8 +2531,21 @@ function psmain { 'runtime', [string], $attributeCollection ) + #-platform (G-105 cross-build staging): punkbin platform-dir name to fetch + #into / select within (default: the local platform) + $platformAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + } + $platformAttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $platformAttributeCollection.Add($platformAttribute) + $dynParam2 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'platform', [string], $platformAttributeCollection + ) + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() $paramDictionary.Add('runtime', $dynParam1) + $paramDictionary.Add('platform', $dynParam2) return $paramDictionary } elseif ($action -eq 'run') { #GetDynamicParamDictionary ParameterDefinitions @@ -2087,7 +2590,7 @@ function psmain { 'action' { write-host "got action " $PSBoundParameters.action Set-Variable -Name $_ -Value $PSBoundParameters."$_" - $known_actions = @("fetch", "list", "use", "run") + $known_actions = @("fetch", "list", "use", "run", "platforms", "help") if (-not($known_actions -contains $action)) { write-host "action '$action' not understood. Known_actions: $known_actions" exit 1 @@ -2119,16 +2622,56 @@ function psmain { } switch ($action) { 'fetch' { - $arch = "win32-x86_64" - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $archurl = "$artifacturl/$arch" $sha1url = "$archurl/sha1sums.txt" - $runtime = "tclsh902z.exe" foreach ($boundparam in $PSBoundParameters.Keys) { write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" } + $runtime = "" if ( $PSBoundParameters["runtime"].Length ) { $runtime = $PSBoundParameters["runtime"] + } else { + #no name given: consult the server's curated defaults.txt - the + #punkbin release recommendation (updated there as part of each + #publication change-set; see punkbin AGENTS.md). Per-platform + #server data, so it works for any -platform, not just local. + $defaultsurl = "$artifacturl/defaults.txt" + $defaultslocal = Join-Path -Path $rtfolder -ChildPath "defaults.txt" + if (-not (Test-Path -Path $rtfolder -PathType Container)) { + new-item -Path $rtfolder -ItemType Directory -force | out-null + } + try { + Invoke-WebRequest -Uri $defaultsurl -OutFile $defaultslocal -ErrorAction Stop + } catch { + if (Test-Path -Path $defaultslocal -PathType Leaf) { + Write-Host "WARNING: could not fetch ${defaultsurl}: $($_.Exception.Message)" + Write-Host "WARNING: using cached copy at $defaultslocal" + } else { + Write-Host "Could not fetch ${defaultsurl}: $($_.Exception.Message)" + Write-Host "No server default available - name a runtime explicitly:" + Write-Host " punk-runtime.cmd fetch ?-platform ${arch}?" + Write-Host " (see available names: punk-runtime.cmd list -remote -platform $arch)" + exit 1 + } + } + foreach ($line in (Get-Content -Path $defaultslocal)) { + $line = $line.Trim() + if ($line -eq "" -or $line.StartsWith("#")) { continue } + $parts = -split $line + if ($parts.Count -ge 2 -and $parts[0] -eq $arch) { + $runtime = $parts[1] + break + } + } + if ($runtime -eq "") { + Write-Host "No default recorded for platform '$arch' in the server's defaults.txt - name a runtime explicitly:" + Write-Host " punk-runtime.cmd fetch ?-platform ${arch}?" + Write-Host " (see available names: punk-runtime.cmd list -remote -platform $arch)" + exit 1 + } + Write-Host "using server default for ${arch}: $runtime" } $fileurl = "$archurl/$runtime" @@ -2212,6 +2755,19 @@ function psmain { Write-Host "Local copy of runtime at $output seems to match sha1 checksum of file on server." Write-Host "No download required" } + #G-103: family artifacts (-r names) carry a metadata toml alongside + #on the server - fetch it too; absence is fine (pre-family runtimes) + if ($runtime -match '^(.*)-r([0-9]+)(\.[Ee][Xx][Ee])?$') { + $tomlname = (Get-PunkRuntimeRootName $runtime) + ".toml" + $tomlurl = "$archurl/$tomlname" + $tomllocal = Join-Path -Path $archfolder -ChildPath $tomlname + try { + Invoke-WebRequest -Uri $tomlurl -OutFile $tomllocal -ErrorAction Stop + Write-Host "artifact metadata saved at $tomllocal" + } catch { + Write-Host "no artifact metadata toml on server for $runtime (ok for pre-family runtimes)" + } + } #first fetch establishes the active runtime; later fetches never steal it if ((Get-PunkActiveRuntime $archfolder) -eq "") { Set-PunkActiveRuntime $archfolder $runtime @@ -2227,8 +2783,11 @@ function psmain { } } 'use' { - #select the active runtime for subsequent 'run' calls - $arch = "win32-x86_64" + #select the active runtime for subsequent 'run' calls. With -platform: + #manage THAT platform folder's selection/materialization (cross-build + #staging - the active.toml travels with the folder when deployed; 'run' + #on this machine only ever consults the local platform). + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $candidates = Get-PunkRuntimeCandidates $archfolder $rtname = "" @@ -2236,8 +2795,8 @@ function psmain { $rtname = $PSBoundParameters["runtime"] } if ($rtname -eq "") { - write-host "Usage: punk-runtime.cmd use " - write-host "Installed candidates:" + write-host "Usage: punk-runtime.cmd use ?-platform

?" + write-host "Installed candidates ($arch):" foreach ($f in $candidates) { write-host " $($f.Name)" } @@ -2255,13 +2814,42 @@ function psmain { } exit 1 } - Set-PunkActiveRuntime $archfolder $rtname + #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. + $am = [regex]::Match($rtname, '^(.*)-r([0-9]+)(\.[Ee][Xx][Ee])?$') + if ($am.Success) { + $working = $am.Groups[1].Value + $am.Groups[3].Value + $srcexe = Join-Path -Path $archfolder -ChildPath $rtname + $destexe = Join-Path -Path $archfolder -ChildPath $working + Copy-Item -Path $srcexe -Destination $destexe -Force + $srctoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $rtname) + ".toml") + $desttoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $working) + ".toml") + if (Test-Path -Path $srctoml -PathType Leaf) { + Copy-Item -Path $srctoml -Destination $desttoml -Force + write-host "materialized $working from artifact $rtname (metadata toml copied alongside)" + } else { + write-host "materialized $working from artifact $rtname (no metadata toml found beside the artifact)" + } + Set-PunkActiveRuntime $archfolder $working + } else { + Set-PunkActiveRuntime $archfolder $rtname + } } 'run' { #launch the active runtime, passing arguments. #resolution order: PUNK_ACTIVE_RUNTIME env override, active.toml, single - #installed candidate - otherwise error with candidates (no last-in-list guessing) - $arch = "win32-x86_64" + #installed candidate - otherwise error with candidates (no last-in-list guessing). + #LOCAL PLATFORM ONLY - a foreign platform's binaries are not runnable here. + #Only a LEADING -platform is rejected: everything after 'run' belongs to + #the runtime, so a later arg spelled -platform must pass through untouched. + if ($PSBoundParameters.opts.Length -gt 0 -and $PSBoundParameters.opts[0] -eq '-platform') { + write-host "'run' launches the LOCAL platform's active runtime - it takes no -platform option" + write-host "(foreign-platform folders are cross-build staging; deploy them to their platform to run)" + exit 1 + } + $arch = $script:PunkLocalPlatform $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" if (-not(Test-Path -Path $archfolder -PathType Container)) { write-host "No runtimes seem to be installed for $arch`nPlease use 'punk-runtime.cmd fetch' to install" @@ -2337,9 +2925,9 @@ function psmain { } } 'list' { - #todo - option to list for other os-arch - $arch = 'win32-x86_64' - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + #-platform lists another platform's folder/server dir (cross-build staging) + $arch = Resolve-PunkRuntimePlatform $PSBoundParameters["platform"] + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" $sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt" $archurl = "$artifacturl/$arch" $sha1url = "$archurl/sha1sums.txt" @@ -2380,7 +2968,12 @@ function psmain { $localdict = @{} if (test-path -Path $archfolder -Type Container) { - $dircontents = (get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY.*' | Where-object {-not ($(".txt",".tm") -contains $_.Extension) }) + #shared candidate filter (bash payload parity - its -remote loop + #already uses list_candidates): excludes directories, build + #copies and .txt/.toml/.tm/.tmp/.log support files, so + #active.toml / metadata tomls / stray logs never show as + #local runtimes in the comparison + $dircontents = Get-PunkRuntimeCandidates $archfolder foreach ($f in $dircontents) { $local_sha1 = Get-FileHash -Path $(${f}.FullName) -Algorithm SHA1 $localdict[$f.Name] = ${local_sha1}.Hash @@ -2438,10 +3031,12 @@ function psmain { write-host "$(${dircontents}.count) runtime(s) in $archfolder" Write-host "-----------------------------------------------------------------------" foreach ($f in $dircontents) { + $meta = Get-PunkRuntimeMetadataSummary $archfolder $f.Name $arch + $lhs = "$($f.Name)".PadRight(35, ' ') if ($f.Name -eq $activename) { - write-host "* $($f.Name) (active)" + write-host "* $lhs (active) $meta" } else { - write-host " $($f.Name)" + write-host " $lhs $meta" } } Write-host "-----------------------------------------------------------------------" @@ -2450,20 +3045,100 @@ function psmain { } Write-host "Use: 'list -remote' to compare local runtimes with those available on the artifact server" Write-host "Use: 'use ' to select the runtime that 'run' launches" + Write-host "Use: 'use -name>' to materialize an immutable -r artifact into its working name and select it" } else { write-host "No runtimes seem to be installed for $arch in $archfolder`nPlease use 'punk-runtime.cmd fetch' to install." write-host "Use 'punk-runtime.cmd list -remote' to see available runtimes for $arch" } } } - default { - $actions = @("fetch", "list", "use", "run") - write-host "Available actions: $actions" - write-host "received" - foreach ($boundparam in $PSBoundParameters.opts) { - write-host $boundparam + '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) + $localdirs = @() + if (Test-Path -Path $rtfolder -PathType Container) { + $localdirs = @(Get-ChildItem -Path $rtfolder -Directory | Select-Object -ExpandProperty Name | Sort-Object) + } + if ( $PSBoundParameters.ContainsKey('remote') ) { + $manifesturl = "$artifacturl/platforms.txt" + $manifestlocal = Join-Path -Path $rtfolder -ChildPath "platforms.txt" + if (-not (Test-Path -Path $rtfolder -PathType Container)) { + new-item -Path $rtfolder -ItemType Directory -force | out-null + } + try { + Invoke-WebRequest -Uri $manifesturl -OutFile $manifestlocal -ErrorAction Stop + Write-Host "Fetched $manifesturl" + } catch { + if (Test-Path -Path $manifestlocal -PathType Leaf) { + Write-Host "WARNING: could not fetch ${manifesturl}: $($_.Exception.Message)" + Write-Host "WARNING: using cached copy at $manifestlocal" + } else { + Write-Host "No platforms.txt available from ${manifesturl}: $($_.Exception.Message)" + Write-Host "(pre-convention punkbin or third-party mirror without the discovery manifest)" + Write-Host "Name platforms explicitly with -platform; canonical names: 'help platforms' in the punk shell" + exit 1 + } + } + $remoteplatforms = @() + foreach ($line in (Get-Content -Path $manifestlocal)) { + $line = $line.Trim() + if ($line -eq "" -or $line.StartsWith("#")) { continue } + $remoteplatforms += $line + } + Write-host "-----------------------------------------------------------------------" + Write-Host "Platforms served by $artifacturl" + Write-host "-----------------------------------------------------------------------" + foreach ($p in $remoteplatforms) { + $marks = @() + if ($p -eq $script:PunkLocalPlatform) { $marks += "local platform" } + if ($localdirs -contains $p) { $marks += "local dir present" } + $lhs = "$p".PadRight(25, ' ') + if ($marks.Count -gt 0) { + write-host " $lhs ($($marks -join ', '))" + } else { + write-host " $lhs" + } + } + foreach ($d in $localdirs) { + if (-not ($remoteplatforms -contains $d)) { + $lhs = "$d".PadRight(25, ' ') + write-host " $lhs (local dir only - not served remotely)" + } + } + Write-host "-----------------------------------------------------------------------" + Write-Host "Use: 'list -remote -platform ' to see a platform's runtimes" + } else { + Write-host "-----------------------------------------------------------------------" + Write-Host "Local platform folders under $rtfolder" + Write-host "-----------------------------------------------------------------------" + if ($localdirs.Count -eq 0) { + write-host " (none - 'fetch' creates the local platform's folder)" + } + foreach ($d in $localdirs) { + $lhs = "$d".PadRight(25, ' ') + if ($d -eq $script:PunkLocalPlatform) { + write-host "* $lhs (local platform)" + } else { + write-host " $lhs" + } + } + Write-host "-----------------------------------------------------------------------" + Write-Host "Use: 'platforms -remote' to see platforms served by the artifact server" + Write-Host "Canonical platform names: 'help platforms' in the punk shell" } } + 'help' { + Show-PunkRuntimeHelp + } + default { + #no action given (unknown actions were already rejected in the + #process block): show the short usage and point at 'help' + Show-PunkRuntimeUsage + write-host "" + write-host "'punk-runtime.cmd help' gives options, env vars and examples." + } } return $PSBoundParameters