142 lines
3.9 KiB
Bash
Executable file
142 lines
3.9 KiB
Bash
Executable file
#!/bin/bash
|
|
# node-modules-lock.sh - Manage node_modules write protection
|
|
#
|
|
# PURPOSE: Enforce that node_modules is read-only to prevent accidental edits.
|
|
# Packages should be modified at ~/Code/@packages/, not in node_modules.
|
|
#
|
|
# USAGE:
|
|
# ./tooling/scripts/node-modules-lock.sh lock [dir] # Make read-only (postinstall)
|
|
# ./tooling/scripts/node-modules-lock.sh unlock [dir] # Make writable (preinstall)
|
|
# ./tooling/scripts/node-modules-lock.sh status [dir] # Show current state
|
|
# ./tooling/scripts/node-modules-lock.sh lock-all # Lock all node_modules in monorepo
|
|
#
|
|
# Called automatically by package.json preinstall/postinstall hooks.
|
|
# [dir] defaults to current working directory if not specified.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
# Target directory: use argument if provided, else current working directory
|
|
TARGET_DIR="${2:-$(pwd)}"
|
|
NODE_MODULES="$TARGET_DIR/node_modules"
|
|
|
|
usage() {
|
|
echo "Usage: $0 {lock|unlock|status|lock-all|unlock-all|status-all} [dir]"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " lock [dir] - Make node_modules read-only (run after pnpm install)"
|
|
echo " unlock [dir] - Make node_modules writable (run before pnpm install)"
|
|
echo " status [dir] - Show current protection status"
|
|
echo " lock-all - Lock ALL node_modules in monorepo"
|
|
echo " unlock-all - Unlock ALL node_modules in monorepo"
|
|
echo " status-all - Show status of ALL node_modules in monorepo"
|
|
echo ""
|
|
echo "[dir] defaults to current working directory"
|
|
exit 1
|
|
}
|
|
|
|
status() {
|
|
if [ ! -d "$NODE_MODULES" ]; then
|
|
echo "node_modules: does not exist"
|
|
return 0
|
|
fi
|
|
|
|
# Check if .bin directory is writable (indicator of lock state)
|
|
if [ -w "$NODE_MODULES/.bin" ]; then
|
|
echo "node_modules: UNLOCKED (writable)"
|
|
else
|
|
echo "node_modules: LOCKED (read-only)"
|
|
fi
|
|
}
|
|
|
|
lock() {
|
|
if [ ! -d "$NODE_MODULES" ]; then
|
|
echo "node_modules does not exist, skipping lock"
|
|
return 0
|
|
fi
|
|
|
|
# Lock all top-level entries EXCEPT .pnpm* (pnpm's internal state files)
|
|
# pnpm needs to write to .pnpm-workspace-state-v1.json after postinstall
|
|
find "$NODE_MODULES" -mindepth 1 -maxdepth 1 ! -name '.pnpm*' -exec chmod -R a-w {} +
|
|
|
|
echo "node_modules: LOCKED"
|
|
}
|
|
|
|
unlock() {
|
|
if [ ! -d "$NODE_MODULES" ]; then
|
|
echo "node_modules does not exist, skipping unlock"
|
|
return 0
|
|
fi
|
|
|
|
chmod -R u+w "$NODE_MODULES"
|
|
|
|
echo "node_modules: UNLOCKED"
|
|
}
|
|
|
|
# Find all node_modules directories in the monorepo
|
|
find_all_node_modules() {
|
|
find "$REPO_ROOT" -type d -name "node_modules" -not -path "*/node_modules/*" 2>/dev/null
|
|
}
|
|
|
|
lock_all() {
|
|
echo "Locking all node_modules in monorepo..."
|
|
local count=0
|
|
while IFS= read -r nm_dir; do
|
|
NODE_MODULES="$nm_dir"
|
|
lock
|
|
count=$((count + 1))
|
|
done < <(find_all_node_modules)
|
|
echo "Locked $count node_modules directories"
|
|
}
|
|
|
|
unlock_all() {
|
|
echo "Unlocking all node_modules in monorepo..."
|
|
local count=0
|
|
while IFS= read -r nm_dir; do
|
|
NODE_MODULES="$nm_dir"
|
|
unlock
|
|
count=$((count + 1))
|
|
done < <(find_all_node_modules)
|
|
echo "Unlocked $count node_modules directories"
|
|
}
|
|
|
|
status_all() {
|
|
echo "Status of all node_modules in monorepo:"
|
|
echo ""
|
|
while IFS= read -r nm_dir; do
|
|
local rel_path="${nm_dir#$REPO_ROOT/}"
|
|
NODE_MODULES="$nm_dir"
|
|
printf " %-60s " "$rel_path"
|
|
if [ -w "$nm_dir/.bin" ] 2>/dev/null; then
|
|
echo "UNLOCKED"
|
|
else
|
|
echo "LOCKED"
|
|
fi
|
|
done < <(find_all_node_modules)
|
|
}
|
|
|
|
case "${1:-}" in
|
|
lock)
|
|
lock
|
|
;;
|
|
unlock)
|
|
unlock
|
|
;;
|
|
status)
|
|
status
|
|
;;
|
|
lock-all)
|
|
lock_all
|
|
;;
|
|
unlock-all)
|
|
unlock_all
|
|
;;
|
|
status-all)
|
|
status_all
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|