Pantheon/scripts/check-headers.sh

99 lines
2.4 KiB
Bash
Executable File

#!/bin/sh
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (C) 2026 David (Corey) Herrera
#
# Verify every source file ADDED in a range carries the SPDX pair and the file
# header block. See docs/coding-best-practices.md.
#
# scripts/check-headers.sh [range] # default: origin/main..HEAD
#
# Only files the range ADDS are checked. Files that already exist are left
# alone deliberately: the rule is that everyone submitting code stays in
# compliance, not that the whole tree gets retrofitted.
#
# Exempt: tests, generated sources, and files carrying upstream third-party
# attribution (those keep their own header).
#
# Run it locally before opening a pull request; CI runs the same script.
set -eu
cd "$(git rev-parse --show-toplevel)"
RANGE="${1:-${BASE_REF:-origin/main}..HEAD}"
files=$(git diff --diff-filter=A --name-only "$RANGE" -- \
'*.c' '*.h' '*.cu' '*.cuh' '*.m')
if [ -z "$files" ]; then
echo "check-headers: ok (no source files added)"
exit 0
fi
fail=0
note() {
echo "check-headers: $1" >&2
echo " $2" >&2
fail=$((fail + 1))
}
OLDIFS=$IFS
IFS='
'
for f in $files; do
IFS=$OLDIFS
# Added and then removed again within the range.
[ -f "$f" ] || continue
case "$f" in
tests/*|*/tests/*) continue ;;
*_ptx.h|*_ptx.cuh) continue ;;
esac
# Third-party-derived files keep their upstream attribution.
if head -5 "$f" | grep -qE 'SPDX-FileCopyrightText|Apache-2\.0'; then
continue
fi
if ! head -3 "$f" | grep -q 'SPDX-License-Identifier'; then
note "$f: missing the SPDX identifier" \
"add: /* SPDX-License-Identifier: AGPL-3.0-or-later */"
continue
fi
if ! head -16 "$f" | grep -q '@file'; then
note "$f: missing the @file header block" \
"run: ./scripts/add-file-headers.sh --apply $f"
continue
fi
brief=$(head -16 "$f" | sed -n 's|.*@brief[[:space:]]*||p' | head -1)
case "$brief" in
'')
note "$f: @brief is empty" \
"one line saying what this file is for"
;;
TODO*|todo*)
note "$f: @brief is still TODO" \
"replace it with one line saying what this file is for"
;;
esac
for tag in @status @issues @todo; do
if ! head -16 "$f" | grep -q -- "$tag"; then
note "$f: header block is missing $tag" \
"all three of @status/@issues/@todo are present, 'None' when empty"
fi
done
done
IFS=$OLDIFS
if [ "$fail" -gt 0 ]; then
echo "check-headers: $fail problem(s) — see docs/coding-best-practices.md" >&2
exit 1
fi
echo "check-headers: ok"