@biffo/cli 0.172.0 → 0.172.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -48,11 +48,28 @@
|
|
|
48
48
|
#
|
|
49
49
|
# Usage:
|
|
50
50
|
# sh scripts/verify.sh # everything applicable to this repo
|
|
51
|
+
# sh scripts/verify.sh --list # print the checks it WOULD run, and stop
|
|
51
52
|
# pnpm run verify # same
|
|
52
53
|
# BIFFO_SKIP_VERIFY=1 git push # escape hatch, for when you mean it
|
|
54
|
+
#
|
|
55
|
+
# `--list` exists so parity with CI can be tested against what this script
|
|
56
|
+
# actually does, rather than against its source text. The checks are assembled
|
|
57
|
+
# at runtime from what the repo has, so grepping the file for `pnpm run lint`
|
|
58
|
+
# proves nothing -- and a parity test that can be satisfied by a comment is not
|
|
59
|
+
# a parity test.
|
|
60
|
+
#
|
|
61
|
+
# `--list` reports what THIS REPO requires, deliberately ignoring whether the
|
|
62
|
+
# tooling happens to be installed here. Parity with CI is a property of the
|
|
63
|
+
# repository; "can this machine run it" is a property of the machine. Conflating
|
|
64
|
+
# them made the parity test pass locally and fail on a CI runner that has no
|
|
65
|
+
# `uv` or `terraform` -- the gate-green/CI-red split this whole exercise exists
|
|
66
|
+
# to remove, reproduced inside its own guard.
|
|
53
67
|
|
|
54
68
|
set -u
|
|
55
69
|
|
|
70
|
+
LIST=""
|
|
71
|
+
[ "${1:-}" = "--list" ] && LIST=1
|
|
72
|
+
|
|
56
73
|
FAILED=""
|
|
57
74
|
PASSED=""
|
|
58
75
|
SKIPPED=""
|
|
@@ -60,13 +77,56 @@ SKIPPED=""
|
|
|
60
77
|
PYTEST="${BIFFO_VERIFY_PYTEST:-}"
|
|
61
78
|
|
|
62
79
|
have_script() {
|
|
63
|
-
[ -f package.json ] || return 1
|
|
64
|
-
|
|
80
|
+
[ -f "$2/package.json" ] || return 1
|
|
81
|
+
# grep rather than node: --list must work on a machine with no toolchain at
|
|
82
|
+
# all, because what it reports is a property of the repo, not of the machine.
|
|
83
|
+
# Deliberately NOT anchored to line start: that only matches a pretty-printed
|
|
84
|
+
# package.json, and a minified one would silently report "no lint script" --
|
|
85
|
+
# a skip that looks like a considered decision. A false positive here costs a
|
|
86
|
+
# loud `pnpm run` failure; a false negative costs an unchecked push.
|
|
87
|
+
grep -qE "\"$1\"[[:space:]]*:" "$2/package.json"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Every directory holding a JS package this repo owns.
|
|
91
|
+
#
|
|
92
|
+
# A repo with a root package.json is a workspace: `turbo run lint` fans out and
|
|
93
|
+
# running per-package as well would double the work. A repo WITHOUT one keeps
|
|
94
|
+
# its JS in subdirectories -- web/ and web-admin/ in the plugin repos,
|
|
95
|
+
# apps/frontend/ in the siblings -- and their CI runs the same scripts there
|
|
96
|
+
# with `working-directory:`.
|
|
97
|
+
#
|
|
98
|
+
# ## Why this exists (#852)
|
|
99
|
+
#
|
|
100
|
+
# The gate used to check the repo root and nothing else. In the ten repos with
|
|
101
|
+
# no root package.json -- every plugin, every sibling, both runner repos -- it
|
|
102
|
+
# printed `javascript n/a - no package.json in this repo` and then
|
|
103
|
+
# `verify passed`, on repos whose entire frontend is JS. A 100% TypeScript
|
|
104
|
+
# change pushed green with zero JavaScript verification.
|
|
105
|
+
#
|
|
106
|
+
# That is worse than the missing hooks this gate was built to fix. A repo with
|
|
107
|
+
# no hooks makes no claim; this one claimed to have checked. And the standard
|
|
108
|
+
# it was written to enforce says exactly that inapplicable and absent must not
|
|
109
|
+
# look the same -- while reporting "not applicable" for the language the change
|
|
110
|
+
# was written in.
|
|
111
|
+
js_dirs() {
|
|
112
|
+
if [ -f package.json ]; then
|
|
113
|
+
echo "."
|
|
114
|
+
return
|
|
115
|
+
fi
|
|
116
|
+
find . -name package.json \
|
|
117
|
+
-not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/.next/*" \
|
|
118
|
+
-not -path "*/.turbo/*" -not -path "*/.worktrees/*" -not -path "*/out/*" \
|
|
119
|
+
-not -path "*/coverage/*" -not -path "*/.venv/*" 2>/dev/null |
|
|
120
|
+
sed 's|/package.json$||' | sort
|
|
65
121
|
}
|
|
66
122
|
|
|
67
123
|
run_check() {
|
|
68
124
|
name="$1"
|
|
69
125
|
shift
|
|
126
|
+
if [ -n "$LIST" ]; then
|
|
127
|
+
echo "$*"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
70
130
|
start=$(date +%s)
|
|
71
131
|
if "$@" >"/tmp/biffo-verify.$$" 2>&1; then
|
|
72
132
|
PASSED="$PASSED $name"
|
|
@@ -80,16 +140,17 @@ run_check() {
|
|
|
80
140
|
}
|
|
81
141
|
|
|
82
142
|
skip() {
|
|
143
|
+
[ -n "$LIST" ] && return 0
|
|
83
144
|
SKIPPED="$SKIPPED $1"
|
|
84
145
|
printf ' \033[90m-- %-16s n/a - %s\033[0m\n' "$1" "$2"
|
|
85
146
|
}
|
|
86
147
|
|
|
87
|
-
printf '\nverify - the checks CI runs, before the push\n\n'
|
|
148
|
+
[ -n "$LIST" ] || printf '\nverify - the checks CI runs, before the push\n\n'
|
|
88
149
|
|
|
89
150
|
# Python first: ruff is near-instant, so the cheapest feedback on the largest
|
|
90
151
|
# single class of failure comes back immediately.
|
|
91
152
|
if [ -f pyproject.toml ]; then
|
|
92
|
-
if command -v uv >/dev/null 2>&1; then
|
|
153
|
+
if [ -n "$LIST" ] || command -v uv >/dev/null 2>&1; then
|
|
93
154
|
run_check ruff-check uv run ruff check .
|
|
94
155
|
run_check ruff-format uv run ruff format --check .
|
|
95
156
|
run_check pyright uv run pyright
|
|
@@ -107,7 +168,7 @@ fi
|
|
|
107
168
|
|
|
108
169
|
# Terraform, wherever this repo keeps it: modules/ in the template and
|
|
109
170
|
# instances, infra/ and modules/ in siblings.
|
|
110
|
-
if command -v terraform >/dev/null 2>&1; then
|
|
171
|
+
if [ -n "$LIST" ] || command -v terraform >/dev/null 2>&1; then
|
|
111
172
|
# Scope must match this repo's CI, not exceed it. The template and instances
|
|
112
173
|
# deliberately fmt-check modules/ ONLY: infra/environments/ is user-owned, and
|
|
113
174
|
# a template-shipped check asserting over paths the template does not own is
|
|
@@ -138,20 +199,34 @@ fi
|
|
|
138
199
|
|
|
139
200
|
# JS, cheapest first; `test` last because it is slowest and the most likely to
|
|
140
201
|
# be interrupted by an impatient reader.
|
|
141
|
-
|
|
202
|
+
JS_DIRS=$(js_dirs)
|
|
203
|
+
if [ -n "$JS_DIRS" ]; then
|
|
142
204
|
skip build "excluded - a full app build is too slow for a push gate"
|
|
143
|
-
for
|
|
144
|
-
label
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
205
|
+
for d in $JS_DIRS; do
|
|
206
|
+
# Name the package in the label when there is more than one, so a failure
|
|
207
|
+
# says WHERE. A single unlabelled "lint" across three packages is how you
|
|
208
|
+
# end up fixing the wrong one.
|
|
209
|
+
suffix=""
|
|
210
|
+
[ "$d" != "." ] && suffix="(${d#./})"
|
|
211
|
+
for s in lint typecheck format:check test; do
|
|
212
|
+
label="$(printf '%s' "$s" | tr -d ':')$suffix"
|
|
213
|
+
if have_script "$s" "$d"; then
|
|
214
|
+
if [ "$d" = "." ]; then
|
|
215
|
+
run_check "$label" pnpm run "$s"
|
|
216
|
+
else
|
|
217
|
+
run_check "$label" pnpm --dir "$d" run "$s"
|
|
218
|
+
fi
|
|
219
|
+
else
|
|
220
|
+
skip "$label" "no \"$s\" script"
|
|
221
|
+
fi
|
|
222
|
+
done
|
|
150
223
|
done
|
|
151
224
|
else
|
|
152
|
-
skip javascript "no package.json in this repo"
|
|
225
|
+
skip javascript "no package.json anywhere in this repo"
|
|
153
226
|
fi
|
|
154
227
|
|
|
228
|
+
[ -n "$LIST" ] && exit 0
|
|
229
|
+
|
|
155
230
|
printf '\n'
|
|
156
231
|
if [ -n "$FAILED" ]; then
|
|
157
232
|
printf '\033[31mverify failed:\033[0m%s\n' "$FAILED"
|
|
@@ -48,11 +48,28 @@
|
|
|
48
48
|
#
|
|
49
49
|
# Usage:
|
|
50
50
|
# sh scripts/verify.sh # everything applicable to this repo
|
|
51
|
+
# sh scripts/verify.sh --list # print the checks it WOULD run, and stop
|
|
51
52
|
# pnpm run verify # same
|
|
52
53
|
# BIFFO_SKIP_VERIFY=1 git push # escape hatch, for when you mean it
|
|
54
|
+
#
|
|
55
|
+
# `--list` exists so parity with CI can be tested against what this script
|
|
56
|
+
# actually does, rather than against its source text. The checks are assembled
|
|
57
|
+
# at runtime from what the repo has, so grepping the file for `pnpm run lint`
|
|
58
|
+
# proves nothing -- and a parity test that can be satisfied by a comment is not
|
|
59
|
+
# a parity test.
|
|
60
|
+
#
|
|
61
|
+
# `--list` reports what THIS REPO requires, deliberately ignoring whether the
|
|
62
|
+
# tooling happens to be installed here. Parity with CI is a property of the
|
|
63
|
+
# repository; "can this machine run it" is a property of the machine. Conflating
|
|
64
|
+
# them made the parity test pass locally and fail on a CI runner that has no
|
|
65
|
+
# `uv` or `terraform` -- the gate-green/CI-red split this whole exercise exists
|
|
66
|
+
# to remove, reproduced inside its own guard.
|
|
53
67
|
|
|
54
68
|
set -u
|
|
55
69
|
|
|
70
|
+
LIST=""
|
|
71
|
+
[ "${1:-}" = "--list" ] && LIST=1
|
|
72
|
+
|
|
56
73
|
FAILED=""
|
|
57
74
|
PASSED=""
|
|
58
75
|
SKIPPED=""
|
|
@@ -60,13 +77,56 @@ SKIPPED=""
|
|
|
60
77
|
PYTEST="${BIFFO_VERIFY_PYTEST:-}"
|
|
61
78
|
|
|
62
79
|
have_script() {
|
|
63
|
-
[ -f package.json ] || return 1
|
|
64
|
-
|
|
80
|
+
[ -f "$2/package.json" ] || return 1
|
|
81
|
+
# grep rather than node: --list must work on a machine with no toolchain at
|
|
82
|
+
# all, because what it reports is a property of the repo, not of the machine.
|
|
83
|
+
# Deliberately NOT anchored to line start: that only matches a pretty-printed
|
|
84
|
+
# package.json, and a minified one would silently report "no lint script" --
|
|
85
|
+
# a skip that looks like a considered decision. A false positive here costs a
|
|
86
|
+
# loud `pnpm run` failure; a false negative costs an unchecked push.
|
|
87
|
+
grep -qE "\"$1\"[[:space:]]*:" "$2/package.json"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Every directory holding a JS package this repo owns.
|
|
91
|
+
#
|
|
92
|
+
# A repo with a root package.json is a workspace: `turbo run lint` fans out and
|
|
93
|
+
# running per-package as well would double the work. A repo WITHOUT one keeps
|
|
94
|
+
# its JS in subdirectories -- web/ and web-admin/ in the plugin repos,
|
|
95
|
+
# apps/frontend/ in the siblings -- and their CI runs the same scripts there
|
|
96
|
+
# with `working-directory:`.
|
|
97
|
+
#
|
|
98
|
+
# ## Why this exists (#852)
|
|
99
|
+
#
|
|
100
|
+
# The gate used to check the repo root and nothing else. In the ten repos with
|
|
101
|
+
# no root package.json -- every plugin, every sibling, both runner repos -- it
|
|
102
|
+
# printed `javascript n/a - no package.json in this repo` and then
|
|
103
|
+
# `verify passed`, on repos whose entire frontend is JS. A 100% TypeScript
|
|
104
|
+
# change pushed green with zero JavaScript verification.
|
|
105
|
+
#
|
|
106
|
+
# That is worse than the missing hooks this gate was built to fix. A repo with
|
|
107
|
+
# no hooks makes no claim; this one claimed to have checked. And the standard
|
|
108
|
+
# it was written to enforce says exactly that inapplicable and absent must not
|
|
109
|
+
# look the same -- while reporting "not applicable" for the language the change
|
|
110
|
+
# was written in.
|
|
111
|
+
js_dirs() {
|
|
112
|
+
if [ -f package.json ]; then
|
|
113
|
+
echo "."
|
|
114
|
+
return
|
|
115
|
+
fi
|
|
116
|
+
find . -name package.json \
|
|
117
|
+
-not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/.next/*" \
|
|
118
|
+
-not -path "*/.turbo/*" -not -path "*/.worktrees/*" -not -path "*/out/*" \
|
|
119
|
+
-not -path "*/coverage/*" -not -path "*/.venv/*" 2>/dev/null |
|
|
120
|
+
sed 's|/package.json$||' | sort
|
|
65
121
|
}
|
|
66
122
|
|
|
67
123
|
run_check() {
|
|
68
124
|
name="$1"
|
|
69
125
|
shift
|
|
126
|
+
if [ -n "$LIST" ]; then
|
|
127
|
+
echo "$*"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
70
130
|
start=$(date +%s)
|
|
71
131
|
if "$@" >"/tmp/biffo-verify.$$" 2>&1; then
|
|
72
132
|
PASSED="$PASSED $name"
|
|
@@ -80,16 +140,17 @@ run_check() {
|
|
|
80
140
|
}
|
|
81
141
|
|
|
82
142
|
skip() {
|
|
143
|
+
[ -n "$LIST" ] && return 0
|
|
83
144
|
SKIPPED="$SKIPPED $1"
|
|
84
145
|
printf ' \033[90m-- %-16s n/a - %s\033[0m\n' "$1" "$2"
|
|
85
146
|
}
|
|
86
147
|
|
|
87
|
-
printf '\nverify - the checks CI runs, before the push\n\n'
|
|
148
|
+
[ -n "$LIST" ] || printf '\nverify - the checks CI runs, before the push\n\n'
|
|
88
149
|
|
|
89
150
|
# Python first: ruff is near-instant, so the cheapest feedback on the largest
|
|
90
151
|
# single class of failure comes back immediately.
|
|
91
152
|
if [ -f pyproject.toml ]; then
|
|
92
|
-
if command -v uv >/dev/null 2>&1; then
|
|
153
|
+
if [ -n "$LIST" ] || command -v uv >/dev/null 2>&1; then
|
|
93
154
|
run_check ruff-check uv run ruff check .
|
|
94
155
|
run_check ruff-format uv run ruff format --check .
|
|
95
156
|
run_check pyright uv run pyright
|
|
@@ -107,7 +168,7 @@ fi
|
|
|
107
168
|
|
|
108
169
|
# Terraform, wherever this repo keeps it: modules/ in the template and
|
|
109
170
|
# instances, infra/ and modules/ in siblings.
|
|
110
|
-
if command -v terraform >/dev/null 2>&1; then
|
|
171
|
+
if [ -n "$LIST" ] || command -v terraform >/dev/null 2>&1; then
|
|
111
172
|
# Scope must match this repo's CI, not exceed it. The template and instances
|
|
112
173
|
# deliberately fmt-check modules/ ONLY: infra/environments/ is user-owned, and
|
|
113
174
|
# a template-shipped check asserting over paths the template does not own is
|
|
@@ -138,20 +199,34 @@ fi
|
|
|
138
199
|
|
|
139
200
|
# JS, cheapest first; `test` last because it is slowest and the most likely to
|
|
140
201
|
# be interrupted by an impatient reader.
|
|
141
|
-
|
|
202
|
+
JS_DIRS=$(js_dirs)
|
|
203
|
+
if [ -n "$JS_DIRS" ]; then
|
|
142
204
|
skip build "excluded - a full app build is too slow for a push gate"
|
|
143
|
-
for
|
|
144
|
-
label
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
205
|
+
for d in $JS_DIRS; do
|
|
206
|
+
# Name the package in the label when there is more than one, so a failure
|
|
207
|
+
# says WHERE. A single unlabelled "lint" across three packages is how you
|
|
208
|
+
# end up fixing the wrong one.
|
|
209
|
+
suffix=""
|
|
210
|
+
[ "$d" != "." ] && suffix="(${d#./})"
|
|
211
|
+
for s in lint typecheck format:check test; do
|
|
212
|
+
label="$(printf '%s' "$s" | tr -d ':')$suffix"
|
|
213
|
+
if have_script "$s" "$d"; then
|
|
214
|
+
if [ "$d" = "." ]; then
|
|
215
|
+
run_check "$label" pnpm run "$s"
|
|
216
|
+
else
|
|
217
|
+
run_check "$label" pnpm --dir "$d" run "$s"
|
|
218
|
+
fi
|
|
219
|
+
else
|
|
220
|
+
skip "$label" "no \"$s\" script"
|
|
221
|
+
fi
|
|
222
|
+
done
|
|
150
223
|
done
|
|
151
224
|
else
|
|
152
|
-
skip javascript "no package.json in this repo"
|
|
225
|
+
skip javascript "no package.json anywhere in this repo"
|
|
153
226
|
fi
|
|
154
227
|
|
|
228
|
+
[ -n "$LIST" ] && exit 0
|
|
229
|
+
|
|
155
230
|
printf '\n'
|
|
156
231
|
if [ -n "$FAILED" ]; then
|
|
157
232
|
printf '\033[31mverify failed:\033[0m%s\n' "$FAILED"
|