From 3cfbb8921c6e3afb642c337fe1cfb2a99145c06b Mon Sep 17 00:00:00 2001 From: dan-claude-bot Date: Mon, 20 Jul 2026 00:00:57 +0000 Subject: [PATCH] fix: platform reads a manifest whose last line lacks a newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `while IFS='=' read -r k v` drops an unterminated final line: read returns 1 at EOF even having filled k and v. A manifest ending `bootstrapped=...Z` with no trailing \n rendered `RIG 1.2.3` with the timestamp silently gone — the version read fine, so nothing looked wrong. Guarded with `|| [ -n "$k" ]`, the same shape parse_users_file already uses (lib/users-config.sh:47). #61's writer should not have to know whether this reader tolerates a missing newline. Also make human_b fall back like human_kb. With numfmt absent, memory degraded to a raw number while disk printed 'unknown' beside it, from data already in hand. Both found in review of #74. Refs #64 --- commands/platform.sh | 11 +++++++++-- test/cli.sh | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/commands/platform.sh b/commands/platform.sh index 12ccdaa..2bdb999 100755 --- a/commands/platform.sh +++ b/commands/platform.sh @@ -97,7 +97,10 @@ if DF="$(df -PB1 / 2>/dev/null)"; then DISK_TOTAL="$(printf '%s\n' "$DF" | awk 'NR==2 {print $2}')" DISK_FREE="$(printf '%s\n' "$DF" | awk 'NR==2 {print $4}')" fi -human_b() { [ -n "${1:-}" ] && numfmt --to=iec-i "$1" 2>/dev/null || printf 'unknown'; } +human_b() { # bytes -> IEC; falls back like human_kb rather than to 'unknown' + [ -n "${1:-}" ] || { printf 'unknown'; return 0; } + numfmt --to=iec-i "$1" 2>/dev/null || printf '%s B' "$1" +} # --- virtualization --------------------------------------------------------- # THE set -e TRAP: systemd-detect-virt exits NON-ZERO on bare metal while @@ -139,7 +142,11 @@ MARKER="${RIG_ROLE_MARKER:-/etc/rig/role}" manifest_field() { # $1 = key — empty when absent, unreadable or unset local k v [ -r "$MANIFEST" ] || return 0 - while IFS='=' read -r k v; do + # `|| [ -n "$k" ]` so a manifest whose last line lacks a trailing newline + # still yields that line: read returns 1 at EOF even having filled k/v. + # Same guard parse_users_file uses (lib/users-config.sh:47) — #61's writer + # should not have to know whether this reader tolerates a missing \n. + while IFS='=' read -r k v || [ -n "$k" ]; do [ "$k" = "$1" ] || continue printf '%s\n' "$v" return 0 diff --git a/test/cli.sh b/test/cli.sh index a05ecfb..8e93a88 100644 --- a/test/cli.sh +++ b/test/cli.sh @@ -1017,6 +1017,13 @@ check "platform: reads the manifest version" 0 "9.9.9" \ env RIG_MANIFEST="$PLATWORK/manifest" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform check "platform: reads the manifest timestamp" 0 "bootstrapped 2026-07-19T14:24:51Z" \ env RIG_MANIFEST="$PLATWORK/manifest" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform +# ...including one whose last line has no trailing newline: `read` returns 1 at +# EOF even having filled the variables, so an unguarded loop drops that line +# silently — the timestamp would vanish while the version still rendered. #61's +# writer must not have to know this reader's tolerances (found in #74 review). +printf 'version=9.9.9\nbootstrapped=2026-07-19T14:24:51Z' > "$PLATWORK/manifest-nonl" +check "platform: reads a manifest with no trailing newline" 0 "bootstrapped 2026-07-19T14:24:51Z" \ + env RIG_MANIFEST="$PLATWORK/manifest-nonl" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform printf 'role=dev class=human host=yes join=authkey\n' > "$PLATWORK/role" check "platform: renders the role marker's traits" 0 "dev (class=human host=yes join=authkey)" \ env RIG_MANIFEST="$PLATWORK/absent" RIG_ROLE_MARKER="$PLATWORK/role" "$ROOT/bin/rig" platform