utils/scripts/install_dependency_freshness_gate.sh
Krzysztof kuhy Rudnicki 71a43d9658
Some checks failed
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
crdt_sync_dart tests / test (push) Has been cancelled
crdt_sync_flutter tests / test (push) Has been cancelled
design_system tests / test (push) Has been cancelled
sync_settings_ui tests / test (push) Has been cancelled
Script the gate rollout instead of hand-copying it into sixteen repos
The gate is three files per repo -- a delegate, a pre-commit hook entry and a
CI workflow -- and Phase 3 needs them in sixteen more repos. Hand-replication
is precisely how the 250-line cap ended up shipping under four different hook
ids, two of which were independent reimplementations rather than delegates, so
the copy is now a script and the templates have exactly one home.

  scripts/install_dependency_freshness_gate.sh <repo> [--check] [--fvm]

Idempotent, and it rewrites a copy that has drifted from the template rather
than leaving it. `--check` reports without writing. `--fvm` pins the Flutter
SDK, and only in a repo that actually holds a pubspec.yaml -- an .fvmrc in a
Python repo declares a toolchain nothing there builds with. An unresolvable
SDK version leaves the pin alone and exits 1: guessing would pin every repo to
a number nobody chose.

The pre-commit block is APPENDED rather than parsed and re-emitted. Every one
of these configs carries comments explaining why its hooks exist, and a YAML
round-trip through the standard library drops all of them.

306 tests, 100% branch coverage on every production module.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
2026-08-28 14:12:00 +02:00

86 lines
2.3 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# Install the dependency-freshness gate's three pieces into a repo.
#
# The gate is a delegate script + a pre-commit hook entry + a CI workflow, and
# every repo needs all three. Copying them by hand across ~16 repos is what
# produced four different ids for the 250-line cap, two of them independent
# reimplementations, so the copy lives here and the templates have one home
# (utils/dep_freshness/templates/).
#
# Idempotent: a second run reports "already current" and writes nothing.
#
# NOTE: never install the gate ahead of the upgrade. A repo that gets the gate
# while its dependencies are still stale is instantly and permanently red --
# gate and upgrade belong in the same commit.
#
# Usage:
# scripts/install_dependency_freshness_gate.sh <repo> [--check] [--fvm]
#
# Options:
# --check report what a run would write, change nothing
# --fvm also pin the Flutter SDK in <repo>/.fvmrc (Dart repos only)
#
# Exit: 0 installed or already current | 1 bad usage / missing repo
# ============================================================================
set -euo pipefail
readonly UTILS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
REPO=""
CHECK=""
FVM=""
usage() {
echo "Usage: $SCRIPT_NAME <repo> [--check] [--fvm]"
echo " --check report what would be written, change nothing"
echo " --fvm also write <repo>/.fvmrc pinning the Flutter SDK"
exit 0
}
validate_requirements() {
if [[ -z "$REPO" ]]; then
echo "Error: a repository path is required" >&2
exit 1
fi
if [[ ! -d "$REPO" ]]; then
echo "Error: no such directory: $REPO" >&2
exit 1
fi
}
main() {
validate_requirements
PYTHONPATH="$UTILS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
python3 -m dep_freshness.install_main "$REPO" $CHECK $FVM
}
while [[ $# -gt 0 ]]; do
case $1 in
--check)
CHECK="--check"
shift
;;
--fvm)
FVM="--fvm"
shift
;;
-h|--help)
usage
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
REPO="$1"
shift
;;
esac
done
main