#!/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"