From 176f2ad70296b98a7aed6c514b1fcc73cfb82611 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Mon, 27 Jul 2026 04:20:02 +1000 Subject: [PATCH] punk-runtime list/use stop offering support files as runtimes (punkshell 0.27.3) 'bin/punk-runtime.cmd list -platform win32-ix86' listed a LUCK.url shortcut and a tclsh8.6.10-luck-zip.cfg build request as if they were selectable runtimes. Both payloads already filtered support files - .txt/.toml/.tm/.tmp/.log - so the mechanism existed and only the recognised set was too narrow. It was also duplicated across NINE filter sites in three syntaxes (a powershell array, a bash case pattern, a bash grep -E regex), and two of them had already drifted: the "not a selectable runtime" guard omitted .log and matched *_BUILDCOPY without a trailing wildcard. The set is now defined ONCE per payload - $PunkRuntimeSupportExt / Test-PunkRuntimeSupportName in the ps1, punk_support_ext_re / is_punk_support_name in the bash - and every site consults it, so the two payloads cannot drift apart. Their listings are required to agree byte-for-byte and now do so by construction. Recognised support files, by category: docs/text (.txt .md .html .htm .pdf .url), config/metadata (.toml .json .xml .yml .yaml .ini .cfg), dev leftovers (.tm .tmp .log .bak .old), checksums/signatures (.sha1 .sha256 .asc .sig) and archives (.zip .tar .gz .bz2 .xz .7z), plus any *_BUILDCOPY* name. Anything else is still treated as a runtime candidate - deliberately, so a stray binary is visible rather than silently hidden. Verified: the win32-ix86 listing drops from 6 entries to the 4 real runtimes; the bash helpers produce the identical four (the polyglot routes msys bash to the ps1 payload on windows, so the bash side was exercised directly). Re-wrapped per bin/AGENTS.md with checkfile ERROR-free; runtimecmd_roundtrip (byte-identity), runtimecmd_checkfile, runtimecmd_freshness (10/10, both payloads against the fixture server) and runtimebash_wsl (8/8) all pass, as does the full suite at its documented baseline. bin/runtime/README.md now states what a platform folder is FOR (a store tier keyed by the platform a runtime is for, not by this machine) and what is tolerated in it, with the recognised-support-file table and the warning that anything else will be offered as a runtime. It is force-added to git: /bin/* is ignored and a ! negation cannot re-include a file inside an excluded directory, so git add -f is the only mechanism - recorded in the .fossil-settings/AGENTS.md force-tracked one-off list, whose fossil counterpart is an explicit 'fossil add' at the next catch-up checkin. Also records in G-101 a survey of TEMP_REFERENCE/androwish for the zipfs-on-8.6 question the developer raised (non-contract findings; pointer added to TEMP_REFERENCE/AGENTS.md per its contract so the survey is not repeated). The undroidwish zipfs backport is ADDITIVE - generic/zipfs.c (6586 lines) + zipfs.h + a library/zipfs1.0 dir + one object line in the win and unix Makefiles - so it is carryable as a suite patch rather than a fork; it is C rather than C++ like mk4tcl; Tcl_StaticPackage registration is gated on ZIPFS_IN_TCL / ZIPFS_IN_TK, i.e. designed to compile into tcl or tk; and it is Christian Werner's, under the tree's Tcl-style license.terms. Its capability delta against Tcl 9 zipfs is recorded (no 'root', no ::zipfs ensemble, and not registered in child or thread interps), as is the exe-path mount convention that motivates G-129. Provenance of our specimen is established: LUCK is a CGI repacker that selects packages out of a prebuilt vanillatclsh binary's ZIP, so bin/runtime/win32-ix86/tclsh8.6.10-luck-zip.exe is a repack of androwish's own build - a working existence proof of zipfs-on-8.6 rather than a claim. Three open questions are recorded rather than guessed: whether it applies to 8.6.18 (the tree is 8.6.10), whether it builds under the pinned zig, and where the boot-time self-mount hook lives - stock tclAppInit calls Tclzipfs_Init only under #ifdef TCL_TEST, so it is in the vanillatclsh/undroidwish application main, which is also the hook a dual-container autodetecting boot would need. Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com --- .fossil-settings/AGENTS.md | 2 +- CHANGELOG.md | 18 +++++ bin/AGENTS.md | 11 ++- bin/punk-runtime.cmd | 79 ++++++++++++++++----- bin/runtime/README.md | 61 ++++++++++++++++ goals/G-101-tcl86-kit-container-strategy.md | 50 +++++++++++++ punkproject.toml | 2 +- src/scriptapps/bin/punk-runtime.bash | 45 ++++++++---- src/scriptapps/bin/punk-runtime.ps1 | 34 +++++++-- 9 files changed, 261 insertions(+), 41 deletions(-) create mode 100644 bin/runtime/README.md diff --git a/.fossil-settings/AGENTS.md b/.fossil-settings/AGENTS.md index f18570f3..625354db 100644 --- a/.fossil-settings/AGENTS.md +++ b/.fossil-settings/AGENTS.md @@ -25,7 +25,7 @@ Versioned fossil settings (one setting value or glob list per file) for this dua ### ignore-glob derivation rules (verified against fossil 2.28) -- **No negation exists in fossil globs.** Git's `/bin/*` + `!exception` pattern becomes: ignore `bin/*` wholesale and explicitly `fossil add` the tracked exceptions (ignore-glob never affects already-managed files, so they stay tracked). The exception set is whatever git tracks under an ignored tree - at the time of writing: `bin/AGENTS.md`, `bin/*.cmd`, `bin/*.kit`, `bin/*.tcl`, `bin/*.sh`, `bin/*.bash`, `.claude/settings.json` (shared claude harness hooks/permissions; session-local `.claude` files stay ignored), `.claude/skills/**` (project agent skills - synced copies of `.agents/skills/**`, which is not git-ignored and needs no exception), plus force-tracked one-offs (`bin/libssp-0.dll`, `src/vfs/punk9magicsplat.vfs/lib/nmake/x86_64-w64-mingw32-nmakehlp.exe`). Derive the current set with the verification comparison below rather than trusting this list. +- **No negation exists in fossil globs.** Git's `/bin/*` + `!exception` pattern becomes: ignore `bin/*` wholesale and explicitly `fossil add` the tracked exceptions (ignore-glob never affects already-managed files, so they stay tracked). The exception set is whatever git tracks under an ignored tree - at the time of writing: `bin/AGENTS.md`, `bin/*.cmd`, `bin/*.kit`, `bin/*.tcl`, `bin/*.sh`, `bin/*.bash`, `.claude/settings.json` (shared claude harness hooks/permissions; session-local `.claude` files stay ignored), `.claude/skills/**` (project agent skills - synced copies of `.agents/skills/**`, which is not git-ignored and needs no exception), plus force-tracked one-offs (`bin/libssp-0.dll`, `bin/runtime/README.md` - guidance, not build output; a `!` negation cannot re-include it because git does not descend into the excluded `bin/runtime` directory, so `git add -f` is the only mechanism, `src/vfs/punk9magicsplat.vfs/lib/nmake/x86_64-w64-mingw32-nmakehlp.exe`). Derive the current set with the verification comparison below rather than trusting this list. - A bare directory name prunes that whole tree; `*` crosses `/`; git patterns intended to match at any depth need an additional `*/` variant; `#` comment lines are honoured. - Do not add the fossil-generated names (`manifest`, `manifest.uuid`, `manifest.tags`) to `ignore-glob` - fossil handles them itself. - **Nested `.gitignore` files are git-only**: ignore files inside subtrees (e.g. the project-layout templates under `src/project_layouts/`) are honoured by git as nested ignores of the outer repo but invisible to fossil - template content they match is untracked in git yet managed by fossil. Treat such divergence as a signal to review the git side (usually `git add -f` of the affected template files). diff --git a/CHANGELOG.md b/CHANGELOG.md index beb65bf4..0797619e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.27.3] - 2026-07-27 + +- `bin/punk-runtime.cmd list` and `use` no longer offer support files as selectable + runtimes. The payloads already filtered `.txt/.toml/.tm/.tmp/.log`; the recognised set + now also covers docs/text (`.md .html .htm .pdf .url`), config/metadata + (`.json .xml .yml .yaml .ini .cfg`), dev leftovers (`.bak .old`), checksums and + signatures (`.sha1 .sha256 .asc .sig`) and archives (`.zip .tar .gz .bz2 .xz .7z`). + Prompted by a runtime tier where a build-request `.cfg` and a `.url` shortcut were + listed as runtimes. +- The set is now defined ONCE per payload (`$PunkRuntimeSupportExt` / + `Test-PunkRuntimeSupportName` in the powershell payload, `punk_support_ext_re` / + `is_punk_support_name` in the bash payload) instead of repeated at each of the nine + filter sites, so the two payloads cannot drift - their listings are required to agree + byte-for-byte. +- `bin/runtime/README.md` now states what a platform folder is for and what is tolerated + in it: anything not recognised as a support file is offered as a runtime, so unrelated + executables do not belong there. + ## [0.27.2] - 2026-07-27 - The bake's boot-precondition gate (G-125) recognises a third tcl-library convention: diff --git a/bin/AGENTS.md b/bin/AGENTS.md index 2bd50c74..efbbbf73 100644 --- a/bin/AGENTS.md +++ b/bin/AGENTS.md @@ -143,7 +143,16 @@ suffix (2026-07-25; exact names - incl. extensionless unix artifacts in foreign `-platform` folders - match first, then the `.exe`-suffixed spelling). Root-name handling strips only a `.exe` suffix (dotted tcl patchlevels make generic last-dot stripping wrong for extensionless unix -names). Candidate listing excludes directories and `.txt/.toml/.tm/.tmp/.log` files. +names). Candidate listing excludes directories, `*_BUILDCOPY*` names, and SUPPORT FILES +recognised by extension - docs/text (`.txt .md .html .htm .pdf .url`), config/metadata +(`.toml .json .xml .yml .yaml .ini .cfg`), dev leftovers (`.tm .tmp .log .bak .old`), +checksums/signatures (`.sha1 .sha256 .asc .sig`) and archives +(`.zip .tar .gz .bz2 .xz .7z`). The same set filters `use` and the sha1 value-matching +against the server's sha1sums (support rows ride in that file too). It is defined ONCE per +payload - `$PunkRuntimeSupportExt`/`Test-PunkRuntimeSupportName` in the ps1, +`punk_support_ext_re`/`is_punk_support_name` in the bash - and the two must stay in step +because the listings are required to agree byte-for-byte; extend both and re-wrap. What a +tier is expected to contain is documented for users in `bin/runtime/README.md`. Forgot-to-switch-back guard (2026-07-25): `make.tcl bake`/`bakehouse` emit a BUILD-WARNING (recapped at end of run) when a working runtime they wrap is materialized from an older revision than an `-r` artifact present beside it - a deliberate diff --git a/bin/punk-runtime.cmd b/bin/punk-runtime.cmd index 2da7e938..ff215c77 100755 --- a/bin/punk-runtime.cmd +++ b/bin/punk-runtime.cmd @@ -1453,6 +1453,30 @@ set_active() { echo "active runtime for $archtail set to: $1 (recorded in $active_file)" } #installed runtime candidates - exclude directories, build copies and +#Support-file extensions: things that legitimately sit in a runtime tier beside the +#runtimes but are never themselves selectable runtimes. Filtered from the local +#candidate listing, from 'use', and from sha1 value-matching against the server's +#sha1sums (support rows ride in that file too). Keep this in step with the ps1 +#payload's $PunkRuntimeSupportExt/Test-PunkRuntimeSupportName - listings must agree +#byte-for-byte. +# docs/text .txt .md .html .htm .pdf .url +# config/meta .toml .json .xml .yml .yaml .ini .cfg +# dev leftover .tm .tmp .log .bak .old +# checksums .sha1 .sha256 .asc .sig +# archives .zip .tar .gz .bz2 .xz .7z +#Anything else is treated as a runtime candidate - see bin/runtime/README.md for what +#a runtime tier is expected to contain. +punk_support_ext_re='\.(txt|md|html|htm|pdf|url|toml|json|xml|yml|yaml|ini|cfg|tm|tmp|log|bak|old|sha1|sha256|asc|sig|zip|tar|gz|bz2|xz|7z)$' +is_punk_support_name() { + case "$1" in + *.txt|*.md|*.html|*.htm|*.pdf|*.url) return 0;; + *.toml|*.json|*.xml|*.yml|*.yaml|*.ini|*.cfg) return 0;; + *.tm|*.tmp|*.log|*.bak|*.old) return 0;; + *.sha1|*.sha256|*.asc|*.sig) return 0;; + *.zip|*.tar|*.gz|*.bz2|*.xz|*.7z) return 0;; + esac + return 1 +} #non-runtime files (ps1-payload parity: files only, same extension excludes). #LC_ALL=C: glob expansion order is collation-dependent - C-locale byte order #matches the ps1 payload's ordinal sorting, so listings agree everywhere. @@ -1463,8 +1487,9 @@ list_candidates() { [[ -f "$f" ]] || continue f="${f##*/}" case "$f" in - *_BUILDCOPY*|*.txt|*.toml|*.tm|*.tmp|*.log) continue;; + *_BUILDCOPY*) continue;; esac + is_punk_support_name "$f" && continue printf '%s\n' "$f" done fi @@ -1691,7 +1716,7 @@ freshness_verdict() { fi #deterministic pick: C-collation first value-match, support files #(.txt/.toml/...) filtered before the pick (ps1-payload parity) - vmatch=$(tr -d '\r' < "$sha1file" | awk -v h="$asha" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev '\.(txt|toml|tm|tmp|log)$' | LC_ALL=C sort | head -n 1) + vmatch=$(tr -d '\r' < "$sha1file" | awk -v h="$asha" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev "$punk_support_ext_re" | LC_ALL=C sort | head -n 1) fi if [[ -n "$vmatch" ]]; then mrev=$(rev_of "$vmatch") @@ -1958,7 +1983,7 @@ case "$action" in [[ -n "$dsha" && "$local_sha1" == "$dsha" ]] && vname="$platform_default" fi if [[ -z "$vname" ]]; then - vname=$(tr -d '\r' < "$sha1local" | awk -v h="$local_sha1" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev '\.(txt|toml|tm|tmp|log)$' | LC_ALL=C sort | head -n 1) + vname=$(tr -d '\r' < "$sha1local" | awk -v h="$local_sha1" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev "$punk_support_ext_re" | LC_ALL=C sort | head -n 1) fi if [[ -n "$vname" ]]; then mid="sha1=$vname" @@ -1987,9 +2012,7 @@ case "$action" in #skip support files riding in the server's sha1sums beside the #runtimes (artifact metadata tomls etc) - same extension set the #local candidate filter excludes - case "$rname" in - *.txt|*.toml|*.tm|*.tmp|*.log) continue;; - esac + is_punk_support_name "$rname" && continue if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then annot="" [[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)" @@ -2040,12 +2063,10 @@ case "$action" in fi exit 1 fi - case "$1" in - *_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp) - echo "'$1' is not a selectable runtime" - exit 1 - ;; - esac + if [[ "$1" == *_BUILDCOPY* ]] || is_punk_support_name "$1"; then + echo "'$1' is not a selectable runtime" + exit 1 + fi if [[ ! -f "$archdir/$1" && -f "$archdir/$1.exe" ]]; then #accept the name without its .exe suffix (root-name convention: G-103 #name handling strips only .exe, so the suffixless spelling is @@ -2671,6 +2692,29 @@ function Set-PunkActiveRuntime { [System.IO.File]::WriteAllText($activefile, "active = `"$name`"`n", [System.Text.UTF8Encoding]::new($false)) Write-Host "active runtime set to: $name (recorded in $activefile)" } +#Support-file extensions: things that legitimately sit in a runtime tier beside the +#runtimes but are never themselves selectable runtimes. Filtered from the local +#candidate listing, from 'use', and from sha1 value-matching against the server's +#sha1sums (support rows ride in that file too). Keep this set and the bash payload's +#is_punk_support_name/punk_support_ext_re in step - listings must agree byte-for-byte. +# docs/text .txt .md .html .htm .pdf .url +# config/meta .toml .json .xml .yml .yaml .ini .cfg +# dev leftover .tm .tmp .log .bak .old +# checksums .sha1 .sha256 .asc .sig +# archives .zip .tar .gz .bz2 .xz .7z +#Anything else is treated as a runtime candidate - see bin/runtime/README.md for what +#a runtime tier is expected to contain. +$script:PunkRuntimeSupportExt = @( + ".txt",".md",".html",".htm",".pdf",".url", + ".toml",".json",".xml",".yml",".yaml",".ini",".cfg", + ".tm",".tmp",".log",".bak",".old", + ".sha1",".sha256",".asc",".sig", + ".zip",".tar",".gz",".bz2",".xz",".7z" +) +function Test-PunkRuntimeSupportName { + param([string] $name) + return ($script:PunkRuntimeSupportExt -contains [System.IO.Path]::GetExtension($name).ToLowerInvariant()) +} #installed runtime candidates - exclude build copies and non-runtime files. #ORDINAL name order: culture-sensitive Sort-Object collates differently between #windows powershell (NLS) and pwsh (ICU) - both launch paths exist (the .ps1 @@ -2681,7 +2725,7 @@ function Get-PunkRuntimeCandidates { if (-not (Test-Path -Path $archfolder -PathType Container)) { return @() } - $files = @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp",".log") -contains $_.Extension) }) + $files = @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not (Test-PunkRuntimeSupportName $_.Name) }) if ($files.Count -le 1) { return $files } @@ -3116,8 +3160,7 @@ function Write-PunkFreshnessVerdict { $names = @($valdict.Keys) [array]::Sort($names, [System.StringComparer]::Ordinal) foreach ($n in $names) { - $rext = [System.IO.Path]::GetExtension($n) - if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } + if (Test-PunkRuntimeSupportName $n) { continue } if ($valdict[$n] -eq $asha) { $vmatch = $n; break } } } @@ -3785,8 +3828,7 @@ function psmain { $rnames = @($remotedict.Keys) [array]::Sort($rnames, [System.StringComparer]::Ordinal) foreach ($rn in $rnames) { - $rext = [System.IO.Path]::GetExtension($rn) - if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } + if (Test-PunkRuntimeSupportName $rn) { continue } if ($remotedict[$rn] -eq $local_sha1) { $vname = $rn; break } } } @@ -3821,8 +3863,7 @@ function psmain { #skip support files riding in the server's sha1sums beside #the runtimes (artifact metadata tomls etc) - same extension #set the local candidate filter excludes - $rext = [System.IO.Path]::GetExtension($key) - if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } + if (Test-PunkRuntimeSupportName $key) { continue } $annot = "" if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" } write-host -nonewline (" {0,-35} {1,-33} " -f "-", "") diff --git a/bin/runtime/README.md b/bin/runtime/README.md new file mode 100644 index 00000000..43b47ea7 --- /dev/null +++ b/bin/runtime/README.md @@ -0,0 +1,61 @@ +runtime Tcl Executables are installed here under the appropriate os_architecture folder. +e.g + windows 64bit binaries under win32-x86_64 + windows 32bit binaries under win32-ix86 + linux 64 bit binaries under linux-x86_64 + +## What belongs in a platform folder + +A platform folder is a store TIER: it holds runtime executables FOR that platform. The +folder need not match the machine you are on - a tier for another platform is a +first-class thing to populate, because the kit build reads each kit's tier from that +kit's target platform rather than from the driving tclsh (see src/AGENTS.md). + +Everything in the folder that is not recognised as a support file is treated as a +selectable runtime by `bin/punk-runtime.cmd list` and `use`. Support files are +recognised by extension and are tolerated beside the runtimes without appearing as +candidates: + + docs/text .txt .md .html .htm .pdf .url + config/meta .toml .json .xml .yml .yaml .ini .cfg + dev leftovers .tm .tmp .log .bak .old + checksums .sha1 .sha256 .asc .sig + archives .zip .tar .gz .bz2 .xz .7z + +plus any name containing `_BUILDCOPY`. That covers what legitimately accumulates here: +`active.toml` (which runtime `run` launches - per-machine, not versioned), a runtime's +`.toml` artifact-metadata record, a cached `sha1sums.txt`, a downloaded +checksum or signature, notes about where a runtime came from, and a build-request file +such as the `.cfg` a Lean Undroidwish Construction Kit build is reproducible from. + +Anything else - including a stray binary that is not a Tcl runtime - WILL be offered as +a runtime, so keep unrelated executables out of these folders. If a new kind of support +file starts appearing here regularly, extend the recognised set in BOTH payloads +(`src/scriptapps/bin/punk-runtime.ps1` and `.bash` - see the shared +`PunkRuntimeSupportExt` / `is_punk_support_name` definitions) and re-wrap; the two +listings are required to agree byte-for-byte. + +The folders themselves are not versioned (the `bin/*` ignore globs cover them), so +nothing here is a place to keep anything you cannot re-fetch or rebuild. + +The bin/punk-runtime.cmd cross-platform script can be used to download prebuilt Tcl binaries. +e.g ./bin/punk-runtime.cmd fetch +This will detect the platform and architecture, creating the containing folder as necessary. + +To see existing installed runtimes: + ./bin/punk-runtime.cmd list +Or to compare local runtimes with those available on the artifact server: + ./bin/punk-runtime.cmd list -remote + + An installed runtime can then be used to call the src/make.tcl file. + +e.g - initial check + ./bin/punk-runtime.cmd run src/make.tcl check + +e.g - to build (or just copy with latest version number from src if build not required) modules to projectdir/modules + ./bin/punk-runtime.cmd run src/make.tcl modules + for libs & modules + ./bin/punk-runtime.cmd run src/make.tcl packages + + + diff --git a/goals/G-101-tcl86-kit-container-strategy.md b/goals/G-101-tcl86-kit-container-strategy.md index 3ad30f58..be6027e7 100644 --- a/goals/G-101-tcl86-kit-container-strategy.md +++ b/goals/G-101-tcl86-kit-container-strategy.md @@ -291,6 +291,56 @@ shape via its /app anchor, so this is not an mk4-specific wart): a runtime wrapping a DIFFERENT copy of itself, so the shadowing path is exercised rather than accidentally avoided. +## zipfs backported to 8.6 (androwish/undroidwish - bearing on container choice) + +Recorded 2026-07-27 from a survey of `TEMP_REFERENCE/androwish` (fossil checkout; +the tree is Tcl 8.6.10). The developer raised it as a third possibility beside mk4 +and cookfs: rather than choosing a non-zipfs container for 8.6, bring zipfs itself +to the suite-built 8.6 runtime, as undroidwish does. + +- SHAPE IS ADDITIVE, which is what makes it tractable. `jni/tcl/generic/zipfs.c` + (6586 lines) plus `jni/tcl/generic/zipfs.h` (70), a `jni/tcl/library/zipfs1.0/` + package dir, and one object added in `win/Makefile.in:314` and + `unix/Makefile.in:309`. It is a NEW FILE plus build wiring, not a diffuse patch + spread through the core - so it can be carried as a suite patch of the kind + `src/buildsuites/suite_tcl86/patches/` already holds, rather than a fork. +- It does use core internals (`#include "tclInt.h"`, `"tclFileSystem.h"`), so it is + pinned to the core version it is compiled against. UNVERIFIED and the first thing + to check: whether it applies to 8.6.18 (what suite_tcl86 builds) - the reference + tree is 8.6.10. +- Entry point `Zipfs_Init`, aliased `Tclzipfs_Init` / `Tkzipfs_Init` (zipfs.h:30,42). + The `Tcl_StaticPackage(interp, "zipfs", ...)` registration is gated on + `ZIPFS_IN_TCL` / `ZIPFS_IN_TK` (zipfs.c:6445-6447) - it is designed to be compiled + INTO tcl or tk, which is exactly the shape this goal would want. +- LICENCE: Copyright (c) 2013-2023 Christian Werner, under the tree's + `license.terms` (Tcl-style BSD) - compatible with punkshell's BSD-2-Clause, and a + single named upstream author to attribute per G-063. +- CAPABILITY DELTA against Tcl 9's zipfs, measured on the shipped runtime (see the + table in goals/G-129-kitboot-mountpoint-derivation.md): it provides + `mount unmount info list exists mkzip mkimg mkkey lmkzip lmkimg` but NOT `root`, + provides no `::zipfs` ensemble, and is NOT registered in child or thread interps + (16 commands in every Tcl 9 interp vs 10 in the 8.6 backport's MAIN interp only). + Any punkshell code that must work under it therefore cannot probe with + `tcl::zipfs::root`, cannot call a bare `zipfs`, and must not assume a codethread + can see zipfs at all. +- MOUNT CONVENTION: it mounts the executable's attached archive at the EXECUTABLE'S + OWN PATH, not at `//zipfs:/app` - the reason G-129 exists. +- WHERE THE SELF-MOUNT LIVES IS AN OPEN QUESTION. Stock `win/tclAppInit.c:192` and + `unix/tclAppInit.c:130` call `Tclzipfs_Init` only under `#ifdef TCL_TEST`, so the + boot-time self-mount is in the vanillatclsh/undroidwish application main rather + than in the stock appinit. Locating that hook is prerequisite work for adopting + the backport, and it is the same hook a dual-container autodetecting boot would + need. +- PROVENANCE OF OUR SPECIMEN: LUCK (`undroid/luck/`) is a CGI script that repacks a + PREBUILT `vanillatclsh-*` / `undroidwish-*` binary, selecting packages out of that + binary's ZIP using Memchan channels and in-memory ZIP mounts (no temp files). So + `bin/runtime/win32-ix86/tclsh8.6.10-luck-zip.exe` is a repack of androwish's own + `vanillatclsh` build, and the base binaries come from the tree's + `undroid/build-undroidwish-*.sh` scripts. That makes the runtime a working + existence proof of zipfs-on-8.6, not merely a claim. +- UNVERIFIED: whether zipfs.c builds under the pinned zig toolchain. It is C (not + C++ like mk4tcl), which is a point in its favour against the metakit candidate. + ## Metakit under Tcl 9 (bearing on container choice) Recorded 2026-07-26 from an API survey against TEMP_REFERENCE/tcl9 (9.1b1). diff --git a/punkproject.toml b/punkproject.toml index 0d325547..3ad8cca1 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.27.2" +version = "0.27.3" license = "BSD-2-Clause" diff --git a/src/scriptapps/bin/punk-runtime.bash b/src/scriptapps/bin/punk-runtime.bash index 34cc9435..05f16451 100644 --- a/src/scriptapps/bin/punk-runtime.bash +++ b/src/scriptapps/bin/punk-runtime.bash @@ -173,6 +173,30 @@ set_active() { echo "active runtime for $archtail set to: $1 (recorded in $active_file)" } #installed runtime candidates - exclude directories, build copies and +#Support-file extensions: things that legitimately sit in a runtime tier beside the +#runtimes but are never themselves selectable runtimes. Filtered from the local +#candidate listing, from 'use', and from sha1 value-matching against the server's +#sha1sums (support rows ride in that file too). Keep this in step with the ps1 +#payload's $PunkRuntimeSupportExt/Test-PunkRuntimeSupportName - listings must agree +#byte-for-byte. +# docs/text .txt .md .html .htm .pdf .url +# config/meta .toml .json .xml .yml .yaml .ini .cfg +# dev leftover .tm .tmp .log .bak .old +# checksums .sha1 .sha256 .asc .sig +# archives .zip .tar .gz .bz2 .xz .7z +#Anything else is treated as a runtime candidate - see bin/runtime/README.md for what +#a runtime tier is expected to contain. +punk_support_ext_re='\.(txt|md|html|htm|pdf|url|toml|json|xml|yml|yaml|ini|cfg|tm|tmp|log|bak|old|sha1|sha256|asc|sig|zip|tar|gz|bz2|xz|7z)$' +is_punk_support_name() { + case "$1" in + *.txt|*.md|*.html|*.htm|*.pdf|*.url) return 0;; + *.toml|*.json|*.xml|*.yml|*.yaml|*.ini|*.cfg) return 0;; + *.tm|*.tmp|*.log|*.bak|*.old) return 0;; + *.sha1|*.sha256|*.asc|*.sig) return 0;; + *.zip|*.tar|*.gz|*.bz2|*.xz|*.7z) return 0;; + esac + return 1 +} #non-runtime files (ps1-payload parity: files only, same extension excludes). #LC_ALL=C: glob expansion order is collation-dependent - C-locale byte order #matches the ps1 payload's ordinal sorting, so listings agree everywhere. @@ -183,8 +207,9 @@ list_candidates() { [[ -f "$f" ]] || continue f="${f##*/}" case "$f" in - *_BUILDCOPY*|*.txt|*.toml|*.tm|*.tmp|*.log) continue;; + *_BUILDCOPY*) continue;; esac + is_punk_support_name "$f" && continue printf '%s\n' "$f" done fi @@ -411,7 +436,7 @@ freshness_verdict() { fi #deterministic pick: C-collation first value-match, support files #(.txt/.toml/...) filtered before the pick (ps1-payload parity) - vmatch=$(tr -d '\r' < "$sha1file" | awk -v h="$asha" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev '\.(txt|toml|tm|tmp|log)$' | LC_ALL=C sort | head -n 1) + vmatch=$(tr -d '\r' < "$sha1file" | awk -v h="$asha" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev "$punk_support_ext_re" | LC_ALL=C sort | head -n 1) fi if [[ -n "$vmatch" ]]; then mrev=$(rev_of "$vmatch") @@ -678,7 +703,7 @@ case "$action" in [[ -n "$dsha" && "$local_sha1" == "$dsha" ]] && vname="$platform_default" fi if [[ -z "$vname" ]]; then - vname=$(tr -d '\r' < "$sha1local" | awk -v h="$local_sha1" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev '\.(txt|toml|tm|tmp|log)$' | LC_ALL=C sort | head -n 1) + vname=$(tr -d '\r' < "$sha1local" | awk -v h="$local_sha1" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev "$punk_support_ext_re" | LC_ALL=C sort | head -n 1) fi if [[ -n "$vname" ]]; then mid="sha1=$vname" @@ -707,9 +732,7 @@ case "$action" in #skip support files riding in the server's sha1sums beside the #runtimes (artifact metadata tomls etc) - same extension set the #local candidate filter excludes - case "$rname" in - *.txt|*.toml|*.tm|*.tmp|*.log) continue;; - esac + is_punk_support_name "$rname" && continue if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then annot="" [[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)" @@ -760,12 +783,10 @@ case "$action" in fi exit 1 fi - case "$1" in - *_BUILDCOPY|*.txt|*.toml|*.tm|*.tmp) - echo "'$1' is not a selectable runtime" - exit 1 - ;; - esac + if [[ "$1" == *_BUILDCOPY* ]] || is_punk_support_name "$1"; then + echo "'$1' is not a selectable runtime" + exit 1 + fi if [[ ! -f "$archdir/$1" && -f "$archdir/$1.exe" ]]; then #accept the name without its .exe suffix (root-name convention: G-103 #name handling strips only .exe, so the suffixless spelling is diff --git a/src/scriptapps/bin/punk-runtime.ps1 b/src/scriptapps/bin/punk-runtime.ps1 index 02690262..e691c484 100644 --- a/src/scriptapps/bin/punk-runtime.ps1 +++ b/src/scriptapps/bin/punk-runtime.ps1 @@ -62,6 +62,29 @@ function Set-PunkActiveRuntime { [System.IO.File]::WriteAllText($activefile, "active = `"$name`"`n", [System.Text.UTF8Encoding]::new($false)) Write-Host "active runtime set to: $name (recorded in $activefile)" } +#Support-file extensions: things that legitimately sit in a runtime tier beside the +#runtimes but are never themselves selectable runtimes. Filtered from the local +#candidate listing, from 'use', and from sha1 value-matching against the server's +#sha1sums (support rows ride in that file too). Keep this set and the bash payload's +#is_punk_support_name/punk_support_ext_re in step - listings must agree byte-for-byte. +# docs/text .txt .md .html .htm .pdf .url +# config/meta .toml .json .xml .yml .yaml .ini .cfg +# dev leftover .tm .tmp .log .bak .old +# checksums .sha1 .sha256 .asc .sig +# archives .zip .tar .gz .bz2 .xz .7z +#Anything else is treated as a runtime candidate - see bin/runtime/README.md for what +#a runtime tier is expected to contain. +$script:PunkRuntimeSupportExt = @( + ".txt",".md",".html",".htm",".pdf",".url", + ".toml",".json",".xml",".yml",".yaml",".ini",".cfg", + ".tm",".tmp",".log",".bak",".old", + ".sha1",".sha256",".asc",".sig", + ".zip",".tar",".gz",".bz2",".xz",".7z" +) +function Test-PunkRuntimeSupportName { + param([string] $name) + return ($script:PunkRuntimeSupportExt -contains [System.IO.Path]::GetExtension($name).ToLowerInvariant()) +} #installed runtime candidates - exclude build copies and non-runtime files. #ORDINAL name order: culture-sensitive Sort-Object collates differently between #windows powershell (NLS) and pwsh (ICU) - both launch paths exist (the .ps1 @@ -72,7 +95,7 @@ function Get-PunkRuntimeCandidates { if (-not (Test-Path -Path $archfolder -PathType Container)) { return @() } - $files = @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not ($(".txt",".toml",".tm",".tmp",".log") -contains $_.Extension) }) + $files = @(get-childItem -Path $archfolder -File | Where-object Name -Notlike '*_BUILDCOPY*' | Where-object {-not (Test-PunkRuntimeSupportName $_.Name) }) if ($files.Count -le 1) { return $files } @@ -507,8 +530,7 @@ function Write-PunkFreshnessVerdict { $names = @($valdict.Keys) [array]::Sort($names, [System.StringComparer]::Ordinal) foreach ($n in $names) { - $rext = [System.IO.Path]::GetExtension($n) - if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } + if (Test-PunkRuntimeSupportName $n) { continue } if ($valdict[$n] -eq $asha) { $vmatch = $n; break } } } @@ -1176,8 +1198,7 @@ function psmain { $rnames = @($remotedict.Keys) [array]::Sort($rnames, [System.StringComparer]::Ordinal) foreach ($rn in $rnames) { - $rext = [System.IO.Path]::GetExtension($rn) - if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } + if (Test-PunkRuntimeSupportName $rn) { continue } if ($remotedict[$rn] -eq $local_sha1) { $vname = $rn; break } } } @@ -1212,8 +1233,7 @@ function psmain { #skip support files riding in the server's sha1sums beside #the runtimes (artifact metadata tomls etc) - same extension #set the local candidate filter excludes - $rext = [System.IO.Path]::GetExtension($key) - if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } + if (Test-PunkRuntimeSupportName $key) { continue } $annot = "" if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" } write-host -nonewline (" {0,-35} {1,-33} " -f "-", "")