wren-linux / tools / test-regular-extras.sh
#!/bin/bash
# Checks what the retired Claude Code extras (claude-regular, claude-code) still need to take
# themselves out:
# - lib/claude-settings.py adds Wren Linux's Claude Code settings next to yours
# and takes out only what it added;
# - lib/claude-repo.sh keeps Anthropic's repository while either extra is still set up.
# Nothing on this computer changes. Run by test.sh, or:
# bash tools/test-regular-extras.sh
# shellcheck disable=SC2016 # the pretend programs expand their own variables
HERE=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
T=$(mktemp -d) || { echo " FAIL couldn't make a temp folder (is /tmp full?)"; exit 1; }
trap 'rm -rf "$T"' EXIT
fails=0
check() { # DESCRIPTION CONDITION-COMMAND...
local what=$1
shift
if "$@" >/dev/null 2>&1; then printf ' ok %s\n' "$what"; else printf ' FAIL %s\n' "$what"; fails=$((fails + 1)); fi
}
not() { ! "$@"; }
jq_has() { python3 -I -c 'import json,sys; d=json.load(open(sys.argv[1])); sys.exit(0 if eval(sys.argv[2]) else 1)' "$@"; }
# ---- lib/claude-settings.py ----
M=$HERE/lib/claude-settings.py
# (a small payload of the same shape as the one the retired extra added; the merge is what is checked)
OURS=$T/ours.json
mkdir -p "$T/s"
printf '%s\n' '{"sandbox": {"enabled": true, "filesystem": {"denyRead": ["~/.ssh", "~/.gnupg"]}},' \
' "permissions": {"deny": ["Read(~/.ssh/**)", "Read(~/.gnupg/**)", "Bash(sudo *)"]}}' >"$OURS"
S=$T/s/settings.json ST=$T/s/state.json
python3 -I "$M" add "$S" "$OURS" "$ST"
check "settings: into an empty home, the sandbox is on" jq_has "$S" 'd["sandbox"]["enabled"] is True'
check "...with the deny rules" jq_has "$S" '"Read(~/.ssh/**)" in d["permissions"]["deny"] and "Bash(sudo *)" in d["permissions"]["deny"]'
check "...and only you may read the file" test "$(stat -c %a "$S")" = 600
check "...every folder and file closed to its commands is closed to its file tools too" python3 -I -c '
import json, sys
d = json.load(open(sys.argv[1]))
deny = d["permissions"]["deny"]
for p in d["sandbox"]["filesystem"]["denyRead"]:
assert "Read(%s/**)" % p in deny or "Read(%s)" % p in deny, p
' "$OURS"
python3 -I "$M" remove "$S" "$OURS" "$ST"
check "...and remove leaves an empty settings file" jq_has "$S" 'd == {}'
printf '%s\n' '{"model": "opus", "sandbox": {"enabled": false}, "permissions": {"deny": ["Read(~/secret)", "Read(~/.ssh/**)"], "allow": ["Bash(make *)"]}}' >"$S"
python3 -I "$M" add "$S" "$OURS" "$ST"
python3 -I "$M" add "$S" "$OURS" "$ST"
check "settings: your own values stay (your sandbox off stays off)" jq_has "$S" 'd["sandbox"]["enabled"] is False and d["model"] == "opus"'
check "...your rules stay, and Wren Linux's are added once, even run twice" \
jq_has "$S" 'd["permissions"]["deny"].count("Read(~/.gnupg/**)") == 1 and "Read(~/secret)" in d["permissions"]["deny"] and d["permissions"]["allow"] == ["Bash(make *)"]'
python3 -I "$M" remove "$S" "$OURS" "$ST"
check "...remove takes out only Wren Linux's" \
jq_has "$S" 'd == {"model": "opus", "sandbox": {"enabled": False}, "permissions": {"deny": ["Read(~/secret)", "Read(~/.ssh/**)"], "allow": ["Bash(make *)"]}}'
check "...a rule you had before stays, though Wren Linux has it too" jq_has "$S" '"Read(~/.ssh/**)" in d["permissions"]["deny"]'
echo '[1, 2]' >"$S"
check "settings that aren't a JSON object are left alone" not python3 -I "$M" add "$S" "$OURS" "$ST"
check "...unchanged" test "$(cat "$S")" = '[1, 2]'
# ---- lib/claude-repo.sh: the repository stays while either extra uses it ----
repo() { # MARKS...: which marks exist; prints what was run as root
(
. "$HERE/lib.sh" >/dev/null 2>&1
. "$HERE/lib/claude-repo.sh"
set +e
SUDO='echo'
CLAUDE_SANDBOX_MARK=$T/m/sandbox CLAUDE_REGULAR_MARK=$T/m/regular
rm -rf "$T/m" && mkdir -p "$T/m"
for m in "$@"; do : >"$T/m/$m"; done
note() { :; }
remove_claude_repo_if_unused
)
}
check "repository: kept while the sandboxed claude is set up" test -z "$(repo sandbox)"
check "...kept while claude-regular is" test -z "$(repo regular)"
check "...removed when neither is" grep -q "rm -f /etc/apt/sources.list.d/claude-code.list /etc/apt/keyrings/claude-code.asc" <<<"$(repo)"
check "claude-code.sh --remove keeps it for claude-regular" grep -q '^ remove_claude_repo_if_unused$' "$HERE/extras/claude-code.sh"
check "...and no longer deletes it itself" not grep -q 'rm -f "\$LIST" "\$KEY"' "$HERE/extras/claude-code.sh"
[ "$fails" -eq 0 ]