|
| 1 | +#!/usr/bin/env bash |
| 2 | +# SPDX-License-Identifier: PMPL-1.0-or-later |
| 3 | +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 4 | +# |
| 5 | +# fix-tracked-package-lock.sh — Remove tracked package-lock.json (standards#67) |
| 6 | +# |
| 7 | +# Part of the gitbot-fleet remediation scripts for estate-wide enforcement. |
| 8 | +# This script is triggered by the robot-repo-automaton when a Hypatia |
| 9 | +# finding of type `cicd_rules/tracked_npm_lockfile` is detected. |
| 10 | +# |
| 11 | +# What it does: |
| 12 | +# 1. Finds all tracked package-lock.json files in the repo. |
| 13 | +# 2. Runs `git rm --cached` on each (removing from index, not disk). |
| 14 | +# 3. Ensures package-lock.json and **/package-lock.json are in .gitignore. |
| 15 | +# |
| 16 | +# What it does NOT do: |
| 17 | +# * Does not commit or push — the fleet coordinator handles that. |
| 18 | +# * Does not delete node_modules/ (separate concern). |
| 19 | +# * Does not modify anything outside .gitignore and git index. |
| 20 | +# |
| 21 | +# Usage (gitbot fleet-coordinator integration): |
| 22 | +# bash fix-tracked-package-lock.sh <REPO_PATH> [FINDING_FILE] |
| 23 | +# |
| 24 | +# Standalone usage: |
| 25 | +# cd <repo> && bash /path/to/fix-tracked-package-lock.sh . |
| 26 | + |
| 27 | +set -euo pipefail |
| 28 | + |
| 29 | +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| 30 | +# shellcheck source=lib/third-party-excludes.sh |
| 31 | +source "${SCRIPT_DIR}/lib/third-party-excludes.sh" 2>/dev/null || true |
| 32 | + |
| 33 | +REPO_PATH="${1:-.}" |
| 34 | +FINDING_FILE="${2:-}" |
| 35 | + |
| 36 | +if [ ! -d "${REPO_PATH}/.git" ]; then |
| 37 | + echo "ERROR: ${REPO_PATH} is not a git repository." >&2 |
| 38 | + exit 1 |
| 39 | +fi |
| 40 | + |
| 41 | +echo "fix-tracked-package-lock: scanning ${REPO_PATH} ..." |
| 42 | + |
| 43 | +# --------------------------------------------------------------------------- |
| 44 | +# Step 1: un-track package-lock.json files |
| 45 | +# --------------------------------------------------------------------------- |
| 46 | + |
| 47 | +TRACKED=$(git -C "${REPO_PATH}" ls-files \ |
| 48 | + 'package-lock.json' \ |
| 49 | + '**/package-lock.json' \ |
| 50 | + 2>/dev/null || true) |
| 51 | + |
| 52 | +FIXED_COUNT=0 |
| 53 | +if [ -n "$TRACKED" ]; then |
| 54 | + while IFS= read -r f; do |
| 55 | + [ -z "$f" ] && continue |
| 56 | + git -C "${REPO_PATH}" rm --cached "$f" |
| 57 | + echo " [rm-cached] $f" |
| 58 | + FIXED_COUNT=$((FIXED_COUNT + 1)) |
| 59 | + done <<< "$TRACKED" |
| 60 | +else |
| 61 | + echo " No tracked package-lock.json files found." |
| 62 | +fi |
| 63 | + |
| 64 | +# --------------------------------------------------------------------------- |
| 65 | +# Step 2: ensure .gitignore covers package-lock.json |
| 66 | +# --------------------------------------------------------------------------- |
| 67 | + |
| 68 | +GITIGNORE="${REPO_PATH}/.gitignore" |
| 69 | + |
| 70 | +# Ensure .gitignore exists |
| 71 | +[ -f "$GITIGNORE" ] || touch "$GITIGNORE" |
| 72 | + |
| 73 | +added_gi=false |
| 74 | +for entry in "package-lock.json" "**/package-lock.json"; do |
| 75 | + if ! grep -qxF "$entry" "$GITIGNORE" 2>/dev/null; then |
| 76 | + if ! $added_gi; then |
| 77 | + printf '\n# npm-avoidant (standards#67): estate JS-runtime policy is Deno>Bun>pnpm>npm.\n# npm lockfiles must never be committed estate-wide.\n' >> "$GITIGNORE" |
| 78 | + added_gi=true |
| 79 | + fi |
| 80 | + printf '%s\n' "$entry" >> "$GITIGNORE" |
| 81 | + echo " [gitignore] Added: $entry" |
| 82 | + FIXED_COUNT=$((FIXED_COUNT + 1)) |
| 83 | + fi |
| 84 | +done |
| 85 | + |
| 86 | +if [ $FIXED_COUNT -eq 0 ]; then |
| 87 | + echo " Nothing to fix — already clean." |
| 88 | + exit 0 |
| 89 | +fi |
| 90 | + |
| 91 | +echo "fix-tracked-package-lock: $FIXED_COUNT change(s) applied." |
| 92 | +echo " Refs hyperpolymath/standards#67" |
| 93 | +echo " Next step: review staged changes and commit on a branch." |
0 commit comments