Files
ekklesiaandClaude Opus 5.5 5416112db5 build: one list of checks, run by the release build and by CI alike
tools/check.sh is what build-release.sh ran before building — every
script's syntax, the manual, every tests/test-*.sh — moved, not copied,
so a new test is run by both with nothing else to edit. It now runs every
test even after one fails, keeps each one's output and a summary table
for a CI run, and with --base checks the change's hygiene too.

The manual's revision date honours SOURCE_DATE_EPOCH, so a release built
from a commit a day later ships the manual that commit holds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 18:47:42 -05:00

142 lines
4.4 KiB
Bash
Executable File

#!/bin/bash
# EVERY CHECK THE REPOSITORY HAS THAT NEEDS NO CONTAINER AND NO SCREEN.
#
# tools/check.sh sources, the manual, every test
# tools/check.sh --base REF and the change since REF is clean
# CHECK_LOG_DIR=DIR tools/check.sh each test's output in DIR, and a
# summary.md for a CI run to show
#
# The one list. tools/build-release.sh runs it before it builds anything,
# and CI runs it on every push and pull request, so a test added to tests/
# is a test both of them run, with nothing else to edit.
#
# Every test runs even after one has failed, so a report names all of
# them; the exit status is 1 if any did.
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
base=""
while [[ $# -gt 0 ]]; do
case "$1" in
--base) base="$2"; shift 2 ;;
*) echo "Usage: $0 [--base REF]" >&2; exit 2 ;;
esac
done
logs="${CHECK_LOG_DIR:-$(mktemp -d)}"
mkdir -p "$logs"
summary="$logs/summary.md"
failed=0
{
echo "| Check | Result | Seconds |"
echo "| --- | --- | --- |"
} > "$summary"
# check NAME COMMAND...: run it, keep its output, say how it went.
check() {
local name="$1" started status log
shift
started=$SECONDS
log="$logs/$name.log"
"$@" > "$log" 2>&1
status=$?
if [[ $status -eq 0 ]]; then
printf ' ok %s\n' "$name"
echo "| $name | pass | $(( SECONDS - started )) |" >> "$summary"
else
printf ' FAIL %s\n' "$name"
tail -n 25 "$log" | sed 's/^/ /'
echo "| $name | **FAIL** | $(( SECONDS - started )) |" >> "$summary"
failed=1
fi
}
# Syntax check everything the package ships, by shebang, so this cannot
# drift out of date as classroom commands are added.
validate_sources() {
local script module policy
for script in "$ROOT/packaging/debian/postinst" "$ROOT"/scripts/* "$ROOT"/tests/* \
"$ROOT"/tools/* "$ROOT"/tools/lab/* "$ROOT"/update_*.sh; do
[[ -f "$script" ]] || continue
case "$(head -n 1 "$script")" in
*python3*)
python3 -m py_compile "$script" || return 1
;;
*bash*|*bin/sh*)
bash -n "$script" || return 1
;;
*)
# Sourced, so no #! line: tools/release-lib.sh.
if [[ "$script" == *.sh ]]; then
bash -n "$script" || return 1
fi
;;
esac
done
for module in "$ROOT"/lib/*.py; do
[[ -f "$module" ]] || continue
python3 -m py_compile "$module" || return 1
done
for policy in "$ROOT"/config/chrome/policies/*/*.json "$ROOT"/config/wallpapers/*/metadata.json; do
[[ -f "$policy" ]] || continue
python3 -c 'import json, sys; json.load(open(sys.argv[1]))' "$policy" || return 1
done
find "$ROOT/scripts" "$ROOT/lib" "$ROOT/tests" "$ROOT/tools" \
-name '__pycache__' -type d -prune -exec rm -rf {} + 2>/dev/null || true
}
# The change itself: no whitespace damage, and nothing built or published
# committed by mistake. Only with --base, because it is a question about a
# change, not about a tree.
hygiene() {
git diff --check "$base" HEAD || return 1
if git ls-files | grep -E '\.deb$|^(dist|repo|packaging/root)/'; then
echo "built or published files are committed (above)" >&2
return 1
fi
# A committed release version is one a release could be built from.
source "$ROOT/tools/release-lib.sh"
release_require_version "$(cat "$ROOT/VERSION")"
}
echo "Checking $(git rev-parse --short HEAD 2>/dev/null || echo 'the tree')"
check sources validate_sources
[[ -z "$base" ]] || check hygiene hygiene
# THE MANUAL IS PART OF THE RELEASE, NOT A JOB FOR AFTERWARDS.
#
# Built from docs/manual before the tests run, so that the test which
# checks the digital and printed versions against each other is checking
# what this package is about to ship rather than whatever was built last.
# A page that would make the printed manual wrong fails here.
check manual "$ROOT/tools/build-manual"
for test in "$ROOT"/tests/test-*.sh; do
[[ -x "$test" ]] || continue
check "$(basename "$test" .sh)" "$test"
done
echo
if [[ $failed -eq 0 ]]; then
echo "Every check passed. Logs: $logs"
else
echo "Checks FAILED. Each one's full output is in $logs"
fi
exit "$failed"