From 4f753264b2be0b0c658f23b51c41c7bdc2bfb229 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Fri, 31 Jul 2026 04:34:46 +1000 Subject: [PATCH] punk-runtime: strip CR before sha1sums lookups in the bash payload (punkshell 0.32.3) fetch and list's local-file comparison anchored runtime names at end-of-line without stripping CR first, so a CRLF-published server sha1sums.txt aborted fetch with "Unable to locate hash" on strict grep platforms (GNU and BSD grep; cygwin grep tolerates CRLF, and the ps1 payload is immune via Get-Content line splitting). Both lookups now share the tr -d '\r' form list -remote already used. bin/punk-runtime.cmd regenerated via the scriptwrap machinery; the re-wrap invocation in src/scriptapps/AGENTS.md is corrected to the working -force 1 -askme 0 form found during regeneration. Assisted-by: harness=opencode; primary-model=huggingface/moonshotai/Kimi-K3; api-location=unknown --- CHANGELOG.md | 10 ++++++++++ bin/punk-runtime.cmd | 6 ++++-- punkproject.toml | 2 +- src/scriptapps/AGENTS.md | 2 +- src/scriptapps/bin/punk-runtime.bash | 6 ++++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29fe0e71..f67652fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ 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.32.3] - 2026-07-31 + +- `punk-runtime` bash payload: `fetch` and `list`'s local-file comparison now + strip CR characters before matching sha1sums.txt entries, fixing the + "Unable to locate hash" abort when the artifact server publishes + sha1sums.txt with CRLF line endings (GNU/BSD grep anchor `$` strictly, so + the end-of-line filename match failed even with the entry present; the ps1 + payload was unaffected via Get-Content line splitting). All sha1sums lookups + in the payload now share the CR-stripping form `list -remote` already used. + ## [0.32.2] - 2026-07-30 - Linux lib-tier emission (G-140, achieved): suite_tcl90 gains a `tcllibc-linux` diff --git a/bin/punk-runtime.cmd b/bin/punk-runtime.cmd index 542334ee..3c2a6c20 100755 --- a/bin/punk-runtime.cmd +++ b/bin/punk-runtime.cmd @@ -1800,7 +1800,9 @@ case "$action" in fi #sha1sums.txt line format: * (binary indicator) #(dots in $runtime are regex-any here - acceptable for these filenames) - stored_sha1=$(grep -E "^[0-9a-fA-F]{40} \*${runtime}\$" "$sha1local" | head -n 1 | cut -d' ' -f1) + #strip CR: the server's sha1sums.txt may carry CRLF endings, which + #otherwise defeat the $ anchor below (as every other lookup here does) + stored_sha1=$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${runtime}\$" | 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)" @@ -1943,7 +1945,7 @@ case "$action" in 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) + stored_sha1=$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${f}\$" | head -n 1 | cut -d' ' -f1) mid="" ident="" if [[ -n "$stored_sha1" ]]; then diff --git a/punkproject.toml b/punkproject.toml index 41fd5be3..5bd4cf0f 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.32.2" +version = "0.32.3" license = "BSD-2-Clause" diff --git a/src/scriptapps/AGENTS.md b/src/scriptapps/AGENTS.md index f39d2e1f..5a651437 100644 --- a/src/scriptapps/AGENTS.md +++ b/src/scriptapps/AGENTS.md @@ -13,7 +13,7 @@ Standalone Tcl scripts that serve as entry points for Punk applications and util - Scripts here are invoked directly by the user or by build tools, not loaded as packages. - Wrapper configs (`.toml` files) define how scripts are packaged as platform executables. -- `_wrap.toml` scriptsets are the SOURCE of the generated polyglot `.cmd` scripts in `/bin`, and bin-deployed scriptsets live in the `bin/` subfolder here (e.g `bin/punk-runtime.ps1` + `bin/punk-runtime.bash` + `bin/punk-runtime_wrap.toml` -> `/bin/punk-runtime.cmd` via `punk::mix::commandset::scriptwrap::multishell punk-runtime -askme 0`, run from `src/scriptapps/bin`, alongside the `punk-getzig.*` scriptset). Fixes to a `bin/*.cmd` polyglot are made in the scriptset sources and re-wrapped - never in the output file. Regenerate and commit the bin output together with payload changes: `src/tests/modules/punk/mix/testsuites/scriptwrap/runtimecmd_roundtrip.test` pins the punk-runtime scriptset round-trip byte-identical against `bin/punk-runtime.cmd`, and `src/tests/shell/testsuites/binscripts/dtplite.test` pins the dtplite scriptset (`dtplite.tcl` + `dtplite_wrap.toml`, wrapped from this folder) against `bin/dtplite.cmd` the same way. Payload scripts embedded in polyglots must be LF-only - the wrap embeds payload bytes verbatim and the multishell output contract is LF-only. +- `_wrap.toml` scriptsets are the SOURCE of the generated polyglot `.cmd` scripts in `/bin`, and bin-deployed scriptsets live in the `bin/` subfolder here (e.g `bin/punk-runtime.ps1` + `bin/punk-runtime.bash` + `bin/punk-runtime_wrap.toml` -> `/bin/punk-runtime.cmd` via `punk::mix::commandset::scriptwrap::multishell punk-runtime -force 1 -askme 0` (after `package require punk::mix::commandset::scriptwrap`; `-force 1` is required because `-askme 0` alone aborts when the target exists), run from `src/scriptapps/bin`, alongside the `punk-getzig.*` scriptset). Fixes to a `bin/*.cmd` polyglot are made in the scriptset sources and re-wrapped - never in the output file. Regenerate and commit the bin output together with payload changes: `src/tests/modules/punk/mix/testsuites/scriptwrap/runtimecmd_roundtrip.test` pins the punk-runtime scriptset round-trip byte-identical against `bin/punk-runtime.cmd`, and `src/tests/shell/testsuites/binscripts/dtplite.test` pins the dtplite scriptset (`dtplite.tcl` + `dtplite_wrap.toml`, wrapped from this folder) against `bin/dtplite.cmd` the same way. Payload scripts embedded in polyglots must be LF-only - the wrap embeds payload bytes verbatim and the multishell output contract is LF-only. - The `spud/` directory holds the spud build tool's app scripts. - The `tools/` directory holds miscellaneous build and deployment utilities. diff --git a/src/scriptapps/bin/punk-runtime.bash b/src/scriptapps/bin/punk-runtime.bash index d69d62eb..c805cf1f 100644 --- a/src/scriptapps/bin/punk-runtime.bash +++ b/src/scriptapps/bin/punk-runtime.bash @@ -520,7 +520,9 @@ case "$action" in fi #sha1sums.txt line format: * (binary indicator) #(dots in $runtime are regex-any here - acceptable for these filenames) - stored_sha1=$(grep -E "^[0-9a-fA-F]{40} \*${runtime}\$" "$sha1local" | head -n 1 | cut -d' ' -f1) + #strip CR: the server's sha1sums.txt may carry CRLF endings, which + #otherwise defeat the $ anchor below (as every other lookup here does) + stored_sha1=$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${runtime}\$" | 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)" @@ -663,7 +665,7 @@ case "$action" in 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) + stored_sha1=$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${f}\$" | head -n 1 | cut -d' ' -f1) mid="" ident="" if [[ -n "$stored_sha1" ]]; then