name: Shell tests on: push: branches: [main] pull_request: branches: [main] jobs: shell-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install bats run: sudo apt-get update && sudo apt-get install -y bats - name: Run bats suites run: bats linux_configuration/tests/*.bats # Only the side-effect-free tests run here. The rest of # linux_configuration/tests/ drives pacman, /etc, systemd or adb and # cannot run on an Ubuntu runner without root and an Arch container -- # see the accompanying evidence artifacts for the full list. - name: Run side-effect-free shell tests run: | set -euo pipefail tests=( test_i3blocks_persist_common.sh test_setup_dns_blocker.sh ) failed=0 for t in "${tests[@]}"; do printf '\n=== %s ===\n' "$t" if bash "linux_configuration/tests/$t"; then printf 'PASS %s\n' "$t" else printf 'FAIL %s\n' "$t" failed=$((failed + 1)) fi done if [ "$failed" -gt 0 ]; then printf '\n%d shell test(s) failed\n' "$failed" exit 1 fi # The lib/tests/ suites that came out of the 250-line-cap splits. These # are DISCOVERED rather than listed: every split adds another # lib/tests/run_all.sh, and a hardcoded list silently leaves each new # suite running only on the author's machine. Four such runners already # existed and none of them ran in CI until this step was added. # # Each runner is invoked directly, not as `bash `: the same # invocation the coverage wrapper needs, and it keeps the shebang # authoritative. - name: Run lib/tests suites run: | set -euo pipefail mapfile -t runners < <(find . -path ./node_modules -prune -o \ -path '*/lib/tests/run_all.sh' -print | sort) if [ "${#runners[@]}" -eq 0 ]; then printf 'No lib/tests/run_all.sh found — the discovery glob is wrong\n' exit 1 fi failed=0 for r in "${runners[@]}"; do printf '\n=== %s ===\n' "$r" # A suite that writes to real system paths ships a `jail_args` # file and REFUSES to run bare, because running it bare would # rewrite the runner's own /etc. It needs shell_coverage_jail.sh, # which needs kcov -- and kcov is NOT packaged for Ubuntu 24.04 # ("E: Unable to locate package kcov", measured) and upstream # ships no prebuilt binary, so it would have to be built from # source on every run. # # These suites are therefore SKIPPED here and run for real by the # ci-mirror pre-push hook on the developer machine, which has # kcov. That is the same trade the arch-tests job below makes: # declare what this runner cannot do rather than pretend to check # it. A skip is announced, never silent. # # Empty marker = configuration error, still a hard failure. args_file="${r%/run_all.sh}/jail_args" if [ -f "$args_file" ]; then if ! grep -qv '^#\|^$' "$args_file"; then printf 'FAIL %s (jail_args present but empty)\n' "$r" failed=$((failed + 1)) continue fi printf 'SKIP %s (needs the kcov jail; enforced by pre-push)\n' "$r" continue fi if "$r"; then printf 'PASS %s\n' "$r" else printf 'FAIL %s\n' "$r" failed=$((failed + 1)) fi done if [ "$failed" -gt 0 ]; then printf '\n%d lib/tests suite(s) failed\n' "$failed" exit 1 fi # Tests that assume Arch: they exercise the pacman wrapper, makepkg wrapper # and hosts-guard hooks, so they need an Arch userland rather than Ubuntu. # They run as root here, which is what lets them touch /etc safely — the # container is disposable. arch-tests: runs-on: ubuntu-latest container: image: archlinux:base-devel # test_i3blocks_efficiency.sh counts execve calls with strace, which needs # ptrace. Without these the trace file is never produced. options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined steps: # base-devel ships none of these: git (actions/checkout needs it), strace # (execve counting), or jq (the claude_usage block parses its cache with # it, and without jq that block degrades to "no data" and its assertions # fail). - name: Install dependencies run: pacman -Sy --noconfirm --needed git strace jq - uses: actions/checkout@v4 # The complete Arch-dependent set. Five of the original eight moved to # github.com/kuhyx/digital-wellbeing with the code they test; they run # bare on Ubuntu there. The two that remain were verified locally in # `docker run --cap-add=SYS_PTRACE archlinux:base-devel`. # # test_security_hardening.sh inspects a configured machine (chattr flags, # systemd guard units, /usr/local payloads). It detects that it is not on # one and reports those checks as skipped, while still running its # repository checks — so it is meaningful here AND on the real host, # where it continues to fail loudly if a guard unit is down. - name: Run Arch-specific shell tests run: | set -euo pipefail tests=( test_i3blocks_efficiency.sh test_security_hardening.sh ) failed=0 for t in "${tests[@]}"; do printf '\n=== %s ===\n' "$t" if bash "linux_configuration/tests/$t"; then printf 'PASS %s\n' "$t" else printf 'FAIL %s\n' "$t" failed=$((failed + 1)) fi done if [ "$failed" -gt 0 ]; then printf '\n%d shell test(s) failed\n' "$failed" exit 1 fi