Pantheon/scripts/add-file-headers.sh

116 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Insert the standard file-header block into sources that lack one.
#
# ./scripts/add-file-headers.sh # dry run — report only
# ./scripts/add-file-headers.sh --apply # write the blocks
# ./scripts/add-file-headers.sh --apply src/core # limit to a subtree
#
# Adds the SPDX pair when missing, then:
#
# /**
# * @file name.c
# * @brief TODO
# *
# * @status None
# * @issues None
# * @todo None
# */
#
# @brief is seeded from the paired header's @brief when there is one, otherwise
# left as TODO — a one-line summary is not mechanically derivable, and a wrong
# one is worse than an absent one. scripts/check-headers.sh rejects TODO briefs
# on files a PR adds.
#
# Exempt: tests, generated sources, third-party-derived files (those keep their
# upstream attribution).
set -euo pipefail
APPLY=0
[ "${1-}" = "--apply" ] && { APPLY=1; shift; }
SCOPE="${1-.}"
SPDX='/* SPDX-License-Identifier: AGPL-3.0-or-later */'
COPYRIGHT='/* Copyright (C) 2026 David (Corey) Herrera */'
is_exempt() {
case "$1" in
tests/*|*/tests/*) return 0 ;; # test files
*_ptx.h|*ptx_modules*) return 0 ;; # generated
*) ;;
esac
# third-party attribution present
head -5 "$1" | grep -qE 'SPDX-FileCopyrightText|Apache-2\.0' && return 0
return 1
}
added=0 todo=0 seeded=0 skipped=0
while IFS= read -r f; do
head -12 "$f" | grep -q '@file' && continue
if is_exempt "$f"; then skipped=$((skipped+1)); continue; fi
base=$(basename "$f")
brief="TODO"
pair="${f%.*}.h"
if [ "$pair" != "$f" ] && [ -f "$pair" ]; then
p=$(grep -m1 '@brief' "$pair" 2>/dev/null | sed 's/.*@brief[[:space:]]*//' || true)
[ -n "$p" ] && brief="Implementation of ${p%.}."
fi
[ "$brief" = "TODO" ] && todo=$((todo+1)) || seeded=$((seeded+1))
added=$((added+1))
[ "$APPLY" = "1" ] || continue
tmp=$(mktemp)
if head -1 "$f" | grep -q 'SPDX-License-Identifier'; then
# SPDX already present — insert the block after the copyright line
head -2 "$f" > "$tmp"
rest=3
else
printf '%s\n%s\n' "$SPDX" "$COPYRIGHT" > "$tmp"
rest=1
fi
{
printf '/**\n'
printf ' * @file %s\n' "$base"
printf ' * @brief %s\n' "$brief"
printf ' *\n'
printf ' * @status None\n'
printf ' * @issues None\n'
printf ' * @todo None\n'
printf ' */\n'
} >> "$tmp"
tail -n "+$rest" "$f" >> "$tmp"
mv "$tmp" "$f"
done < <(git ls-files "$SCOPE" | grep -E '\.(c|h|cu|cuh|m)$')
printf 'headers %s: %d file(s) — %d brief seeded from paired header, %d left TODO; %d exempt\n' \
"$([ "$APPLY" = 1 ] && echo written || echo needed)" "$added" "$seeded" "$todo" "$skipped"
# --- SPDX pass -------------------------------------------------------------
# Independent of the header block: a file can carry @file and still have no SPDX
# line. Only files that are unambiguously ours get stamped. Anything whose header
# cites upstream sources is listed for manual triage instead — a port of an
# Apache-2.0 source needs Apache-2.0 plus the upstream copyright, and that is a
# per-file judgement, not a sed.
spdx_added=0 triage=0
while IFS= read -r f; do
head -3 "$f" | grep -q 'SPDX-License-Identifier' && continue
case "$f" in tests/*|*/tests/*|*_ptx.h) continue ;; esac
if head -20 "$f" | grep -qiE 'tt-metal|tt-kmd|Tenstorrent|transliteration|rendering of|upstream'; then
triage=$((triage+1))
[ "$APPLY" = 1 ] || printf ' triage: %s\n' "$f"
continue
fi
spdx_added=$((spdx_added+1))
[ "$APPLY" = 1 ] || continue
tmp=$(mktemp)
printf '%s\n%s\n' "$SPDX" "$COPYRIGHT" > "$tmp"
cat "$f" >> "$tmp"
mv "$tmp" "$f"
done < <(git ls-files "$SCOPE" | grep -E '\.(c|h|cu|cuh|m)$')
printf 'SPDX %s: %d file(s); %d need manual triage (upstream-derived)\n' \
"$([ "$APPLY" = 1 ] && echo written || echo needed)" "$spdx_added" "$triage"
[ "$APPLY" = 1 ] || printf 'dry run — pass --apply to write\n'