Files
samios/tests/test_version.sh
2026-08-12 02:45:12 -07:00

806 lines
38 KiB
Bash
Executable File

#!/bin/bash
# Test: VERSION single-source-of-truth
# Locks the invariant: every version string in the repo must resolve to
# ./VERSION at runtime. No duplicate hardcoding allowed.
#
# Note: many assertions below reference variables (`cli_v`, `voice_v`,
# `mirror_v`, etc.) that are assigned inside single-quoted `bash -c`
# subshells. shellcheck cannot trace through the quoting layer, so it
# raises SC2154 warnings. These are intentional and the assertions DO
# verify the variables — see the test body.
# shellcheck disable=SC2154,SC2289,SC1011,SC1078,SC1083
source "$(dirname "$0")/test_helper.sh"
suite "VERSION single-source-of-truth tests"
VERSION_FILE="$REPO_ROOT/VERSION"
AIROOTFS_VERSION="$PROFILE_DIR/airootfs/etc/samios-version"
SAMIOS_CLI="$PROFILE_DIR/airootfs/usr/local/bin/samios"
INSTALLER="$SCRIPTS_DIR/samios-installer.sh"
PROFILEDEF="$PROFILE_DIR/profiledef.sh"
VOICE_BRIDGE="$SCRIPTS_DIR/voice-bridge.py"
PKGBUILD="$SCRIPTS_DIR/../packages/samios-branding/PKGBUILD"
DESKTOP_SETUP="$SCRIPTS_DIR/samios-desktop-setup.sh"
BASHRC="$PROFILE_DIR/airootfs/root/.bashrc"
ZSHRC="$PROFILE_DIR/airootfs/root/.zshrc"
# ── Hardcoded-release-version detector ────────────────────────────────────
# Scans a file for any X.Y.Z release-version literal, EXCLUDING:
# - lines starting with # (comments documenting SSOT design)
# - the documented fallback "0.0.0-unknown" string
# - test fixture strings like "9.9.9-test" and "0.2.0-rc1" which
# intentionally test version-set and prerelease flows
# The detector must catch ANY release-version literal, including:
# - version=1.2.3 (assignment)
# - "1.2.3" (string literal)
# - '1.2.3' (single-quoted string)
# - 1.2.3-foo (prerelease)
# - 1.2.3+bar (build metadata)
# - SamiOS 1.2.3 (display string)
# - VERSION=1.2.3 OR VERSION="1.2.3" (variable assignment)
detect_hardcoded_release() {
# detect_hardcoded_release <file>
# Exits 0 (clean) if no release-version literal is found; exits 1 if
# one is found.
#
# Production files: only the documented "0.0.0-unknown" fallback is
# allowed (matches the placeholder our resolvers print when the
# canonical VERSION cannot be located). Any other X.Y.Z literal
# (including prerelease, build-meta, v-prefixed, etc.) is a violation.
#
# Test files (under tests/): the same rule applies EXCEPT that two
# additional test fixtures are tolerated (0.2.0-rc1 in test 13's
# prerelease assertion, and 9.9.9-test in test 12's version-set
# assertion). Production components must NEVER contain these strings.
#
# Anchored allowed-token matching: each allowed token is matched as a
# complete regex match with non-alphanumeric boundaries on both sides.
# This prevents `127.0.0.10` from being eaten as `127.0.0.1` followed
# by `0` — `127.0.0.1` only matches at positions where the character
# AFTER it is non-alphanumeric, so `127.0.0.10` keeps its `0` intact
# and the version literal `127.0.0.10` is correctly flagged.
local f="$1"
local version_pattern='(^|[^a-zA-Z0-9])(v?[0-9]+[.][0-9]+[.][0-9]+([-+][a-zA-Z0-9.-]+)*)([^a-zA-Z0-9]|$)'
# Anchored allowed-token regex: same boundary rules as version_pattern.
# The middle group is the allowed token itself.
local allowed_pattern
if [[ "$f" == */tests/test_version.sh ]]; then
# Test fixture: tolerate the two prerelease/version-set fixtures
# AND the documented fallback + localhost IPs.
allowed_pattern='(^|[^a-zA-Z0-9])(0[.]0[.]0-unknown|9[.]9[.]9-test|0[.]2[.]0-rc1|127[.]0[.]0[.]1|127[.]0[.]1[.]1)([^a-zA-Z0-9]|$)'
else
# Production: only the documented 0.0.0-unknown fallback + localhost IPs
allowed_pattern='(^|[^a-zA-Z0-9])(0[.]0[.]0-unknown|127[.]0[.]0[.]1|127[.]0[.]1[.]1)([^a-zA-Z0-9]|$)'
fi
# Comment detection: a line is a comment if its first non-whitespace
# character is '#'. This handles shell, Python (after the leading
# whitespace), .desktop, and other line-oriented comment syntaxes.
local hits
# Strategy: find all version-pattern matches and allowed-pattern matches
# on each line. If a version-pattern match's character range does NOT
# fully overlap with an allowed-pattern match's range, it's a violation.
hits="$(grep -nE "$version_pattern" "$f" \
| grep -vE "^[^:]+:[ \\t]*#" \
| awk -F':' -v version_pat="$version_pattern" -v allowed_pat="$allowed_pattern" '
BEGIN { IGNORECASE = 0 }
{
content = substr($0, length($1) + 2)
# Build a "covered" map: for each allowed match, mark its
# character positions as covered. Then check each version
# match: if its range is NOT entirely covered, flag it.
delete covered
pos = 1
while (pos <= length(content) && match(substr(content, pos), allowed_pat)) {
s = pos + RSTART - 1
e = pos + RSTART + RLENGTH - 2
for (i = s; i <= e; i++) covered[i] = 1
# Advance past this match to find the next allowed match
pos = e + 1
}
# Now scan content for version matches and report any
# that are not fully covered.
vpos = 1
while (vpos <= length(content) && match(substr(content, vpos), version_pat)) {
vs = vpos + RSTART - 1
ve = vpos + RSTART + RLENGTH - 2
covered_full = 1
for (i = vs; i <= ve; i++) if (!covered[i]) { covered_full = 0; break }
if (!covered_full) { print $0; break }
vpos = ve + 1
}
}
')"
if [ -n "$hits" ]; then
echo "HARD-CODED RELEASE VERSIONS FOUND in $f:" >&2
echo "$hits" >&2
return 1
fi
return 0
}
# Export the function so it can be called from `bash -c '...'` subshells
export -f detect_hardcoded_release
# ── Repository-wide SSOT scan ────────────────────────────────────────────
# Every production file in the repo must pass detect_hardcoded_release.
# "Production" excludes tests/ (where we tolerate fixtures) and the
# canonical VERSION + airootfs mirror (which by definition contain
# the literal). If you add a new production file with a hardcoded
# version, it will appear here.
assert "every production file passes detect_hardcoded_release" \
bash -c '
violations=0
# Scan all production files. We exclude by EXACT canonical paths
# (not substring patterns), so a nested or unrelated file named
# VERSION cannot accidentally bypass SSOT enforcement.
while IFS= read -r -d "" f; do
case "$f" in
*/.git/*|*/tests/*) continue ;;
esac
# Compare against the exact canonical paths (resolved).
_resolved="$(cd "$(dirname "$f")" && pwd)/$(basename "$f")"
_root_version="$(cd "$REPO_ROOT" && pwd)/VERSION"
_root_mirror="$(cd "$REPO_ROOT" && pwd)/packaging/archiso/airootfs/etc/samios-version"
if [ "$_resolved" = "$_root_version" ] || [ "$_resolved" = "$_root_mirror" ]; then
continue
fi
if ! detect_hardcoded_release "$f"; then
violations=$((violations + 1))
fi
done < <(find "$REPO_ROOT" -type f -not -path "*/.git/*" -not -path "*/tests/*" -print0)
if [ "$violations" -gt 0 ]; then
echo "FAIL: $violations production files contain hardcoded version literals"
exit 1
fi
'
# ── Regression test: 0.2.0-rc1 is REJECTED in production components ──────
# The prerelease string "0.2.0-rc1" is allowed ONLY in test_version.sh as
# a test fixture. If it appears in any production component, the SSOT
# invariant is violated.
assert "regression: 0.2.0-rc1 is REJECTED in production components" \
bash -c '
# Find any production file containing 0.2.0-rc1 outside of test_version.sh
while IFS= read -r -d "" f; do
case "$f" in
*/.git/*|*/tests/*) continue ;;
esac
if grep -q "0\.2\.0-rc1" "$f" 2>/dev/null; then
echo "FAIL: $f contains 0.2.0-rc1 in production"
exit 1
fi
done < <(find "$REPO_ROOT" -type f -not -path "*/.git/*" -not -path "*/tests/*" -print0)
'
# ── Regression test: 9.9.9-test is REJECTED in production components ─────
# Same as above but for the version-set test fixture.
assert "regression: 9.9.9-test is REJECTED in production components" \
bash -c '
while IFS= read -r -d "" f; do
case "$f" in
*/.git/*|*/tests/*) continue ;;
esac
if grep -q "9\.9\.9-test" "$f" 2>/dev/null; then
echo "FAIL: $f contains 9.9.9-test in production"
exit 1
fi
done < <(find "$REPO_ROOT" -type f -not -path "*/.git/*" -not -path "*/tests/*" -print0)
'
# ── Detector self-tests ──────────────────────────────────────────────────
# The detector is a non-trivial regex; these tests verify it catches every
# form we claim and does not raise false positives. The tests construct
# synthetic files in $TMPDIR, run detect_hardcoded_release against them,
# and check the result. Each form is a separate assertion so a regression
# points at exactly which form broke.
assert "detector self-test: catches double-quoted X.Y.Z literal" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo \"1.2.3\"" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed \"1.2.3\"" >&2; exit 1
'
assert "detector self-test: catches single-quoted X.Y.Z literal" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo '"'"'1.2.3'"'"'" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed '"'"'1.2.3'"'"'" >&2; exit 1
'
assert "detector self-test: catches bare X.Y.Z literal" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo 1.2.3" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed bare 1.2.3" >&2; exit 1
'
assert "detector self-test: catches prerelease X.Y.Z-foo literal" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo 1.2.3-rc1" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed 1.2.3-rc1" >&2; exit 1
'
assert "detector self-test: catches build-metadata X.Y.Z+bar literal" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo 1.2.3+build.5" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed 1.2.3+build.5" >&2; exit 1
'
assert "detector self-test: catches SamiOS X.Y.Z display string" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo \"SamiOS 1.2.3 (WSL Desktop)\"" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed SamiOS 1.2.3" >&2; exit 1
'
assert "detector self-test: catches VERSION=1.2.3 assignment" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "VERSION=\"1.2.3\"" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed VERSION=1.2.3" >&2; exit 1
'
assert "detector self-test: catches v-prefixed v1.2.3" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo v1.2.3" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed v1.2.3" >&2; exit 1
'
assert "detector self-test: catches X.Y.Z in path (/opt/1.2.3/bin)" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "PATH=/opt/1.2.3/bin" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed path-embedded 1.2.3" >&2; exit 1
'
assert "detector self-test: catches X.Y.Z in colon-delimited value" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "key=val:1.2.3:rest" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed colon-delimited 1.2.3" >&2; exit 1
'
assert "detector self-test: catches X.Y.Z in brackets [1.2.3]" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "x=[1.2.3]" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed bracketed 1.2.3" >&2; exit 1
'
assert "detector self-test: ALLOWS 0.0.0-unknown fallback" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "VERSION=0.0.0-unknown" > "$tmpf"
detect_hardcoded_release "$tmpf" && exit 0
echo "FAILED: detector flagged documented fallback 0.0.0-unknown" >&2; exit 1
'
assert "detector self-test: ALLOWS comments (lines starting with #)" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "# This is a comment with 1.2.3 in it" > "$tmpf"
detect_hardcoded_release "$tmpf" && exit 0
echo "FAILED: detector flagged commented-out version" >&2; exit 1
'
assert "detector self-test: ALLOWS localhost IP (127.0.0.1)" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "127.0.0.1 localhost" > "$tmpf"
detect_hardcoded_release "$tmpf" && exit 0
echo "FAILED: detector flagged localhost IP" >&2; exit 1
'
assert "detector self-test: catches MIXED allowed+forbidden on same line" \
bash -c '
# A line that contains BOTH the allowed fallback and a forbidden
# release-version literal must be flagged. This catches the bug
# where `grep -v '\''0.0.0-unknown'\''` would suppress the entire line
# and miss the forbidden literal.
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo 0.0.0-unknown 1.2.3" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed mixed allowed/forbidden line" >&2; exit 1
'
assert "detector self-test: ALLOWS indented # comments with version literal" \
bash -c '
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
cat > "$tmpf" << '"'"'EOF'"'"'
# Top-level comment with 1.2.3
# Indented comment with 1.2.4
echo real_code
EOF
detect_hardcoded_release "$tmpf" && exit 0
echo "FAILED: detector flagged commented version literals" >&2; exit 1
'
assert "detector self-test: catches 127.0.0.10 (substring of allowed IP)" \
bash -c '
# 127.0.0.10 contains 127.0.0.1 as a substring. A naive substring
# filter would erase `127.0.0.1` and leave `0` (which does NOT
# match X.Y.Z), letting the real release-version literal
# `127.0.0.10` slip through. Our anchored detector must catch it.
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo 127.0.0.10" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed 127.0.0.10" >&2; exit 1
'
assert "detector self-test: catches 0.0.0-unknown+build (suffix of fallback)" \
bash -c '
# 0.0.0-unknown+build contains 0.0.0-unknown as a substring. A naive
# filter would erase `0.0.0-unknown` and leave `+build` (which
# does NOT match X.Y.Z), letting the build-metadata version literal
# `0.0.0-unknown+build` slip through.
tmpf="$(mktemp)"
trap "rm -f '\''$tmpf'\''" EXIT
echo "echo 0.0.0-unknown+build" > "$tmpf"
detect_hardcoded_release "$tmpf" || exit 0
echo "FAILED: detector missed 0.0.0-unknown+build" >&2; exit 1
'
# ── 1. Canonical file exists and is a valid semver-ish string ──────────────
assert_file_exists "VERSION file exists at repo root" "$VERSION_FILE"
assert "VERSION is non-empty single-line" \
bash -c '
[ "$(wc -l < "'"$VERSION_FILE"'")" -eq 1 ] && \
[ -n "$(cat "'"$VERSION_FILE"'")" ]
'
assert "VERSION matches semver-ish pattern (X.Y.Z optional suffix)" \
bash -c '
grep -qE "^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$" "'"$VERSION_FILE"'"
'
# ── 2. airootfs mirror is byte-identical to root VERSION ───────────────────
assert_file_exists "airootfs mirror /etc/samios-version exists" "$AIROOTFS_VERSION"
assert "airootfs VERSION mirror matches repo root VERSION" \
bash -c '
diff -q "'"$VERSION_FILE"'" "'"$AIROOTFS_VERSION"'" >/dev/null
'
# ── 3. samios CLI must NOT hardcode any release-version literal ──────────
assert_file_exists "samios CLI exists" "$SAMIOS_CLI"
assert "samios CLI has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$SAMIOS_CLI"'"'
assert "samios CLI references ./VERSION walk-up lookup" \
bash -c '
grep -q "/VERSION" "'"$SAMIOS_CLI"'" && \
grep -q "_lookup_version" "'"$SAMIOS_CLI"'"
'
assert "samios CLI prints repo VERSION when run from repo root" \
bash -c '
cd "'"$REPO_ROOT"'" && \
expected="$(cat VERSION)" && \
actual="$(bash "'"$SAMIOS_CLI"'" version | sed -E "s/^SamiOS v//" | head -n1)" && \
[ "$actual" = "$expected" ]
'
# ── 4. Installer must NOT hardcode any release-version literal ───────────
assert_file_exists "samios-installer.sh exists" "$INSTALLER"
assert "installer has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$INSTALLER"'"'
assert "installer references ./VERSION walk-up lookup" \
bash -c '
grep -q "_VERSION_FILE\|/VERSION" "'"$INSTALLER"'"
'
# Verify the installer's actual heredoc-emitted CLI has the _lookup_version
# function — not a substituted $VERSION literal.
assert "installer heredoc emits CLI with _lookup_version (no literal)" \
bash -c '
# The installer MUST NOT substitute $VERSION into a literal at
# the top of the emitted CLI. We extract the heredoc body (between
# the << SAMIOS_CLI marker line and the closing SAMIOS_CLI) and
# check for the forbidden pattern.
heredoc_open="$(grep -n "<< .*SAMIOS_CLI" "'"$INSTALLER"'" | head -1 | cut -d: -f1)"
heredoc_close="$(awk -v open_line="$heredoc_open" "NR>open_line && /^SAMIOS_CLI\$/{print NR; exit}" "'"$INSTALLER"'")"
if [ -z "$heredoc_open" ] || [ -z "$heredoc_close" ]; then
echo "could not locate SAMIOS_CLI heredoc in installer (open=$heredoc_open close=$heredoc_close)"
exit 1
fi
heredoc_body="$(sed -n "${heredoc_open},${heredoc_close}p" "'"$INSTALLER"'")"
# Forbidden: a literal VERSION="${VERSION}" (substituted from outer scope)
if echo "$heredoc_body" | grep -qE "^VERSION=\"\\\$\{VERSION\}\""; then
echo "installer emits VERSION=\"\${VERSION}\" literal at top of heredoc"
exit 1
fi
# Required: the heredoc body must reference _lookup_version so
# the installed CLI uses the walk-up pattern.
if ! echo "$heredoc_body" | grep -q "_lookup_version"; then
echo "installer heredoc does not reference _lookup_version"
exit 1
fi
'
# ── 5. profiledef.sh must NOT hardcode any release-version literal ────────
assert_file_exists "profiledef.sh exists" "$PROFILEDEF"
assert "profiledef.sh has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$PROFILEDEF"'"'
assert "profiledef.sh references ./VERSION walk-up lookup" \
bash -c '
grep -q "_iso_version\|/VERSION" "'"$PROFILEDEF"'"
'
# ── 6. voice-bridge.py must read from ./VERSION ────────────────────────────
assert_file_exists "voice-bridge.py exists" "$VOICE_BRIDGE"
assert "voice-bridge.py parses as valid Python" \
bash -c '
python3 -c "import ast; ast.parse(open(\"'"$VOICE_BRIDGE"'\").read())"
'
assert "voice-bridge.py has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$VOICE_BRIDGE"'"'
assert "voice-bridge.py resolves VERSION from repo root" \
bash -c '
cd "'"$REPO_ROOT"'" && \
expected="$(cat VERSION)" && \
actual="$(python3 "'"$VOICE_BRIDGE"'" --version | sed -E "s/^SamiOS voice-bridge v//")" && \
[ "$actual" = "$expected" ]
'
# ── 7. PKGBUILD must NOT hardcode any release-version literal ────────────
assert_file_exists "PKGBUILD exists" "$PKGBUILD"
assert "PKGBUILD has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$PKGBUILD"'"'
assert "PKGBUILD references ../../../VERSION" \
bash -c '
grep -q "../../../VERSION" "'"$PKGBUILD"'"
'
# ── 8. desktop-setup.sh must NOT hardcode any release-version literal ──────
assert_file_exists "samios-desktop-setup.sh exists" "$DESKTOP_SETUP"
assert "desktop-setup.sh has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$DESKTOP_SETUP"'"'
# ── 9. .bashrc / .zshrc must NOT hardcode any release-version literal ────
assert ".bashrc has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$BASHRC"'"'
assert ".zshrc has no hardcoded release-version literal" \
bash -c 'detect_hardcoded_release "'"$ZSHRC"'"'
# ── 10. Cross-system invariant: CLI, installer, profiledef, voice-bridge ──
# Test the actual code paths in each file. For the installer and profiledef,
# we extract their version-resolution blocks from the real files (not
# reimplementations) into temp helpers, then rewrite the $0-relative path
# to point at the actual file's directory so the walk-up starts in the
# right place.
PROFILEDEF_RESOLVER="$(mktemp)"
PROFILEDEF_DIR="$(dirname "$PROFILEDEF")"
{
sed -n '/^# ── Canonical version source ───/,/^unset _d$/p' \
"$PROFILEDEF"
echo 'echo "$_iso_version"'
} > "$PROFILEDEF_RESOLVER"
sed -i "s|cd \"\\\$(dirname \"\\\$0\")\"|cd \"$PROFILEDEF_DIR\"|g" "$PROFILEDEF_RESOLVER"
chmod +x "$PROFILEDEF_RESOLVER"
# Extract the installer's version-resolution block (top of file) into a
# real shell script we can run. The block uses $0 to find its own
# directory; when extracted standalone, $0 becomes the helper's path
# (under /tmp), which isn't where VERSION lives. We rewrite the $0
# lookup to start from the installer's actual directory.
INSTALLER_DIR="$(dirname "$INSTALLER")"
INSTALLER_RESOLVER="$(mktemp)"
{
sed -n '/^# ── Canonical version source ───/,/^unset _d _VERSION_FILE/p' \
"$INSTALLER"
echo 'echo "$VERSION"'
} > "$INSTALLER_RESOLVER"
# Rewrite $0 references inside the extracted block so they resolve from
# the installer's actual directory, not from /tmp/codex-judge-XXXXXX.
sed -i "s|cd \"\\\$(dirname \"\\\$0\")\"|cd \"$INSTALLER_DIR\"|g" "$INSTALLER_RESOLVER"
chmod +x "$INSTALLER_RESOLVER"
assert "all four components agree on VERSION (tested from real files)" \
bash -c '
cd "'"$REPO_ROOT"'" || exit 1
expected="$(cat VERSION)"
cli_v="$(bash "'"$SAMIOS_CLI"'" version | head -n1 | sed -E "s/^SamiOS v//")"
voice_v="$(python3 "'"$VOICE_BRIDGE"'" --version | sed -E "s/^SamiOS voice-bridge v//")"
inst_v="$("'"$INSTALLER_RESOLVER"'" | tail -n1)"
prof_v="$("'"$PROFILEDEF_RESOLVER"'")"
if [ "$cli_v" != "$expected" ]; then
echo "CLI=$cli_v expected=$expected"; exit 1
fi
if [ "$voice_v" != "$expected" ]; then
echo "voice=$voice_v expected=$expected"; exit 1
fi
if [ "$inst_v" != "$expected" ]; then
echo "installer=$inst_v expected=$expected"; exit 1
fi
if [ "$prof_v" != "$expected" ]; then
echo "profiledef=$prof_v expected=$expected"; exit 1
fi
'
rm -f "$PROFILEDEF_RESOLVER" "$INSTALLER_RESOLVER"
# ── 11. PKGBUILD resolves pkgver to the canonical VERSION ──────────────────
# Source the PKGBUILD in a subshell (PKGBUILD is bash-runnable) and capture
# the resulting pkgver. This proves the conditional `pkgver=` block in the
# PKGBUILD evaluates to the canonical VERSION.
assert "PKGBUILD pkgver resolves to VERSION (sourced)" \
bash -c '
cd "'"$REPO_ROOT"'"/packaging/packages/samios-branding && \
expected="$(head -n1 ../../../VERSION)" && \
actual="$(set -e; source PKGBUILD >/dev/null 2>&1 && echo "$pkgver")" && \
[ "$actual" = "$expected" ] || { echo "actual=$actual expected=$expected"; exit 1; }
'
# ── 11b. PKGBUILD SSOT is absolute (no SAMIOS_PKGVER_OVERRIDE escape) ─────
# The previous PKGBUILD had a SAMIOS_PKGVER_OVERRIDE env var that allowed
# pkgver to differ from VERSION. That violated the SSOT invariant. Verify
# the override is removed so release builds cannot diverge.
assert "PKGBUILD has no SAMIOS_PKGVER_OVERRIDE escape hatch (SSOT is absolute)" \
bash -c '
# Look for SAMIOS_PKGVER_OVERRIDE outside of comments (lines
# starting with #). Comments can mention the variable for
# documentation purposes; only executable code that references
# it would violate SSOT.
if grep -n "SAMIOS_PKGVER_OVERRIDE" "'"$PKGBUILD"'" | grep -vE "^[0-9]+:#"; then
echo "PKGBUILD still references SAMIOS_PKGVER_OVERRIDE outside a comment — SSOT invariant violated"
grep -n "SAMIOS_PKGVER_OVERRIDE" "'"$PKGBUILD"'" | grep -vE "^[0-9]+:#"
exit 1
fi
# Also verify the override does not actually bypass VERSION, in
# case some future shell magic reintroduces the override.
cd "'"$REPO_ROOT"'"/packaging/packages/samios-branding && \
expected="$(head -n1 ../../../VERSION)" && \
actual="$(SAMIOS_PKGVER_OVERRIDE=99.99.99-bogus bash -c "set -e; source PKGBUILD >/dev/null 2>&1; echo \$pkgver")" && \
[ "$actual" = "$expected" ] || { echo "override leaked: actual=$actual expected=$expected"; exit 1; }
'
assert "regression: hash-based diff catches deletions (not just additions)" \
bash -c '
# The version-set test (assertion above) counts only the right
# side of the diff. A pure deletion produces only a left-side
# line. Verify the diff-based detection we use catches BOTH
# sides: synthesize a fake before/after where a file is deleted
# and a different file is added, then count unique filenames.
diff_file="$(mktemp)"
trap "rm -f '\''$diff_file'\''" EXIT
cat > "$diff_file" << '"'"'DIFF_EOF'"'"'
< d84523f8671da4c8b99f543127715bb8c562fafe8675016112c6d260d14e761b ./some-deleted-file.txt
> 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef ./some-new-file.txt
DIFF_EOF
count="$(awk '"'"'/^[<>] / {print $NF}'"'"' "$diff_file" | sort -u | wc -l)"
if [ "$count" -ne 2 ]; then
echo "FAIL: expected 2 unique filenames (one deleted + one added), got $count"
exit 1
fi
'
# ── 12. End-to-end: version-set mutates only VERSION + mirror ─────────────
assert "version-set mutates only VERSION + airootfs mirror (no other files)" \
bash -c '
clone="$(mktemp -d)"
diff_file="$(mktemp)"
# Cleanup trap uses $clone and $diff_file from the surrounding scope
trap "rm -rf ${clone:?} ${diff_file:?}" EXIT
# Copy with .git so any pre-existing modifications are visible as
# baseline (so we can detect whether version-set changed anything
# by content, not by index state).
cp -a "$REPO_ROOT" "$clone/repo"
cd "$clone/repo" || exit 1
orig_version="$(cat VERSION)"
orig_mirror="$(head -n1 packaging/archiso/airootfs/etc/samios-version)"
# Snapshot the content hash of every file in the repo (excluding
# .git) BEFORE the mutation. We use this to detect any file change,
# not git status which can be misleading for already-modified files.
before_hashes="$(find . -path ./.git -prune -o -type f -print0 2>/dev/null \
| xargs -0 sha256sum 2>/dev/null | sort)"
# `make version-set` must SUCCEED. If it fails (e.g. bad NEW value,
# missing make), the test fails — not silently passes.
if ! make version-set NEW=9.9.9-test >/dev/null 2>&1; then
echo "make version-set NEW=9.9.9-test failed unexpectedly"
exit 1
fi
# VERSION must actually have changed.
if [ "$(cat VERSION)" != "9.9.9-test" ]; then
echo "VERSION did not change to 9.9.9-test"
exit 1
fi
# Mirror must actually have changed.
if [ "$(head -n1 packaging/archiso/airootfs/etc/samios-version)" != "9.9.9-test" ]; then
echo "airootfs mirror did not sync to 9.9.9-test"
exit 1
fi
# Snapshot hashes AFTER the mutation.
after_hashes="$(find . -path ./.git -prune -o -type f -print0 2>/dev/null \
| xargs -0 sha256sum 2>/dev/null | sort)"
# Diff before vs after. Count BOTH sides of the diff (a deletion
# produces only a left-side line; an addition produces only a
# right-side line; a modification produces one of each). Use the
# parsed filename-to-hash maps to count unique files that
# changed in either direction, not just added/right-side lines.
diff <(echo "$before_hashes") <(echo "$after_hashes") > "$diff_file"
# Extract the set of filenames that appear on either side of the
# diff. `awk` gets us the distinct filenames regardless of side.
changed_count="$(awk "/^[<>] / {print \$NF}" "$diff_file" | sort -u | wc -l)"
if [ "$changed_count" -ne 2 ]; then
echo "expected exactly 2 files to change (VERSION + mirror), got $changed_count:"
awk "/^[<>] / {print \$0}" "$diff_file"
exit 1
fi
# Verify the two changed files are VERSION and the mirror (not
# some other files). The diff format is "<sha256> <filename>";
# match on the filename suffix.
unexpected="$(awk "/^[<>] / {print \$NF}" "$diff_file" | sort -u)"
unexpected="$(echo "$unexpected" | grep -vE "VERSION$|packaging/archiso/airootfs/etc/samios-version$")"
if [ -n "$unexpected" ]; then
echo "version-set changed unexpected files:"
echo "$unexpected"
exit 1
fi
# Restore clone state — the developers checkout was never touched.
# We restore to the original VERSION we captured, not to a
# hardcoded value, so this test is safe to run at any version.
echo "$orig_version" > VERSION
if ! make version-sync >/dev/null 2>&1; then
echo "make version-sync failed during restore"
exit 1
fi
# Verify restoration succeeded by snapshotting again and comparing
# to the original before-snapshot. The post-restore state must
# match the original content hash (no stray modifications).
restore_hashes="$(find . -path ./.git -prune -o -type f -print0 2>/dev/null \
| xargs -0 sha256sum 2>/dev/null | sort)"
if [ "$before_hashes" != "$restore_hashes" ]; then
echo "restore failed — clone does not match original"
diff <(echo "$before_hashes") <(echo "$restore_hashes") | head -10
exit 1
fi
# Sanity: the values we saved at the top must be the canonical
# values, not arbitrary ones. (We already know VERSION=0.1.0 from
# earlier tests, but this guards against the test running against
# an unusual repo state.)
if [ -z "$orig_version" ] || [ -z "$orig_mirror" ]; then
echo "could not read original VERSION or mirror; bail"
exit 1
fi
'
# ── 13. Prerelease VERSION works through the whole stack ─────────────────
# Mutate VERSION to a prerelease and verify ALL resolvers see it correctly:
# CLI, voice-bridge, airootfs mirror, installer (resolves through its own
# walk-up), profiledef (resolves through its own walk-up), desktop-setup
# (resolves through its own walk-up), and PKGBUILD (transforms to underscore).
#
# Use a cleanup trap and preserve the original VERSION rather than
# hardcoding 0.1.0, so this test is safe to run at any version.
assert "prerelease VERSION (e.g. 0.2.0-rc1) flows through all 7 components" \
bash -c '
clone="$(mktemp -d)"
trap "rm -rf '\''$clone'\''" EXIT
cp -a "$REPO_ROOT" "$clone/repo"
cd "$clone/repo" || exit 1
orig_version="$(cat VERSION)"
echo "0.2.0-rc1" > VERSION
if ! make version-sync >/dev/null 2>&1; then
echo "make version-sync failed"; exit 1
fi
cli_v="$(bash packaging/archiso/airootfs/usr/local/bin/samios version | head -n1 | sed -E "s/^SamiOS v//")"
voice_v="$(python3 packaging/scripts/voice-bridge.py --version | sed -E "s/^SamiOS voice-bridge v//")"
mirror_v="$(head -n1 packaging/archiso/airootfs/etc/samios-version)"
# Installer resolves through its own walk-up block at the top of the file
installer_resolver="$(mktemp)"
sed -n "/^# ── Canonical version source ───/,/^unset _d _VERSION_FILE/p" \
packaging/scripts/samios-installer.sh | sed "s|cd \"\\\$(dirname \"\\\$0\")\"|cd \"$(pwd)/packaging/scripts\"|g" \
> "$installer_resolver"
printf "echo \"\$VERSION\"\n" >> "$installer_resolver"
chmod +x "$installer_resolver"
installer_v="$("$installer_resolver" | tail -n1)"
rm -f "$installer_resolver"
# Profiledef resolves through its own walk-up block
profiledef_resolver="$(mktemp)"
sed -n "/^# ── Canonical version source ───/,/^unset _d$/p" \
packaging/archiso/profiledef.sh | sed "s|cd \"\\\$(dirname \"\\\$0\")\"|cd \"$(pwd)/packaging/archiso\"|g" \
> "$profiledef_resolver"
printf "echo \"\$_iso_version\"\n" >> "$profiledef_resolver"
chmod +x "$profiledef_resolver"
profiledef_v="$("$profiledef_resolver" | tail -n1)"
rm -f "$profiledef_resolver"
# Desktop-setup resolves through its own walk-up block. Extract
# the resolution block up to (but not including) the
# echo-write line — that line writes to a system path we cant
# touch in a test.
desktop_start="$(grep -n "# Version file — generate" packaging/scripts/samios-desktop-setup.sh | head -1 | cut -d: -f1)"
desktop_end_pre="$(grep -n "echo \"SamiOS.*> /usr/share/samios/version" packaging/scripts/samios-desktop-setup.sh | head -1 | cut -d: -f1)"
desktop_end=$((desktop_end_pre - 1))
desktop_resolver="$(mktemp)"
if [ -z "$desktop_start" ] || [ -z "$desktop_end_pre" ]; then
echo "could not locate desktop-setup version block markers"
exit 1
fi
sed -n "${desktop_start},${desktop_end}p" packaging/scripts/samios-desktop-setup.sh | \
sed "s|cd \"\\\$(dirname \"\\\$0\")\"|cd \"$(pwd)/packaging/scripts\"|g" \
> "$desktop_resolver"
printf "echo \"\$_SAMIOS_DESK_VERSION\"\n" >> "$desktop_resolver"
chmod +x "$desktop_resolver"
desktop_v="$("$desktop_resolver" | tail -n1)"
rm -f "$desktop_resolver"
# PKGBUILD transforms hyphen to underscore for Arch pkgver
pkgver_v="$(cd packaging/packages/samios-branding && set -e && source PKGBUILD >/dev/null 2>&1 && echo "${pkgver:-}")"
# Restore clone to its original VERSION (captured above) rather
# than hardcoding a value — safe at any current version.
echo "$orig_version" > VERSION
make version-sync >/dev/null 2>&1
# Verify all seven agree (or transform as expected)
[ "$cli_v" = "0.2.0-rc1" ] || { echo "CLI=$cli_v"; exit 1; }
[ "$voice_v" = "0.2.0-rc1" ] || { echo "voice=$voice_v"; exit 1; }
[ "$mirror_v" = "0.2.0-rc1" ] || { echo "mirror=$mirror_v"; exit 1; }
[ "$installer_v" = "0.2.0-rc1" ] || { echo "installer=$installer_v"; exit 1; }
[ "$profiledef_v" = "0.2.0-rc1" ] || { echo "profiledef=$profiledef_v"; exit 1; }
[ "$desktop_v" = "0.2.0-rc1" ] || { echo "desktop=$desktop_v"; exit 1; }
[ "$pkgver_v" = "0.2.0_rc1" ] || { echo "pkgver=$pkgver_v expected=0.2.0_rc1"; exit 1; }
'
# ── 14. PKGBUILD transforms "0.X.Y-suffix" to "0.X.Y_suffix" for Arch ───
# This duplicates test 13's PKGBUILD check but in isolation, so a failure
# here points directly at PKGBUILD (not at the broader end-to-end test).
assert "PKGBUILD transforms prerelease hyphen to underscore for Arch pkgver" \
bash -c '
clone="$(mktemp -d)"
trap "rm -rf ${clone:?}" EXIT
cp -a "$REPO_ROOT" "$clone/repo"
cd "$clone/repo/packaging/packages/samios-branding" || exit 1
echo "0.2.0-rc1" > ../../../VERSION
# Source PKGBUILD and echo pkgver — no nested bash -c quoting needed.
pkgver_actual="$(set -e; source PKGBUILD >/dev/null 2>&1 && echo "$pkgver")"
cd /
rm -rf "$clone"
[ "$pkgver_actual" = "0.2.0_rc1" ] || { echo "pkgver=$pkgver_actual expected=0.2.0_rc1"; exit 1; }
'
print_summary