From c1d92306162a0c26a658d6e30e1a9e7195467977 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:31:17 +0100 Subject: [PATCH] chore(hooks): version-controlled pre-commit; SPDX split by file-type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the un-versioned .git/hooks/pre-commit with a committed, installable hook (hooks/pre-commit + hooks/install.sh sets core.hooksPath=hooks), fixing the 'hooks not reproducible across clones' gap. Licence fix (LICENCE-POLICY Rule 1): SPDX expectation now splits by file-type — prose docs (.md/.adoc) expect CC-BY-SA-4.0, code/config expect MPL-2.0 — so the hook stops rejecting correctly CC-BY-SA-licensed docs. Owner-string check kept strict on all (deliberate LLM-edit diagnostic). No-C / SNIFs / Idris-ABI policies preserved unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- hooks/install.sh | 15 ++++++++++ hooks/pre-commit | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100755 hooks/install.sh create mode 100755 hooks/pre-commit diff --git a/hooks/install.sh b/hooks/install.sh new file mode 100755 index 0000000..e6a8469 --- /dev/null +++ b/hooks/install.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) Jonathan D.A. Jewell +# +# Install the version-controlled git hooks for this repo. +# Points git at the committed hooks/ directory (reproducible across clones), +# rather than copying into the un-versioned .git/hooks/. + +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" + +git config core.hooksPath hooks +chmod +x hooks/pre-commit + +echo "Installed: core.hooksPath -> hooks/ (pre-commit active)" diff --git a/hooks/pre-commit b/hooks/pre-commit new file mode 100755 index 0000000..5f07ce8 --- /dev/null +++ b/hooks/pre-commit @@ -0,0 +1,71 @@ +#!/bin/bash +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) Jonathan D.A. Jewell +# +# Pre-commit enforcer: licence/owner headers + architecture policies. +# Canonical, version-controlled hook. Install with: bash hooks/install.sh +# (sets core.hooksPath=hooks so this file is used directly). + +# 1. License Check +# Determine if this is a SHARED (AGPL) or OWNED (MPL) repo +REMOTE_URL=$(git remote get-url origin 2>/dev/null) +DIR_NAME=$(basename "$(pwd)") + +if [[ "$REMOTE_URL" == *"JoshuaJewell"* || "$DIR_NAME" == boj-* || "$DIR_NAME" == bofj-* ]]; then + # Shared repos: components retain their original licences; verify root LICENSE is AGPL. + if ! grep -q "Affero General Public License" LICENSE 2>/dev/null; then + echo "ERROR: Shared repository must have AGPL-3.0-or-later LICENSE file." + exit 1 + fi +else + EXPECTED_OWNER="Jonathan D.A. Jewell " + + # Check staged files for SPDX header, split by file-type per LICENCE-POLICY Rule 1: + # prose docs (.md/.adoc) -> CC-BY-SA-4.0 ; everything else (code/config) -> MPL-2.0. + # Owner-string is required on all (deliberate LLM-edit diagnostic). + STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(zig|ex|idr|eph|py|js|ts|rs|c|h|adoc|md)$') + + for file in $STAGED_FILES; do + case "$file" in + *.md|*.adoc) FILE_LICENSE="CC-BY-SA-4.0" ;; + *) FILE_LICENSE="MPL-2.0" ;; + esac + if ! grep -q "SPDX-License-Identifier: $FILE_LICENSE" "$file"; then + echo "ERROR: File $file is missing correct SPDX-License-Identifier: $FILE_LICENSE" + exit 1 + fi + if ! grep -q "$EXPECTED_OWNER" "$file"; then + echo "ERROR: File $file is missing correct Owner: $EXPECTED_OWNER" + exit 1 + fi + done +fi + +# 2. No C Policy — all APIs/FFIs must be Zig (exceptions: proven, boj-server-cartridges) +if [[ "$DIR_NAME" != "proven" && "$DIR_NAME" != "boj-server-cartridges" ]]; then + STAGED_C_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.c$') + if [ -n "$STAGED_C_FILES" ]; then + echo "ERROR: Strict No C Policy. C files are not allowed in this repository." + echo "Offending files: $STAGED_C_FILES" + exit 1 + fi +fi + +# 3. SNIFs Policy — never raw NIFs; always Safe NIFs +STAGED_BEAM_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ex|exs|erl)$') +for file in $STAGED_BEAM_FILES; do + if grep -qE "erlang:load_nif|:erlang\.load_nif" "$file"; then + echo "ERROR: Raw NIFs are forbidden. Use SNIFs (Safe NIFs) from the snifs repository." + exit 1 + fi +done + +# 4. Idris 2 ABI Policy — ABIs/capability matchers should be Idris 2 (Zig allowed for the FFI bridge) +STAGED_ABI_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -i "abi") +for file in $STAGED_ABI_FILES; do + if [[ "$file" != *.idr && "$file" != *.zig && "$file" != *.adoc && "$file" != *.md ]]; then + echo "WARNING: ABI definition found in non-Idris/Zig file: $file" + fi +done + +exit 0