| 1 | #!/usr/bin/env bash |
| 2 | # One local preflight for the generated files and ratchets that otherwise turn |
| 3 | # main red after a green PR: run it before you push. |
| 4 | # |
| 5 | # scripts/preflight.sh regenerate what can be regenerated, check the rest |
| 6 | # scripts/preflight.sh --check change nothing; fail if anything is stale |
| 7 | # scripts/preflight.sh --full also run the cargo-backed runtime-contract and |
| 8 | # persistence-backlog ratchets (minutes, offline) |
| 9 | # scripts/preflight.sh --base REF compare feature receipts against REF |
| 10 | # (default: merge base with origin/main) |
| 11 | # |
| 12 | # As a pre-push hook it runs in --check mode: |
| 13 | # ln -s ../../scripts/preflight.sh "$(git rev-parse --git-path hooks)/pre-push" |
| 14 | # |
| 15 | # What it covers, and the command that fixes each one: |
| 16 | # - crates/tui/CHANGELOG.md slice scripts/sync-changelog.sh (written here) |
| 17 | # - README locale stamps and links retranslate; check-readme-translations.py |
| 18 | # prints the new sha256 stamp to use |
| 19 | # - product lexicon (warn-only) python3 scripts/check-lexicon.py lists each hit |
| 20 | # - dead-code / blocking-calls each ratchet's --update, committed in the |
| 21 | # (and with --full runtime-contract, same PR with the reason in the PR body |
| 22 | # persistence-backlog) |
| 23 | # - feature release-note receipts add each feat commit's #issue to |
| 24 | # for this branch's own commits CHANGELOG.md in the same PR |
| 25 | set -uo pipefail |
| 26 | |
| 27 | mode="write" |
| 28 | full=0 |
| 29 | base="" |
| 30 | if [[ "$(basename "$0")" == "pre-push" ]]; then |
| 31 | # git passes <remote> <url>; the hook only ever checks. |
| 32 | mode="check" |
| 33 | set -- |
| 34 | fi |
| 35 | while [[ "$#" -gt 0 ]]; do |
| 36 | case "$1" in |
| 37 | --check) mode="check"; shift ;; |
| 38 | --full) full=1; shift ;; |
| 39 | --base) base="${2:?--base needs a ref}"; shift 2 ;; |
| 40 | -h|--help) sed -n '2,26p' "$0"; exit 0 ;; |
| 41 | *) echo "usage: $0 [--check] [--full] [--base REF]" >&2; exit 2 ;; |
| 42 | esac |
| 43 | done |
| 44 | |
| 45 | root="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}")")/.." && pwd)" |
| 46 | cd "${root}" || exit 2 |
| 47 | |
| 48 | failed=() |
| 49 | step() { |
| 50 | local label="$1" fix="$2" |
| 51 | shift 2 |
| 52 | echo "== ${label}" |
| 53 | if "$@"; then |
| 54 | return 0 |
| 55 | fi |
| 56 | failed+=("${label}: ${fix}") |
| 57 | } |
| 58 | |
| 59 | if [[ "${mode}" == "write" ]]; then |
| 60 | step "TUI changelog slice" "scripts/sync-changelog.sh" ./scripts/sync-changelog.sh |
| 61 | else |
| 62 | step "TUI changelog slice" "scripts/sync-changelog.sh" ./scripts/sync-changelog.sh --check |
| 63 | fi |
| 64 | step "README translations in sync" \ |
| 65 | "retranslate the changed README sections, then update each stamp to the sha256 printed above" \ |
| 66 | python3 scripts/check-readme-translations.py |
| 67 | step "README locale link symmetry" "link every README.<locale>.md from README.md" \ |
| 68 | bash scripts/check-readme-locales.sh |
| 69 | # Warn-only: lists retired product words and engineering notes in English |
| 70 | # copy; never fails the preflight. |
| 71 | step "product lexicon (warn-only)" "python3 scripts/check-lexicon.py" \ |
| 72 | python3 scripts/check-lexicon.py --summary |
| 73 | step "dead-code budget" "python3 scripts/check-dead-code-budget.py --update" \ |
| 74 | python3 scripts/check-dead-code-budget.py |
| 75 | step "blocking-calls budget" "python3 scripts/check-blocking-calls-budget.py --update" \ |
| 76 | python3 scripts/check-blocking-calls-budget.py |
| 77 | if [[ "${full}" == "1" ]]; then |
| 78 | step "runtime-contract budget" \ |
| 79 | "python3 scripts/check-runtime-contract-budget.py --update --allow-increase" \ |
| 80 | python3 scripts/check-runtime-contract-budget.py |
| 81 | step "persistence-backlog budget" \ |
| 82 | "python3 scripts/check-persistence-backlog-budget.py --update" \ |
| 83 | python3 scripts/check-persistence-backlog-budget.py |
| 84 | fi |
| 85 | |
| 86 | if [[ -z "${base}" ]]; then |
| 87 | base="$(git merge-base HEAD origin/main 2>/dev/null || true)" |
| 88 | fi |
| 89 | if [[ -n "${base}" ]]; then |
| 90 | step "feature release-note receipts (${base:0:12}..HEAD)" \ |
| 91 | "add each feat commit's #issue to CHANGELOG.md in this branch" \ |
| 92 | ./scripts/release/check-feature-release-notes.sh "${base}" HEAD |
| 93 | else |
| 94 | echo "== feature release-note receipts: skipped (no origin/main; pass --base REF)" |
| 95 | fi |
| 96 | |
| 97 | echo |
| 98 | if [[ "${#failed[@]}" -gt 0 ]]; then |
| 99 | echo "preflight: ${#failed[@]} check(s) failed. Fix, commit the result, and re-run:" >&2 |
| 100 | for line in "${failed[@]}"; do |
| 101 | echo " - ${line}" >&2 |
| 102 | done |
| 103 | exit 1 |
| 104 | fi |
| 105 | if [[ "${mode}" == "write" ]] && ! git diff --quiet -- crates/tui/CHANGELOG.md; then |
| 106 | echo "preflight: OK, and crates/tui/CHANGELOG.md was regenerated -- commit it." |
| 107 | else |
| 108 | echo "preflight: OK" |
| 109 | fi |
| 110 |