#!/usr/bin/env bash # Requires bash ≥ 4 for `mapfile`; on macOS install via `brew install bash` # and invoke as `bash scripts/quality/license-headers.sh`. The portable # fallback below works on bash 3.2 too. # scripts/quality/license-headers.sh # # Verify that every source file under code/ carries an SPDX-License- # Identifier header. The project policy (doc/release-policy.md) is: # # * Every conformallab++ source file (.h, .hpp, .cpp under code/include # or code/src, but NOT code/deps/) must have: # SPDX-License-Identifier: MIT # * The copyright line is encouraged but not enforced (the year is # allowed to lag). # # Files under code/deps/ are vendored third-party code (Eigen, JSON, # GLFW, libigl, …) and are skipped — they carry their own licenses. # # Exit codes: # 0 every checked file has the SPDX header # 1 at least one file is missing it # 2 prerequisite missing # # Run from any directory; the script resolves the repo root from its own path. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$ROOT" SPDX="SPDX-License-Identifier: MIT" COPYRIGHT="Copyright (c) 2024-$(date +%Y) Tarik Moussa." FIX=0 for arg in "$@"; do case "$arg" in --fix) FIX=1 ;; *) echo "Unknown arg: $arg (only --fix supported)" >&2; exit 2 ;; esac done # Insert a 2-line header at the top of $1, but BELOW any existing # `#pragma once` so the include-guard role is preserved. Idempotent — # the caller has already verified the SPDX line is missing. insert_header () { local f="$1" local tmp tmp="$(mktemp)" local first_line first_line="$(head -1 "$f")" if echo "$first_line" | grep -q "^#pragma once"; then # Header form 1: keep `#pragma once` on line 1, then license, then blank. { echo "$first_line" echo "// $COPYRIGHT" echo "// $SPDX" echo tail -n +2 "$f" } > "$tmp" elif echo "$first_line" | grep -q "^#ifndef"; then # Header form 2: insert license ABOVE the include guard. { echo "// $COPYRIGHT" echo "// $SPDX" echo cat "$f" } > "$tmp" else # Other (most likely a .cpp): just prepend. { echo "// $COPYRIGHT" echo "// $SPDX" echo cat "$f" } > "$tmp" fi mv "$tmp" "$f" } # Files to check: code/include + code/src + code/tests. Skip code/deps. FILES_LIST="$(find code/include code/src code/tests \ \( -name "*.h" -o -name "*.hpp" -o -name "*.cpp" \) \ -type f 2>/dev/null \ | grep -v "^code/deps/" \ | grep -v " 2\." \ | sort)" n_total=0 n_missing=0 n_fixed=0 missing_list="" while IFS= read -r f; do [ -z "$f" ] && continue n_total=$((n_total + 1)) # Check only the first 30 lines — SPDX must be near the top. if ! head -30 "$f" | grep -q "$SPDX"; then n_missing=$((n_missing + 1)) if [ "$FIX" -eq 1 ]; then insert_header "$f" n_fixed=$((n_fixed + 1)) fi missing_list="$missing_list $f " fi done <&2 exit 2 fi echo "Checked $n_total source files for SPDX header." if [ "$FIX" -eq 1 ]; then if [ "$n_fixed" -gt 0 ]; then echo "FIX mode: inserted '$SPDX' into $n_fixed file(s)." echo " Review with: git diff" exit 0 fi fi if [ "$n_missing" -gt 0 ]; then echo echo "FAIL: $n_missing file(s) missing '$SPDX' in their first 30 lines:" printf '%s' "$missing_list" echo echo "Recovery: bash scripts/quality/license-headers.sh --fix" exit 1 fi echo "OK: every checked file carries '$SPDX'." exit 0