Wren Linux

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 ]