#!/bin/bash # check-deps.sh - Enforces the strict dependency allowlist from CONVENTIONS.md # Exit 1 if any unapproved import is found. # # The allowlist is parsed from CONVENTIONS.md to maintain a single source of truth. set -euo pipefail CONVENTIONS_FILE="${1:-CONVENTIONS.md}" if [ ! -f "$CONVENTIONS_FILE" ]; then echo "❌ CONVENTIONS.md not found" exit 1 fi # Parse approved packages from CONVENTIONS.md table # Looks for lines like: | `gopkg.in/yaml.v3` | ... ALLOWED=() while IFS= read -r line; do # Extract package from markdown table cell: | `package` | pkg=$(echo "$line" | grep -oP '\| `\K[^`]+' | head -1 || true) if [ -n "$pkg" ] && [[ "$pkg" != "Package" ]]; then ALLOWED+=("$pkg") fi done < <(grep -E '^\| `[a-zA-Z]' "$CONVENTIONS_FILE" || true) if [ ${#ALLOWED[@]} -eq 0 ]; then echo "⚠️ No approved packages found in $CONVENTIONS_FILE" echo " (This is fine if you want stdlib-only)" fi # Get DIRECT dependencies only (exclude indirect/transitive) # Fail closed: if go list fails, we exit non-zero IMPORTS=$(go list -m -f '{{if not .Indirect}}{{.Path}}{{end}}' all 2>&1) || { echo "❌ Failed to list dependencies: $IMPORTS" exit 1 } # Filter out the module itself (first line) and empty lines IMPORTS=$(echo "$IMPORTS" | tail -n +2 | grep -v '^$' || true) if [ -z "$IMPORTS" ]; then echo "✅ No external dependencies" exit 0 fi VIOLATIONS="" while IFS= read -r import; do [ -z "$import" ] && continue # Check if import matches any allowed package (prefix match for subpackages) MATCHED=false for allowed in "${ALLOWED[@]}"; do if [[ "$import" == "$allowed" ]] || [[ "$import" == "$allowed/"* ]]; then MATCHED=true break fi done if [ "$MATCHED" = false ]; then VIOLATIONS="${VIOLATIONS} - ${import}"$'\n' fi done <<< "$IMPORTS" if [ -n "$VIOLATIONS" ]; then echo "❌ UNAPPROVED DEPENDENCIES DETECTED" echo "" echo "The following imports are not in the allowlist:" printf "%s" "$VIOLATIONS" echo "" echo "Approved packages (from CONVENTIONS.md):" for pkg in "${ALLOWED[@]}"; do echo " - $pkg" done echo "" echo "To add a dependency:" echo " 1. Open a PR that ONLY updates CONVENTIONS.md" echo " 2. Get explicit approval from Aaron" echo " 3. After merge, use the package in a separate PR" exit 1 fi echo "✅ All dependencies are approved" echo " Direct deps: $(echo "$IMPORTS" | wc -l | tr -d ' ')"