diff --git a/.binary-allowlist b/.binary-allowlist new file mode 100644 index 00000000..b3f3f4ef --- /dev/null +++ b/.binary-allowlist @@ -0,0 +1,72 @@ +# Binary files this repo commits on purpose. One bash glob per line. +# +# Read by scripts/check_no_binaries.sh. Note that matching uses bash `case`, +# where `*` DOES cross `/` -- so `Programming/*.pdf` matches a .pdf at any +# depth under Programming/. +# +# Policy for this repo (approved decision 3). This is an archive of submitted +# university coursework: the reports, figures, schematics and datasets ARE the +# deliverable, so they stay, and history is not rewritten. What the gate is +# actually here to stop is NEW junk -- compiled output, and binaries dropped +# into the tooling directories. +# +# So the allowlist is scoped by PATH, not blanket: +# - documents/figures/data are allowed under Programming/ and NotProgramming/ +# - compiled output (.o .so .a .exe .dll .dylib .class .pyc .pyo) is allowed +# NOWHERE, and has no entry below. 189 such files were untracked in +# d4075278; .gitignore keeps them out. +# - the repo root, gates/, scripts/ and .github/ have no entry either, so a +# binary added there is blocked. +# +# Every pattern here must also survive `git ls-files | git check-ignore +# --stdin --no-index` printing nothing -- an allowlisted file that .gitignore +# also matches fails silently on `git add` rather than erroring. + +# --- Reports and presentations (155 .pdf, 3 .docx, 1 .doc, 1 .pptx) --------- +Programming/*.pdf +NotProgramming/*.pdf +Programming/*.doc +NotProgramming/*.doc +Programming/*.docx +NotProgramming/*.docx +Programming/*.ppt +NotProgramming/*.ppt +Programming/*.pptx +NotProgramming/*.pptx + +# --- Figures, screenshots, schematics (275 .png, 68 .jpg, 52 .gif, ...) ----- +Programming/*.png +NotProgramming/*.png +Programming/*.jpg +NotProgramming/*.jpg +Programming/*.jpeg +NotProgramming/*.jpeg +Programming/*.gif +NotProgramming/*.gif +Programming/*.bmp +NotProgramming/*.bmp +Programming/*.svg +NotProgramming/*.svg +Programming/*.webp +NotProgramming/*.webp +Programming/*.ico +NotProgramming/*.ico +Programming/*.tif +NotProgramming/*.tif +Programming/*.tiff +NotProgramming/*.tiff + +# --- Fonts shipped inside a submitted project ------------------------------ +Programming/*.ttf +NotProgramming/*.ttf + +# --- Datasets and course-supplied archives --------------------------------- +# The .tgz entries matter: EOPSY ships its simulators as ftp/sched.tgz and +# ftp/memory.tgz, and `make setup` extracts them. Blocking those would break +# the lab. The .zip files are submitted result bundles and course inputs. +Programming/*.zip +NotProgramming/*.zip +Programming/*.tgz +NotProgramming/*.tgz +Programming/*.bin +NotProgramming/*.bin diff --git a/scripts/check_no_binaries.sh b/scripts/check_no_binaries.sh new file mode 100755 index 00000000..080428a8 --- /dev/null +++ b/scripts/check_no_binaries.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# Pre-commit hook: block binary and image files from being committed. +# +# Copied from ~/testsAndMisc/meta/scripts/check_no_binaries.sh, which is where +# this gate lives today (it is NOT in the shared ~/utils gate set). Keep the two +# in step, and prefer moving it to ~/utils over letting them drift -- a +# hand-copied gate is exactly what produced four forks of the 250-line cap. +# +# Only the remedy text differs: "move it to a sibling _binaries/ dir" is wrong +# advice here, because in a coursework archive the PDFs and figures ARE the +# submitted deliverable (approved decision 3: allowlist in place, no history +# rewrite). +# Allowed exceptions are listed in .binary-allowlist (one glob pattern per line). +set -euo pipefail + +ALLOWLIST_FILE=".binary-allowlist" + +# Binary/image extensions to block +BLOCKED_EXTENSIONS=( + # Images + png jpg jpeg gif webp svg ico bmp tiff tif psd + # Audio/Video + mp3 mp4 wav avi mkv flac ogg wma aac m4a mov wmv flv + # Archives + zip tar gz tgz bz2 xz 7z rar + # Documents + pdf doc docx xls xlsx ppt pptx + # Fonts + ttf woff woff2 eot otf + # Compiled / binary + o so a exe dll dylib pyc pyo class + # Data + apkg bin flat db sqlite sqlite3 +) + +# Build regex pattern from extensions +pattern="" +for ext in "${BLOCKED_EXTENSIONS[@]}"; do + if [ -z "$pattern" ]; then + pattern="\\.(${ext}" + else + pattern="${pattern}|${ext}" + fi +done +pattern="${pattern})$" + +# Load allowlist +allowed_patterns=() +if [ -f "$ALLOWLIST_FILE" ]; then + while IFS= read -r line; do + # Skip comments and empty lines + [[ "$line" =~ ^[[:space:]]*# ]] && continue + [[ -z "${line// /}" ]] && continue + allowed_patterns+=("$line") + done < "$ALLOWLIST_FILE" +fi + +is_allowed() { + local file="$1" + for pat in "${allowed_patterns[@]+"${allowed_patterns[@]}"}"; do + # Use bash pattern matching (glob) + # shellcheck disable=SC2254 + case "$file" in + $pat) return 0 ;; + esac + done + return 1 +} + +found=0 +for file in "$@"; do + # Skip files not tracked by git (deleted from index or untracked) + git ls-files --error-unmatch "$file" >/dev/null 2>&1 || continue + # Check if the file matches a blocked extension + if echo "$file" | grep -qiE "$pattern"; then + if is_allowed "$file"; then + continue + fi + echo "BLOCKED: $file" + echo " Binary/image files should not be committed to the repo." + echo " In this coursework archive the submitted reports, figures and" + echo " datasets are allowed -- but only under Programming/ or NotProgramming/." + echo " To allow this file, add a glob pattern to $ALLOWLIST_FILE" + found=1 + fi +done + +if [ "$found" -eq 1 ]; then + echo "" + echo "ERROR: Attempted to commit binary/image files." + echo "Options:" + echo " 1. A submitted deliverable? Add a glob to $ALLOWLIST_FILE" + echo " 2. Build output? Add it to .gitignore instead, never the allowlist" + exit 1 +fi