diff --git a/.github/scripts/test-raspberrypi-detection.sh b/.github/scripts/test-raspberrypi-detection.sh new file mode 100755 index 0000000..424d47d --- /dev/null +++ b/.github/scripts/test-raspberrypi-detection.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +tasks_file="$repo_root/roles/raspberrypi/tasks/main.yml" + +if [ ! -f "$tasks_file" ]; then + printf 'cannot find %s\n' "$tasks_file" >&2 + exit 1 +fi + +# The role decides whether a host is a Raspberry Pi from the return code of two +# ansible.builtin.command probes. The command module never executes under +# --check: it returns rc 0 with "Command would have run if not in check mode". +# Without check_mode: false on both probes every host looks like a Raspberry Pi +# in a check run, so distro specific tasks (e.g. the Ubuntu cgroup +# cmdline.txt edit) run against hosts that must not be touched. +probe_count="$(grep -c -- 'check_mode: false' "$tasks_file" || true)" +if [ "$probe_count" -lt 2 ]; then + printf 'raspberrypi detection probes must set check_mode: false (found %s, expected 2)\n' \ + "$probe_count" >&2 + exit 1 +fi + +# Both probes must be the detection greps, not some other task. +for probe in \ + 'grep -E "Raspberry Pi|BCM2708|BCM2709|BCM2835|BCM2836" /proc/cpuinfo' \ + 'grep -E "Raspberry Pi" /proc/device-tree/model'; do + grep -Fq -- "$probe" "$tasks_file" || { + printf 'missing raspberry pi detection probe: %s\n' "$probe" >&2 + exit 1 + } +done + +# The probes must stay registered under the names the gate consumes. +for var in grep_cpuinfo_raspberrypi grep_device_tree_model_raspberrypi; do + grep -Fq -- "register: $var" "$tasks_file" || { + printf 'detection probe is not registered as %s\n' "$var" >&2 + exit 1 + } +done + +# The gate must keep keying on rc 0 and must tolerate a missing rc so an +# undefined result cannot be read as "this host is a Raspberry Pi". +grep -Eq -- 'when:.*grep_cpuinfo_raspberrypi\.rc.*== 0.*grep_device_tree_model_raspberrypi\.rc.*== 0' \ + "$tasks_file" || { + printf 'raspberry_pi gate no longer tests both probe return codes for 0\n' >&2 + exit 1 +} + +grep -Eq -- 'grep_cpuinfo_raspberrypi\.rc \| default\(1\)' "$tasks_file" || { + printf 'raspberry_pi gate does not default the /proc/cpuinfo probe rc\n' >&2 + exit 1 +} + +grep -Eq -- 'grep_device_tree_model_raspberrypi\.rc \| default\(1\)' "$tasks_file" || { + printf 'raspberry_pi gate does not default the /proc/device-tree probe rc\n' >&2 + exit 1 +} + +# The distro specific include must remain gated on the detection result so a +# non Raspberry Pi host skips setup/ and teardown/ entirely. +grep -Eq -- 'raspberry_pi\|default\(false\)' "$tasks_file" || { + printf 'OS specific tasks are no longer gated on raspberry_pi\n' >&2 + exit 1 +} + +printf 'Raspberry Pi detection regression test passed\n' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c50ee62..3d224a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -76,6 +76,12 @@ repos: language: system pass_filenames: false files: ^roles/prereq/(tasks/main|defaults/main)\.yml$|^\.github/scripts/test-disable-swap\.sh$ + - id: raspberrypi-detection-test + name: Raspberry Pi detection test + entry: .github/scripts/test-raspberrypi-detection.sh + language: system + pass_filenames: false + files: ^roles/raspberrypi/(tasks/main|defaults/main)\.yml$|^\.github/scripts/test-raspberrypi-detection\.sh$ - id: cilium-bgp-manifest-test name: Cilium BGP manifest test entry: python3 .github/scripts/test-cilium-bgp-manifest.py diff --git a/roles/raspberrypi/tasks/main.yml b/roles/raspberrypi/tasks/main.yml index 2319427..670b214 100644 --- a/roles/raspberrypi/tasks/main.yml +++ b/roles/raspberrypi/tasks/main.yml @@ -1,20 +1,27 @@ --- +# The probes below must run in check mode as well. The command module does not +# execute under --check, it returns rc 0 with "Command would have run if not in +# check mode", which would otherwise mark every host as a Raspberry Pi and make +# check runs apply the distro specific tasks. The rc values mean: 0 matched, 1 no +# match, 2 the file is absent (no /proc/device-tree on non ARM systems). - name: Test for raspberry pi /proc/cpuinfo ansible.builtin.command: grep -E "Raspberry Pi|BCM2708|BCM2709|BCM2835|BCM2836" /proc/cpuinfo register: grep_cpuinfo_raspberrypi failed_when: false changed_when: false + check_mode: false - name: Test for raspberry pi /proc/device-tree/model ansible.builtin.command: grep -E "Raspberry Pi" /proc/device-tree/model register: grep_device_tree_model_raspberrypi failed_when: false changed_when: false + check_mode: false - name: Set raspberry_pi fact to true ansible.builtin.set_fact: raspberry_pi: true - when: grep_cpuinfo_raspberrypi.rc == 0 or grep_device_tree_model_raspberrypi.rc == 0 + when: (grep_cpuinfo_raspberrypi.rc | default(1)) == 0 or (grep_device_tree_model_raspberrypi.rc | default(1)) == 0 - name: Set detected_distribution to Raspbian (ARM64 on Raspbian, Debian Buster/Bullseye/Bookworm) ansible.builtin.set_fact: