@biffo/cli 0.258.2 → 0.258.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.258.2",
3
+ "version": "0.258.3",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -250,10 +250,28 @@ if [ "$BIFFO_PG_REAP_HOURS" -gt 0 ] 2>/dev/null && command -v docker >/dev/null
250
250
  say "cannot compute a reap cutoff on this date(1); skipping container reaping"
251
251
  else
252
252
  _reaped=0
253
- for _c in $(docker ps -a --filter "name=biffo-pg-test-" --format '{{.Names}}' 2>/dev/null); do
253
+ _considered=0
254
+ # TWO filters, not one literal name (#1383). The label is what containers
255
+ # created from here now carry; the name prefix keeps covering every one
256
+ # created before the label existed, which is all of them on any workstation
257
+ # that has run this before. `sort -u` because a container matching both
258
+ # filters is listed by both.
259
+ _reapable=$(
260
+ {
261
+ docker ps -a --filter "name=biffo-pg-test-" --format '{{.Names}}' 2>/dev/null
262
+ docker ps -a --filter "label=biffo.ephemeral=1" --format '{{.Names}}' 2>/dev/null
263
+ } | sort -u
264
+ )
265
+ # A space-separated copy, purely for the `case` membership test below. The
266
+ # newline-separated form cannot be tested with *" $_c "* -- the separator is
267
+ # a newline, so only the first and last entries would ever match, and every
268
+ # container this script owns would be re-reported as one it does not.
269
+ _reapable_flat=" $(printf '%s ' $_reapable)"
270
+ for _c in $_reapable; do
254
271
  [ "$_c" = "$CONTAINER" ] && continue
255
272
  _made=$(docker inspect -f '{{.Created}}' "$_c" 2>/dev/null | cut -c1-19)
256
273
  [ -z "$_made" ] && continue
274
+ _considered=$((_considered + 1))
257
275
  # Both are UTC ISO-8601 to the second, so a string compare IS a time
258
276
  # compare -- no epoch conversion, and portable across date(1) flavours.
259
277
  if awk -v a="$_made" -v b="$_reap_cutoff" 'BEGIN { exit !(a < b) }'; then
@@ -261,7 +279,60 @@ if [ "$BIFFO_PG_REAP_HOURS" -gt 0 ] 2>/dev/null && command -v docker >/dev/null
261
279
  fi
262
280
  done
263
281
  [ "$_reaped" -gt 0 ] &&
264
- say "reaped $_reaped container(s) unused for over ${BIFFO_PG_REAP_HOURS}h (set BIFFO_PG_REAP_HOURS=0 to disable)"
282
+ say "reaped $_reaped of $_considered container(s) unused for over ${BIFFO_PG_REAP_HOURS}h (set BIFFO_PG_REAP_HOURS=0 to disable)"
283
+
284
+ # ── What the reaper can SEE but must not touch ──────────────────────────
285
+ #
286
+ # The defect in #1383 was not that stale containers survived; it was that
287
+ # they were outside the denominator, so a clean reaper run was a true
288
+ # statement about a set that silently excluded them. Three Postgres
289
+ # containers up for DAYS -- one of them six -- sat beside a perfectly tidy
290
+ # `biffo-pg-test-*` family, and a `tabsii-rls-local` two days stale is
291
+ # already measured in docs/guides/development-practices.md as costing ~20m
292
+ # of chasing failures that belonged to nobody.
293
+ #
294
+ # They are REPORTED, never removed, and that is a deliberate departure from
295
+ # the issue's preferred fix ("reap any container on the Postgres test image
296
+ # with no client connections"). The reaper's own licence to delete rests on
297
+ # "reaping something still wanted is cheap and self-correcting" -- true only
298
+ # of containers THIS script creates, because this script recreates them. A
299
+ # hand-run container may hold hand-loaded data that nothing here can
300
+ # reconstruct, and an idle one is exactly the case a connection check
301
+ # cannot distinguish from an abandoned one. Widening the deletion set to
302
+ # every `postgres:`/`postgis:` image would trade an invisible mess for an
303
+ # unrecoverable one.
304
+ #
305
+ # So: name them, price them, and let a human decide. That is what would have
306
+ # made this visible six days earlier instead of during an unrelated cleanup.
307
+ # One `docker ps` for names AND images, so the image filter costs nothing;
308
+ # only the handful that survive it are worth an `inspect` for the timestamp.
309
+ # This runs on every pg-lane invocation, so a per-container inspect over the
310
+ # whole daemon would put ~1s on a hot path to print a housekeeping note.
311
+ # Neither field can contain a space, so a space separator needs no IFS
312
+ # games -- and `IFS=$'\t'` would not have worked anyway: these scripts run
313
+ # under dash, where that splits on the four characters $ ' \ t.
314
+ _unclaimed=''
315
+ docker ps -a --format '{{.Names}} {{.Image}}' 2>/dev/null | {
316
+ while read -r _c _img; do
317
+ [ "$_c" = "$CONTAINER" ] && continue
318
+ case "$_reapable_flat" in *" $_c "*) continue ;; esac
319
+ # Both images this script can choose (see IMAGE above), matched by
320
+ # repository rather than tag so a BIFFO_PG_IMAGE override still lands.
321
+ case "$_img" in postgres:* | postgis/*) ;; *) continue ;; esac
322
+ _made=$(docker inspect -f '{{.Created}}' "$_c" 2>/dev/null | cut -c1-19)
323
+ [ -z "$_made" ] && continue
324
+ awk -v a="$_made" -v b="$_reap_cutoff" 'BEGIN { exit !(a < b) }' &&
325
+ _unclaimed="$_unclaimed $_c($_img)"
326
+ done
327
+ # Reported inside the subshell the pipeline created: `_unclaimed` set in a
328
+ # piped `while` does not survive it in a POSIX shell.
329
+ if [ -n "$_unclaimed" ]; then
330
+ say "NOT reaped -- Postgres containers over ${BIFFO_PG_REAP_HOURS}h old that this script did not create:"
331
+ for _u in $_unclaimed; do say " $_u"; done
332
+ say " A stale one costs test failures that belong to nobody (#1383). Remove by hand"
333
+ say " (docker rm -f <name>), or start it with --label biffo.ephemeral=1 to have it reaped."
334
+ fi
335
+ }
265
336
  fi
266
337
  fi
267
338
 
@@ -276,7 +347,12 @@ if ! psql_admin -c 'SELECT 1' >/dev/null 2>&1; then
276
347
  docker start "$CONTAINER" >/dev/null
277
348
  else
278
349
  say "creating container $CONTAINER ($IMAGE) on port $PORT"
279
- docker run -d --name "$CONTAINER" \
350
+ # The label is what makes this container reapable by a property rather than
351
+ # by the name it happens to carry (#1383). The name prefix still works and
352
+ # is still scanned, but it only ever described containers this script named;
353
+ # anything started under another name was outside the reaper's denominator
354
+ # entirely. A label travels with the container whatever it is called.
355
+ docker run -d --name "$CONTAINER" --label biffo.ephemeral=1 \
280
356
  -e POSTGRES_PASSWORD="$PASS" -p "$PORT:5432" "$IMAGE" >/dev/null
281
357
  fi
282
358
  # Polled, not slept: a cold image pull and a warm restart differ by an order of