utils/dep_freshness/install_cli.py
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

58 lines
1.6 KiB
Python

"""Command line for `install`: which repo, check-only, and the SDK pin."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
from dep_freshness.install import install, plan, write_fvmrc
from dep_freshness.registries.toolchain import flutter_latest
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Install the dependency-freshness gate into a repo."
)
parser.add_argument("repo", type=Path)
parser.add_argument(
"--check",
action="store_true",
help="report what would be written, change nothing",
)
parser.add_argument(
"--fvm", action="store_true", help="also pin the Flutter SDK in <repo>/.fvmrc"
)
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
repo = args.repo.resolve()
if args.check:
todo = plan(repo)
for piece in todo:
print(f"would write: {piece}")
if not todo:
print("gate already current")
return 0
for piece in install(repo):
print(f"wrote: {piece}")
if args.fvm:
version, _dart = flutter_latest()
if version is None:
# Guessing a version here would pin every repo to a number nobody
# chose, and the gate would then compare against it forever.
print(
"could not resolve latest stable Flutter; .fvmrc left alone",
file=sys.stderr,
)
return 1
if write_fvmrc(repo, version):
print(f"wrote: .fvmrc (flutter {version})")
return 0