@biffo/cli 0.171.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
- node -e "process.exit(JSON.parse(require('fs').readFileSync('package.json','utf8')).scripts?.['$1']?0:1)" 2>/dev/null
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
- if [ -f package.json ]; then
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 s in lint typecheck format:check test; do
144
- label=$(printf '%s' "$s" | tr -d ':')
145
- if have_script "$s"; then
146
- run_check "$label" pnpm run "$s"
147
- else
148
- skip "$label" "no \"$s\" script in package.json"
149
- fi
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
- node -e "process.exit(JSON.parse(require('fs').readFileSync('package.json','utf8')).scripts?.['$1']?0:1)" 2>/dev/null
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
- if [ -f package.json ]; then
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 s in lint typecheck format:check test; do
144
- label=$(printf '%s' "$s" | tr -d ':')
145
- if have_script "$s"; then
146
- run_check "$label" pnpm run "$s"
147
- else
148
- skip "$label" "no \"$s\" script in package.json"
149
- fi
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.171.0",
3
+ "version": "0.172.1",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",