#!/bin/bash # Does an open-ended list of app names survive the trip from the command line # into a CLI handler? Runs in $TMPDIR against stubs built from the REAL lines in # init.sh and start.sh, so editing either is what makes this fail. # # scripts/dev/lp-cli-argv-test # # The path has two independent chokepoints, and fixing one hid the other: # # 1. the root wrapper (baked from init.sh) invokes start.sh with exactly nine # hardcoded positional slots, unset ones filled with the literal "empty" # 2. the CLI dispatcher calls its handlers with NO arguments, so "$@" and # `shift` inside a handler operate on an empty list # # LP_CLI_ARGS was added for (2) but built from start.sh's "$@" — which (1) had # already capped at nine. `restore first-run bulk` reads "${LP_CLI_ARGS[@]:5}", # so a 13-app restore arrived as four, restored four, and reported success. # # Thirteen is the fixture size for a reason: it is what a real host had. REPO="$(cd "$(dirname "$0")/../.." && pwd)" BASE="$(mktemp -d "${TMPDIR:-/tmp}/lp-cli-argv-test-XXXXXX")" trap 'rm -rf "$BASE"' EXIT fail=0 chk(){ if [[ "$2" == "$3" ]]; then echo " ok $1"; else echo " FAIL $1: got '$2' want '$3'"; fail=1; fi; } # --- the two real lines under test ------------------------------------------- invoke=$(grep -E '^\s*\./start\.sh "\$command1"' "$REPO/init.sh" | head -1) capture=$(grep -E '^declare -a LP_CLI_ARGS=' "$REPO/start.sh" | head -1) [[ -n "$invoke" ]] || { echo " FAIL could not find the start.sh invocation in init.sh"; exit 1; } [[ -n "$capture" ]] || { echo " FAIL could not find LP_CLI_ARGS in start.sh"; exit 1; } # --- stub start.sh: the real capture line, then report what a handler sees ---- { echo '#!/bin/bash' echo "$capture" # Offsets as the real handlers use them: :5 for `restore first-run bulk` # (restore first-run bulk app…), :4 for `restore preflight`. echo 'bulk=(); for a in "${LP_CLI_ARGS[@]:5}"; do [[ -z "$a" || "$a" == "empty" ]] && continue; bulk+=("$a"); done' echo 'pref=(); for a in "${LP_CLI_ARGS[@]:4}"; do [[ -z "$a" || "$a" == "empty" ]] && continue; pref+=("$a"); done' echo 'echo "BULK=${#bulk[@]}:${bulk[*]}"' echo 'echo "PREF=${#pref[@]}"' echo 'echo "SLOT1=$1 SLOT5=$5 SLOT9=$9"' } > "$BASE/start.sh" chmod +x "$BASE/start.sh" # --- stub wrapper: the nine slots exactly as the real one builds them --------- { echo '#!/bin/bash' for i in $(seq 1 9); do echo "command$i=\"\${$i:-empty}\""; done echo 'script_dir="$(dirname "$0")"' echo 'cd "$script_dir"' echo "$invoke" } > "$BASE/wrapper" chmod +x "$BASE/wrapper" APPS=(bookstack grafana matrix mattermost navidrome nextcloud prometheus rocketchat speedtest stalwart stoat trivy vikunja) echo "--- 13 app names through 'restore first-run bulk' ---" out=$("$BASE/wrapper" restore first-run bulk 2 QuantumOtter "${APPS[@]}") bulk=$(sed -n 's/^BULK=\([0-9]*\):.*/\1/p' <<< "$out") names=$(sed -n 's/^BULK=[0-9]*://p' <<< "$out") chk "count" "$bulk" "13" chk "first app" "${names%% *}" "bookstack" chk "last app" "${names##* }" "vikunja" echo "--- the nine legacy slots still behave as before ---" # argv here is: restore(1) first-run(2) bulk(3) 2(4) QuantumOtter(5) # bookstack(6) grafana(7) matrix(8) mattermost(9) — so slot 9 is the fourth app, # which is exactly the cap that made a 13-app restore into a 4-app one. slots=$(sed -n 's/^SLOT1=//p' <<< "$out") chk "slots" "$slots" "restore SLOT5=QuantumOtter SLOT9=mattermost" echo "--- unset slots still arrive as the literal \"empty\", and are dropped ---" out=$("$BASE/wrapper" restore preflight 2 QuantumOtter) chk "no phantom apps" "$(sed -n 's/^PREF=//p' <<< "$out")" "0" echo "--- an explicit six, which used to be capped at five ---" out=$("$BASE/wrapper" restore preflight 2 QuantumOtter "${APPS[@]:0:6}") chk "count" "$(sed -n 's/^PREF=//p' <<< "$out")" "6" echo "" if (( fail )); then echo "FAILED"; exit 1; fi echo "All argv checks passed."