Switches the operator path from bash-only to a Python console, keeps the bash
menu as a fallback, and makes the offline package set travel with the repository
so the person installing a device never downloads anything.
bfos/ (stdlib only, no textual/rich: those would need a download, which defeats
the offline goal):
- simple mode offers three choices (Kur / Durum / Çıkış); the detailed mode with
all 21 fleet operations and the local installer is reachable from the menu or
with --full, so a first-time operator is not buried in options
- gate parity with the bash console: scope read from the playbook file and
refused on mismatch, production needs a typed token plus a separate approval,
release-gated runs need their wave name typed, destructive runs need a second
approval, "all" walks wave by wave and stops at the first failure
- Turkish throughout; audit log at /var/log/blueforce-console.log (600, falls
back to the user state dir) with a single redaction choke point
- bfos/ui_gui.py adds an optional tkinter window over the same operations layer;
headless and missing-tkinter cases explain themselves in Turkish
admin/bf-bootstrap.sh: links the Python console as `bf` (primary) and keeps
`bf-menu` (fallback), reports both, and recommends `sudo bf`.
Offline packages are committable now (this was the blocking contradiction):
- .gitignore ignored `Packages`, `Packages.gz` and `packages.lock.tsv` everywhere,
so a built bundle could never be committed and the installer would always have
been told to download. provisioning/offline-repo/built/ is now allowed through
while scratch output stays untracked
- provisioning/offline-repo/{add-packages.sh,build-index.sh,repo-manifest.yaml}
plus a Turkish step-by-step README for the repository owner
- the over-broad `*token*` ignore rule is narrowed to `*[-_.]token*` so ordinary
filenames are not silently excluded while token files still are
Tests: run-all.sh now runs the Python suites too (previously it collected only
*.sh, so 212 Python assertions were silently skipped) and executes from the repo
root. 11/11 pass, exit 0.
Two test expectations were corrected rather than the code: a read-only
wave-scoped report legitimately asks for no gate token (the bash console never
did either), so that assertion now checks the command carries operation_wave and
operation_confirmed, and a new test asserts release-gated runs still require
their token. The bootstrap completion contract moved from `sudo bf-menu` to
`sudo bf`.
92 lines
2.6 KiB
Bash
Executable file
92 lines
2.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Single entry point for the repository test suite.
|
|
#
|
|
# Runs every tests/*.sh (except this file) in sorted order, reports each result, and
|
|
# exits with the sum of the individual exit codes so that a single non-zero test can
|
|
# never be mistaken for a clean run.
|
|
#
|
|
# Usage:
|
|
# bash tests/run-all.sh # run everything
|
|
# bash tests/run-all.sh offline firstboot # run only tests whose name contains a term
|
|
#
|
|
# -e is deliberately not set: a failing test must not abort the rest of the run, it has
|
|
# to be reported and counted.
|
|
set -uo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
SELF="$(basename "${BASH_SOURCE[0]}")"
|
|
|
|
filters=("$@")
|
|
tests=()
|
|
while IFS= read -r script; do
|
|
tests+=("$script")
|
|
done < <(find "$SCRIPT_DIR" -maxdepth 1 -type f \( -name '*.sh' -o -name 'test_*.py' -o -name 'check_*.py' \) ! -name "$SELF" | sort)
|
|
|
|
if [[ ${#tests[@]} -eq 0 ]]; then
|
|
printf 'run-all: no tests found in %s\n' "$SCRIPT_DIR" >&2
|
|
exit 1
|
|
fi
|
|
|
|
selected=()
|
|
for script in "${tests[@]}"; do
|
|
if [[ ${#filters[@]} -eq 0 ]]; then
|
|
selected+=("$script")
|
|
continue
|
|
fi
|
|
for filter in "${filters[@]}"; do
|
|
if [[ "$(basename "$script")" == *"$filter"* ]]; then
|
|
selected+=("$script")
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [[ ${#selected[@]} -eq 0 ]]; then
|
|
printf 'run-all: no test matched: %s\n' "${filters[*]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf 'run-all: %d test file(s) from %s\n' "${#selected[@]}" "$REPO_ROOT"
|
|
exit_code_sum=0
|
|
failures=()
|
|
|
|
for script in "${selected[@]}"; do
|
|
name="$(basename "$script")"
|
|
printf '\n=== %s ===\n' "$name"
|
|
case "$script" in
|
|
*.py)
|
|
# Python suites run from the repository root so `import bfos` and
|
|
# `tests.test_*` module paths resolve.
|
|
( cd "$REPO_ROOT" && python3 "$script" )
|
|
;;
|
|
*)
|
|
( cd "$REPO_ROOT" && bash "$script" )
|
|
;;
|
|
esac
|
|
status=$?
|
|
if [[ "$status" -eq 0 ]]; then
|
|
printf '%s\n' "--- $name: PASS"
|
|
else
|
|
printf '%s\n' "--- $name: FAIL (exit $status)"
|
|
failures+=("$name")
|
|
fi
|
|
exit_code_sum=$((exit_code_sum + status))
|
|
done
|
|
|
|
printf '\n=== run-all summary ===\n'
|
|
printf 'tests run : %d\n' "${#selected[@]}"
|
|
# Build the failing-name suffix explicitly: inlining this command substitution in
|
|
# the printf argument list makes the empty-array case drop an argument and printf
|
|
# then reports "usage: printf ...".
|
|
failure_suffix=""
|
|
if [[ ${#failures[@]} -gt 0 ]]; then
|
|
failure_suffix=" (${failures[*]})"
|
|
fi
|
|
printf 'failed : %d%s\n' "${#failures[@]}" "$failure_suffix"
|
|
printf 'exit code : %d (sum of individual exit codes)\n' "$exit_code_sum"
|
|
|
|
if [[ "$exit_code_sum" -gt 255 ]]; then
|
|
exit 255
|
|
fi
|
|
exit "$exit_code_sum"
|