From b18fc76868413134035c4c3ae62e75fde5269aae Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Thu, 9 Apr 2026 12:40:50 -0400 Subject: [PATCH 1/2] feat: CLI safety/test harness for scripts/ suite (#438) --- scripts/test_harness.sh | 195 ++++++++++++++++++++++++++++++++++++++++ scripts/test_runner.sh | 9 ++ 2 files changed, 204 insertions(+) create mode 100755 scripts/test_harness.sh create mode 100755 scripts/test_runner.sh diff --git a/scripts/test_harness.sh b/scripts/test_harness.sh new file mode 100755 index 00000000..231f68e4 --- /dev/null +++ b/scripts/test_harness.sh @@ -0,0 +1,195 @@ +#!/usr/bin/env bash +# test_harness.sh — Common CLI safety/test harness for the scripts/ suite +# Usage: ./scripts/test_harness.sh [--verbose] [--ci] [directory] +# +# Discovers .sh, .py, and .yaml files in the target directory and validates them: +# - .sh : runs shellcheck (or SKIPS if unavailable) +# - .py : runs python3 -m py_compile +# - .yaml: validates with python3 yaml.safe_load +# +# Exit codes: 0 = all pass, 1 = any fail + +set -euo pipefail + +# --- Defaults --- +VERBOSE=0 +CI_MODE=0 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TARGET_DIR="${SCRIPT_DIR}" + +# --- Colors (disabled in CI) --- +RED="" +GREEN="" +YELLOW="" +CYAN="" +RESET="" +if [[ -t 1 && "${CI:-}" != "true" ]]; then + RED=$'\033[0;31m' + GREEN=$'\033[0;32m' + YELLOW=$'\033[0;33m' + CYAN=$'\033[0;36m' + RESET=$'\033[0m' +fi + +# --- Argument parsing --- +while [[ $# -gt 0 ]]; do + case "$1" in + --verbose|-v) VERBOSE=1; shift ;; + --ci) CI_MODE=1; shift ;; + -*) echo "Unknown option: $1" >&2; exit 2 ;; + *) TARGET_DIR="$1"; shift ;; + esac +done + +# --- Counters --- +PASS=0 +FAIL=0 +SKIP=0 +TOTAL=0 + +# --- Helpers --- +log_verbose() { + if [[ "${VERBOSE}" -eq 1 ]]; then + echo " ${CYAN}[DEBUG]${RESET} $*" + fi +} + +record_pass() { + ((PASS++)) + ((TOTAL++)) + echo "${GREEN}PASS${RESET} $1" +} + +record_fail() { + ((FAIL++)) + ((TOTAL++)) + echo "${RED}FAIL${RESET} $1" + if [[ -n "${2:-}" ]]; then + echo " ${2}" + fi +} + +record_skip() { + ((SKIP++)) + ((TOTAL++)) + echo "${YELLOW}SKIP${RESET} $1 — $2" +} + +# --- Checkers --- +check_shell_file() { + local file="$1" + local rel="${file#${TARGET_DIR}/}" + if command -v shellcheck &>/dev/null; then + log_verbose "Running shellcheck on ${rel}" + local output + if output=$(shellcheck -x -S warning "${file}" 2>&1); then + record_pass "${rel}" + else + record_fail "${rel}" "${output}" + fi + else + record_skip "${rel}" "shellcheck not installed" + fi +} + +check_python_file() { + local file="$1" + local rel="${file#${TARGET_DIR}/}" + log_verbose "Running py_compile on ${rel}" + local output + if output=$(python3 -m py_compile "${file}" 2>&1); then + record_pass "${rel}" + else + record_fail "${rel}" "${output}" + fi +} + +check_yaml_file() { + local file="$1" + local rel="${file#${TARGET_DIR}/}" + log_verbose "Validating YAML: ${rel}" + local output + if output=$(python3 -c "import yaml; yaml.safe_load(open('${file}'))" 2>&1); then + record_pass "${rel}" + else + record_fail "${rel}" "${output}" + fi +} + +# --- Main --- +echo "" +echo "=== scripts/ test harness ===" +echo "Target: ${TARGET_DIR}" +echo "" + +if [[ ! -d "${TARGET_DIR}" ]]; then + echo "Error: target directory '${TARGET_DIR}' not found" >&2 + exit 1 +fi + +# Check python3 availability +if ! command -v python3 &>/dev/null; then + echo "${RED}Error: python3 is required but not found${RESET}" >&2 + exit 1 +fi + +# Check PyYAML availability +if ! python3 -c "import yaml" 2>/dev/null; then + echo "${YELLOW}Warning: PyYAML not installed — YAML checks will be skipped${RESET}" >&2 + YAML_AVAILABLE=0 +else + YAML_AVAILABLE=1 +fi + +# Discover and check .sh files +sh_files=() +while IFS= read -r -d '' f; do + sh_files+=("$f") +done < <(find "${TARGET_DIR}" -maxdepth 1 -name "*.sh" ! -name "test_harness.sh" ! -name "test_runner.sh" -print0 | sort -z) + +for f in "${sh_files[@]:-}"; do + [[ -n "$f" ]] && check_shell_file "$f" +done + +# Discover and check .py files +py_files=() +while IFS= read -r -d '' f; do + py_files+=("$f") +done < <(find "${TARGET_DIR}" -maxdepth 1 -name "*.py" -print0 | sort -z) + +for f in "${py_files[@]:-}"; do + [[ -n "$f" ]] && check_python_file "$f" +done + +# Discover and check .yaml files in target dir +yaml_files=() +while IFS= read -r -d '' f; do + yaml_files+=("$f") +done < <(find "${TARGET_DIR}" -maxdepth 1 -name "*.yaml" -print0 | sort -z) + +if [[ "${YAML_AVAILABLE}" -eq 1 ]]; then + for f in "${yaml_files[@]:-}"; do + [[ -n "$f" ]] && check_yaml_file "$f" + done +else + for f in "${yaml_files[@]:-}"; do + [[ -n "$f" ]] && record_skip "${f#${TARGET_DIR}/}" "PyYAML not installed" + done +fi + +# --- Summary --- +echo "" +echo "=== Results ===" +echo " ${GREEN}PASS${RESET}: ${PASS}" +echo " ${RED}FAIL${RESET}: ${FAIL}" +echo " ${YELLOW}SKIP${RESET}: ${SKIP}" +echo " Total: ${TOTAL}" +echo "" + +if [[ "${FAIL}" -gt 0 ]]; then + echo "${RED}FAILED${RESET} — ${FAIL} file(s) did not pass validation." + exit 1 +else + echo "${GREEN}ALL CLEAR${RESET} — all checked files passed." + exit 0 +fi diff --git a/scripts/test_runner.sh b/scripts/test_runner.sh new file mode 100755 index 00000000..94424f6f --- /dev/null +++ b/scripts/test_runner.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# test_runner.sh — Convenience wrapper for test_harness.sh +# Runs the test harness with sensible defaults for local development. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +exec "${SCRIPT_DIR}/test_harness.sh" --verbose "$@" From 641db6211226290239e5f193b6aab2869a79e097 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Fri, 10 Apr 2026 00:20:37 -0400 Subject: [PATCH 2/2] burn: Add proof-driven PR template (.gitea/PULL_REQUEST_TEMPLATE.md) Closes #451. Enforces the CONTRIBUTING.md proof standard at PR authoring time: summary, linked issue, acceptance criteria, proof evidence, risk and rollback. Aligns with existing bin/pr-checklist.py CI gate. --- .gitea/PULL_REQUEST_TEMPLATE.md | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .gitea/PULL_REQUEST_TEMPLATE.md diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..b6449473 --- /dev/null +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,49 @@ +## Summary + + + +## Linked Issue + + + + + +## Acceptance Criteria + + + +- [ ] Criterion 1 +- [ ] Criterion 2 + +## Proof + +### What was tested + + + + +``` +$ + +``` + +### Visual proof (if applicable) + + + + +## Risk and Rollback + + + +- **Risk level:** low / medium / high +- **What breaks if this is wrong:** +- **How to rollback:** + +## Checklist + +- [ ] Proof meets CONTRIBUTING.md standard (exact commands, output, or artifacts) +- [ ] Python files pass syntax check (`python -c "import ast; ast.parse(open('file.py').read())"`) +- [ ] Shell scripts are executable (`chmod +x`) +- [ ] Branch is up-to-date with base +- [ ] No more than 3 unrelated issues bundled in this PR