@bensandee/tooling 0.50.2 → 0.51.0
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/README.md +45 -20
- package/dist/bin.mjs +109 -93
- package/dist/{check-hutCTIXe.mjs → check-COzp4S_Q.mjs} +25 -0
- package/dist/docker-check/index.d.mts +5 -1
- package/dist/docker-check/index.mjs +1 -1
- package/package.json +4 -4
- package/tooling.schema.json +11 -0
package/README.md
CHANGED
|
@@ -126,10 +126,11 @@ The generated release workflow exposes these as optional `workflow_dispatch` inp
|
|
|
126
126
|
|
|
127
127
|
### Docker
|
|
128
128
|
|
|
129
|
-
| Command | Description
|
|
130
|
-
| ------------------------ |
|
|
131
|
-
| `tooling docker:build` | Build Docker images for discovered Docker packages.
|
|
132
|
-
| `tooling docker:publish` | Build, tag, and push Docker images to a registry.
|
|
129
|
+
| Command | Description |
|
|
130
|
+
| ------------------------ | ------------------------------------------------------------------- |
|
|
131
|
+
| `tooling docker:build` | Build Docker images for discovered Docker packages. |
|
|
132
|
+
| `tooling docker:publish` | Build, tag, and push Docker images to a registry. |
|
|
133
|
+
| `tooling docker:check` | Start a Compose stack, run health checks, run smoketest, tear down. |
|
|
133
134
|
|
|
134
135
|
Docker packages are discovered automatically. Any package with a `Dockerfile` or `docker/Dockerfile` is a Docker package. Image names are derived as `{root-package-name}-{package-name}`, build context defaults to `.` (project root). For single-package repos, `Dockerfile` or `docker/Dockerfile` at the project root is checked.
|
|
135
136
|
|
|
@@ -255,9 +256,32 @@ import type {
|
|
|
255
256
|
|
|
256
257
|
## Docker check
|
|
257
258
|
|
|
258
|
-
|
|
259
|
+
`docker:check` verifies that a Docker Compose stack starts correctly, passes health checks, and (optionally) survives a smoketest command. The lifecycle is: build images, compose up, poll health checks, run smoketest, compose down.
|
|
259
260
|
|
|
260
|
-
###
|
|
261
|
+
### Smoketests
|
|
262
|
+
|
|
263
|
+
A smoketest is a command that runs against the live Docker stack after all containers are healthy and HTTP health checks pass. This is useful for running integration tests, API contract checks, or any validation that requires the full stack to be running.
|
|
264
|
+
|
|
265
|
+
**Auto-detection:** If the root `package.json` has a `"test:smoke"` script, it is automatically used as the smoketest command — no configuration needed.
|
|
266
|
+
|
|
267
|
+
**Manual configuration:** For non-conventional command names, configure via `.tooling.json`:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"dockerCheck": {
|
|
272
|
+
"smoketest": ["pnpm", "test:integration"],
|
|
273
|
+
"smoketestCwd": "."
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
If the smoketest command exits non-zero, `docker:check` dumps container logs and fails with reason `"smoketest-failed"`.
|
|
279
|
+
|
|
280
|
+
### Library API
|
|
281
|
+
|
|
282
|
+
The `@bensandee/tooling/docker-check` export provides utilities for checking Docker Compose stacks programmatically.
|
|
283
|
+
|
|
284
|
+
#### Quick start
|
|
261
285
|
|
|
262
286
|
```ts
|
|
263
287
|
import { createRealExecutor, runDockerCheck } from "@bensandee/tooling/docker-check";
|
|
@@ -269,7 +293,7 @@ const config: CheckConfig = {
|
|
|
269
293
|
composeFiles: ["docker-compose.yaml"],
|
|
270
294
|
services: ["api", "db"],
|
|
271
295
|
},
|
|
272
|
-
buildCommand: "pnpm image:build",
|
|
296
|
+
buildCommand: ["pnpm", "image:build"],
|
|
273
297
|
healthChecks: [
|
|
274
298
|
{
|
|
275
299
|
name: "API",
|
|
@@ -277,6 +301,7 @@ const config: CheckConfig = {
|
|
|
277
301
|
validate: async (res) => res.ok,
|
|
278
302
|
},
|
|
279
303
|
],
|
|
304
|
+
smoketest: ["pnpm", "test:smoke"],
|
|
280
305
|
timeoutMs: 120_000,
|
|
281
306
|
pollIntervalMs: 5_000,
|
|
282
307
|
};
|
|
@@ -287,24 +312,24 @@ if (!result.success) {
|
|
|
287
312
|
}
|
|
288
313
|
```
|
|
289
314
|
|
|
290
|
-
|
|
315
|
+
#### Exports
|
|
291
316
|
|
|
292
|
-
| Export | Description
|
|
293
|
-
| -------------------------------------- |
|
|
294
|
-
| `runDockerCheck(executor, config)` | Full lifecycle: build, compose up, health check polling, teardown |
|
|
295
|
-
| `createRealExecutor()` | Production executor (real shell, fetch, timers)
|
|
296
|
-
| `composeUp(executor, config)` | Start compose services
|
|
297
|
-
| `composeDown(executor, config)` | Stop and remove compose services
|
|
298
|
-
| `composeLogs(executor, config)` | Stream compose logs
|
|
299
|
-
| `composePs(executor, config)` | List running containers
|
|
300
|
-
| `checkHttpHealth(executor, check)` | Run a single HTTP health check
|
|
301
|
-
| `getContainerHealth(executor, config)` | Check container-level health status
|
|
317
|
+
| Export | Description |
|
|
318
|
+
| -------------------------------------- | ---------------------------------------------------------------------------- |
|
|
319
|
+
| `runDockerCheck(executor, config)` | Full lifecycle: build, compose up, health check polling, smoketest, teardown |
|
|
320
|
+
| `createRealExecutor()` | Production executor (real shell, fetch, timers) |
|
|
321
|
+
| `composeUp(executor, config)` | Start compose services |
|
|
322
|
+
| `composeDown(executor, config)` | Stop and remove compose services |
|
|
323
|
+
| `composeLogs(executor, config)` | Stream compose logs |
|
|
324
|
+
| `composePs(executor, config)` | List running containers |
|
|
325
|
+
| `checkHttpHealth(executor, check)` | Run a single HTTP health check |
|
|
326
|
+
| `getContainerHealth(executor, config)` | Check container-level health status |
|
|
302
327
|
|
|
303
|
-
|
|
328
|
+
#### Types
|
|
304
329
|
|
|
305
330
|
| Type | Description |
|
|
306
331
|
| --------------------- | ------------------------------------------------------------------------------------------ |
|
|
307
|
-
| `CheckConfig` | Full check config (compose settings, build command, health checks, timeouts)
|
|
332
|
+
| `CheckConfig` | Full check config (compose settings, build command, health checks, smoketest, timeouts) |
|
|
308
333
|
| `ComposeConfig` | Docker Compose settings (cwd, compose files, env file, services) |
|
|
309
334
|
| `HttpHealthCheck` | Health check definition (name, URL, validate function) |
|
|
310
335
|
| `CheckResult` | Result: `{ success: true, elapsedMs }` or `{ success: false, reason, message, elapsedMs }` |
|
package/dist/bin.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { d as debug, f as debugExec, g as note, h as log, l as createRealExecutor$1, m as redact, p as isEnvVerbose, t as runDockerCheck, u as isExecSyncError } from "./check-
|
|
2
|
+
import { d as debug, f as debugExec, g as note, h as log, l as createRealExecutor$1, m as redact, p as isEnvVerbose, t as runDockerCheck, u as isExecSyncError } from "./check-COzp4S_Q.mjs";
|
|
3
3
|
import { defineCommand, runMain } from "citty";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
import { isCancel, select } from "@clack/prompts";
|
|
@@ -590,6 +590,8 @@ const DockerCheckConfigSchema = z.object({
|
|
|
590
590
|
healthChecks: z.array(DeclarativeHealthCheckSchema).optional().meta({ description: "Health check definitions" }),
|
|
591
591
|
buildCommand: z.array(z.string()).optional().meta({ description: "Command to build images before checking (e.g. [\"pnpm\", \"image:build\"])" }),
|
|
592
592
|
buildCwd: z.string().optional().meta({ description: "Working directory for build command" }),
|
|
593
|
+
smoketest: z.array(z.string()).optional().meta({ description: "Command to run as a smoketest after health checks pass (e.g. [\"pnpm\", \"test:smoke\"])" }),
|
|
594
|
+
smoketestCwd: z.string().optional().meta({ description: "Working directory for smoketest command" }),
|
|
593
595
|
timeoutMs: z.number().int().positive().optional().meta({ description: "Overall timeout in milliseconds" }),
|
|
594
596
|
pollIntervalMs: z.number().int().positive().optional().meta({ description: "Poll interval in milliseconds" })
|
|
595
597
|
});
|
|
@@ -1369,15 +1371,15 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1369
1371
|
"@release-it/bumper": "7.0.5",
|
|
1370
1372
|
"@types/node": "24.12.0",
|
|
1371
1373
|
"commit-and-tag-version": "12.7.1",
|
|
1372
|
-
"knip": "6.3.
|
|
1373
|
-
"lefthook": "2.1.
|
|
1374
|
-
"oxfmt": "0.
|
|
1375
|
-
"oxlint": "1.
|
|
1376
|
-
"prettier": "3.8.
|
|
1374
|
+
"knip": "6.3.1",
|
|
1375
|
+
"lefthook": "2.1.5",
|
|
1376
|
+
"oxfmt": "0.44.0",
|
|
1377
|
+
"oxlint": "1.59.0",
|
|
1378
|
+
"prettier": "3.8.2",
|
|
1377
1379
|
"release-it": "19.2.4",
|
|
1378
1380
|
"tsdown": "0.21.7",
|
|
1379
1381
|
"typescript": "6.0.2",
|
|
1380
|
-
"vitest": "4.1.
|
|
1382
|
+
"vitest": "4.1.4",
|
|
1381
1383
|
"pnpm": "10.33.0"
|
|
1382
1384
|
}["@types/node"],
|
|
1383
1385
|
tsdown: {
|
|
@@ -1385,15 +1387,15 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1385
1387
|
"@release-it/bumper": "7.0.5",
|
|
1386
1388
|
"@types/node": "24.12.0",
|
|
1387
1389
|
"commit-and-tag-version": "12.7.1",
|
|
1388
|
-
"knip": "6.3.
|
|
1389
|
-
"lefthook": "2.1.
|
|
1390
|
-
"oxfmt": "0.
|
|
1391
|
-
"oxlint": "1.
|
|
1392
|
-
"prettier": "3.8.
|
|
1390
|
+
"knip": "6.3.1",
|
|
1391
|
+
"lefthook": "2.1.5",
|
|
1392
|
+
"oxfmt": "0.44.0",
|
|
1393
|
+
"oxlint": "1.59.0",
|
|
1394
|
+
"prettier": "3.8.2",
|
|
1393
1395
|
"release-it": "19.2.4",
|
|
1394
1396
|
"tsdown": "0.21.7",
|
|
1395
1397
|
"typescript": "6.0.2",
|
|
1396
|
-
"vitest": "4.1.
|
|
1398
|
+
"vitest": "4.1.4",
|
|
1397
1399
|
"pnpm": "10.33.0"
|
|
1398
1400
|
}["tsdown"],
|
|
1399
1401
|
typescript: {
|
|
@@ -1401,15 +1403,15 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1401
1403
|
"@release-it/bumper": "7.0.5",
|
|
1402
1404
|
"@types/node": "24.12.0",
|
|
1403
1405
|
"commit-and-tag-version": "12.7.1",
|
|
1404
|
-
"knip": "6.3.
|
|
1405
|
-
"lefthook": "2.1.
|
|
1406
|
-
"oxfmt": "0.
|
|
1407
|
-
"oxlint": "1.
|
|
1408
|
-
"prettier": "3.8.
|
|
1406
|
+
"knip": "6.3.1",
|
|
1407
|
+
"lefthook": "2.1.5",
|
|
1408
|
+
"oxfmt": "0.44.0",
|
|
1409
|
+
"oxlint": "1.59.0",
|
|
1410
|
+
"prettier": "3.8.2",
|
|
1409
1411
|
"release-it": "19.2.4",
|
|
1410
1412
|
"tsdown": "0.21.7",
|
|
1411
1413
|
"typescript": "6.0.2",
|
|
1412
|
-
"vitest": "4.1.
|
|
1414
|
+
"vitest": "4.1.4",
|
|
1413
1415
|
"pnpm": "10.33.0"
|
|
1414
1416
|
}["typescript"],
|
|
1415
1417
|
vitest: {
|
|
@@ -1417,15 +1419,15 @@ const PER_PACKAGE_DEV_DEPS = {
|
|
|
1417
1419
|
"@release-it/bumper": "7.0.5",
|
|
1418
1420
|
"@types/node": "24.12.0",
|
|
1419
1421
|
"commit-and-tag-version": "12.7.1",
|
|
1420
|
-
"knip": "6.3.
|
|
1421
|
-
"lefthook": "2.1.
|
|
1422
|
-
"oxfmt": "0.
|
|
1423
|
-
"oxlint": "1.
|
|
1424
|
-
"prettier": "3.8.
|
|
1422
|
+
"knip": "6.3.1",
|
|
1423
|
+
"lefthook": "2.1.5",
|
|
1424
|
+
"oxfmt": "0.44.0",
|
|
1425
|
+
"oxlint": "1.59.0",
|
|
1426
|
+
"prettier": "3.8.2",
|
|
1425
1427
|
"release-it": "19.2.4",
|
|
1426
1428
|
"tsdown": "0.21.7",
|
|
1427
1429
|
"typescript": "6.0.2",
|
|
1428
|
-
"vitest": "4.1.
|
|
1430
|
+
"vitest": "4.1.4",
|
|
1429
1431
|
"pnpm": "10.33.0"
|
|
1430
1432
|
}["vitest"]
|
|
1431
1433
|
};
|
|
@@ -1436,15 +1438,15 @@ const ROOT_DEV_DEPS = {
|
|
|
1436
1438
|
"@release-it/bumper": "7.0.5",
|
|
1437
1439
|
"@types/node": "24.12.0",
|
|
1438
1440
|
"commit-and-tag-version": "12.7.1",
|
|
1439
|
-
"knip": "6.3.
|
|
1440
|
-
"lefthook": "2.1.
|
|
1441
|
-
"oxfmt": "0.
|
|
1442
|
-
"oxlint": "1.
|
|
1443
|
-
"prettier": "3.8.
|
|
1441
|
+
"knip": "6.3.1",
|
|
1442
|
+
"lefthook": "2.1.5",
|
|
1443
|
+
"oxfmt": "0.44.0",
|
|
1444
|
+
"oxlint": "1.59.0",
|
|
1445
|
+
"prettier": "3.8.2",
|
|
1444
1446
|
"release-it": "19.2.4",
|
|
1445
1447
|
"tsdown": "0.21.7",
|
|
1446
1448
|
"typescript": "6.0.2",
|
|
1447
|
-
"vitest": "4.1.
|
|
1449
|
+
"vitest": "4.1.4",
|
|
1448
1450
|
"pnpm": "10.33.0"
|
|
1449
1451
|
}["knip"],
|
|
1450
1452
|
lefthook: {
|
|
@@ -1452,15 +1454,15 @@ const ROOT_DEV_DEPS = {
|
|
|
1452
1454
|
"@release-it/bumper": "7.0.5",
|
|
1453
1455
|
"@types/node": "24.12.0",
|
|
1454
1456
|
"commit-and-tag-version": "12.7.1",
|
|
1455
|
-
"knip": "6.3.
|
|
1456
|
-
"lefthook": "2.1.
|
|
1457
|
-
"oxfmt": "0.
|
|
1458
|
-
"oxlint": "1.
|
|
1459
|
-
"prettier": "3.8.
|
|
1457
|
+
"knip": "6.3.1",
|
|
1458
|
+
"lefthook": "2.1.5",
|
|
1459
|
+
"oxfmt": "0.44.0",
|
|
1460
|
+
"oxlint": "1.59.0",
|
|
1461
|
+
"prettier": "3.8.2",
|
|
1460
1462
|
"release-it": "19.2.4",
|
|
1461
1463
|
"tsdown": "0.21.7",
|
|
1462
1464
|
"typescript": "6.0.2",
|
|
1463
|
-
"vitest": "4.1.
|
|
1465
|
+
"vitest": "4.1.4",
|
|
1464
1466
|
"pnpm": "10.33.0"
|
|
1465
1467
|
}["lefthook"],
|
|
1466
1468
|
oxlint: {
|
|
@@ -1468,15 +1470,15 @@ const ROOT_DEV_DEPS = {
|
|
|
1468
1470
|
"@release-it/bumper": "7.0.5",
|
|
1469
1471
|
"@types/node": "24.12.0",
|
|
1470
1472
|
"commit-and-tag-version": "12.7.1",
|
|
1471
|
-
"knip": "6.3.
|
|
1472
|
-
"lefthook": "2.1.
|
|
1473
|
-
"oxfmt": "0.
|
|
1474
|
-
"oxlint": "1.
|
|
1475
|
-
"prettier": "3.8.
|
|
1473
|
+
"knip": "6.3.1",
|
|
1474
|
+
"lefthook": "2.1.5",
|
|
1475
|
+
"oxfmt": "0.44.0",
|
|
1476
|
+
"oxlint": "1.59.0",
|
|
1477
|
+
"prettier": "3.8.2",
|
|
1476
1478
|
"release-it": "19.2.4",
|
|
1477
1479
|
"tsdown": "0.21.7",
|
|
1478
1480
|
"typescript": "6.0.2",
|
|
1479
|
-
"vitest": "4.1.
|
|
1481
|
+
"vitest": "4.1.4",
|
|
1480
1482
|
"pnpm": "10.33.0"
|
|
1481
1483
|
}["oxlint"]
|
|
1482
1484
|
};
|
|
@@ -1509,15 +1511,15 @@ function addReleaseDeps(deps, config) {
|
|
|
1509
1511
|
"@release-it/bumper": "7.0.5",
|
|
1510
1512
|
"@types/node": "24.12.0",
|
|
1511
1513
|
"commit-and-tag-version": "12.7.1",
|
|
1512
|
-
"knip": "6.3.
|
|
1513
|
-
"lefthook": "2.1.
|
|
1514
|
-
"oxfmt": "0.
|
|
1515
|
-
"oxlint": "1.
|
|
1516
|
-
"prettier": "3.8.
|
|
1514
|
+
"knip": "6.3.1",
|
|
1515
|
+
"lefthook": "2.1.5",
|
|
1516
|
+
"oxfmt": "0.44.0",
|
|
1517
|
+
"oxlint": "1.59.0",
|
|
1518
|
+
"prettier": "3.8.2",
|
|
1517
1519
|
"release-it": "19.2.4",
|
|
1518
1520
|
"tsdown": "0.21.7",
|
|
1519
1521
|
"typescript": "6.0.2",
|
|
1520
|
-
"vitest": "4.1.
|
|
1522
|
+
"vitest": "4.1.4",
|
|
1521
1523
|
"pnpm": "10.33.0"
|
|
1522
1524
|
}["release-it"];
|
|
1523
1525
|
if (config.structure === "monorepo") deps["@release-it/bumper"] = {
|
|
@@ -1525,15 +1527,15 @@ function addReleaseDeps(deps, config) {
|
|
|
1525
1527
|
"@release-it/bumper": "7.0.5",
|
|
1526
1528
|
"@types/node": "24.12.0",
|
|
1527
1529
|
"commit-and-tag-version": "12.7.1",
|
|
1528
|
-
"knip": "6.3.
|
|
1529
|
-
"lefthook": "2.1.
|
|
1530
|
-
"oxfmt": "0.
|
|
1531
|
-
"oxlint": "1.
|
|
1532
|
-
"prettier": "3.8.
|
|
1530
|
+
"knip": "6.3.1",
|
|
1531
|
+
"lefthook": "2.1.5",
|
|
1532
|
+
"oxfmt": "0.44.0",
|
|
1533
|
+
"oxlint": "1.59.0",
|
|
1534
|
+
"prettier": "3.8.2",
|
|
1533
1535
|
"release-it": "19.2.4",
|
|
1534
1536
|
"tsdown": "0.21.7",
|
|
1535
1537
|
"typescript": "6.0.2",
|
|
1536
|
-
"vitest": "4.1.
|
|
1538
|
+
"vitest": "4.1.4",
|
|
1537
1539
|
"pnpm": "10.33.0"
|
|
1538
1540
|
}["@release-it/bumper"];
|
|
1539
1541
|
break;
|
|
@@ -1543,15 +1545,15 @@ function addReleaseDeps(deps, config) {
|
|
|
1543
1545
|
"@release-it/bumper": "7.0.5",
|
|
1544
1546
|
"@types/node": "24.12.0",
|
|
1545
1547
|
"commit-and-tag-version": "12.7.1",
|
|
1546
|
-
"knip": "6.3.
|
|
1547
|
-
"lefthook": "2.1.
|
|
1548
|
-
"oxfmt": "0.
|
|
1549
|
-
"oxlint": "1.
|
|
1550
|
-
"prettier": "3.8.
|
|
1548
|
+
"knip": "6.3.1",
|
|
1549
|
+
"lefthook": "2.1.5",
|
|
1550
|
+
"oxfmt": "0.44.0",
|
|
1551
|
+
"oxlint": "1.59.0",
|
|
1552
|
+
"prettier": "3.8.2",
|
|
1551
1553
|
"release-it": "19.2.4",
|
|
1552
1554
|
"tsdown": "0.21.7",
|
|
1553
1555
|
"typescript": "6.0.2",
|
|
1554
|
-
"vitest": "4.1.
|
|
1556
|
+
"vitest": "4.1.4",
|
|
1555
1557
|
"pnpm": "10.33.0"
|
|
1556
1558
|
}["commit-and-tag-version"];
|
|
1557
1559
|
break;
|
|
@@ -1561,15 +1563,15 @@ function addReleaseDeps(deps, config) {
|
|
|
1561
1563
|
"@release-it/bumper": "7.0.5",
|
|
1562
1564
|
"@types/node": "24.12.0",
|
|
1563
1565
|
"commit-and-tag-version": "12.7.1",
|
|
1564
|
-
"knip": "6.3.
|
|
1565
|
-
"lefthook": "2.1.
|
|
1566
|
-
"oxfmt": "0.
|
|
1567
|
-
"oxlint": "1.
|
|
1568
|
-
"prettier": "3.8.
|
|
1566
|
+
"knip": "6.3.1",
|
|
1567
|
+
"lefthook": "2.1.5",
|
|
1568
|
+
"oxfmt": "0.44.0",
|
|
1569
|
+
"oxlint": "1.59.0",
|
|
1570
|
+
"prettier": "3.8.2",
|
|
1569
1571
|
"release-it": "19.2.4",
|
|
1570
1572
|
"tsdown": "0.21.7",
|
|
1571
1573
|
"typescript": "6.0.2",
|
|
1572
|
-
"vitest": "4.1.
|
|
1574
|
+
"vitest": "4.1.4",
|
|
1573
1575
|
"pnpm": "10.33.0"
|
|
1574
1576
|
}["@changesets/cli"];
|
|
1575
1577
|
break;
|
|
@@ -1594,22 +1596,22 @@ async function generatePackageJson(ctx) {
|
|
|
1594
1596
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1595
1597
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1596
1598
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.10.2";
|
|
1597
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1599
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.51.0";
|
|
1598
1600
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.12.0";
|
|
1599
1601
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
|
|
1600
1602
|
"@changesets/cli": "2.30.0",
|
|
1601
1603
|
"@release-it/bumper": "7.0.5",
|
|
1602
1604
|
"@types/node": "24.12.0",
|
|
1603
1605
|
"commit-and-tag-version": "12.7.1",
|
|
1604
|
-
"knip": "6.3.
|
|
1605
|
-
"lefthook": "2.1.
|
|
1606
|
-
"oxfmt": "0.
|
|
1607
|
-
"oxlint": "1.
|
|
1608
|
-
"prettier": "3.8.
|
|
1606
|
+
"knip": "6.3.1",
|
|
1607
|
+
"lefthook": "2.1.5",
|
|
1608
|
+
"oxfmt": "0.44.0",
|
|
1609
|
+
"oxlint": "1.59.0",
|
|
1610
|
+
"prettier": "3.8.2",
|
|
1609
1611
|
"release-it": "19.2.4",
|
|
1610
1612
|
"tsdown": "0.21.7",
|
|
1611
1613
|
"typescript": "6.0.2",
|
|
1612
|
-
"vitest": "4.1.
|
|
1614
|
+
"vitest": "4.1.4",
|
|
1613
1615
|
"pnpm": "10.33.0"
|
|
1614
1616
|
}["oxfmt"];
|
|
1615
1617
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = {
|
|
@@ -1617,15 +1619,15 @@ async function generatePackageJson(ctx) {
|
|
|
1617
1619
|
"@release-it/bumper": "7.0.5",
|
|
1618
1620
|
"@types/node": "24.12.0",
|
|
1619
1621
|
"commit-and-tag-version": "12.7.1",
|
|
1620
|
-
"knip": "6.3.
|
|
1621
|
-
"lefthook": "2.1.
|
|
1622
|
-
"oxfmt": "0.
|
|
1623
|
-
"oxlint": "1.
|
|
1624
|
-
"prettier": "3.8.
|
|
1622
|
+
"knip": "6.3.1",
|
|
1623
|
+
"lefthook": "2.1.5",
|
|
1624
|
+
"oxfmt": "0.44.0",
|
|
1625
|
+
"oxlint": "1.59.0",
|
|
1626
|
+
"prettier": "3.8.2",
|
|
1625
1627
|
"release-it": "19.2.4",
|
|
1626
1628
|
"tsdown": "0.21.7",
|
|
1627
1629
|
"typescript": "6.0.2",
|
|
1628
|
-
"vitest": "4.1.
|
|
1630
|
+
"vitest": "4.1.4",
|
|
1629
1631
|
"pnpm": "10.33.0"
|
|
1630
1632
|
}["prettier"];
|
|
1631
1633
|
addReleaseDeps(devDeps, ctx.config);
|
|
@@ -1711,15 +1713,15 @@ async function generatePackageJson(ctx) {
|
|
|
1711
1713
|
"@release-it/bumper": "7.0.5",
|
|
1712
1714
|
"@types/node": "24.12.0",
|
|
1713
1715
|
"commit-and-tag-version": "12.7.1",
|
|
1714
|
-
"knip": "6.3.
|
|
1715
|
-
"lefthook": "2.1.
|
|
1716
|
-
"oxfmt": "0.
|
|
1717
|
-
"oxlint": "1.
|
|
1718
|
-
"prettier": "3.8.
|
|
1716
|
+
"knip": "6.3.1",
|
|
1717
|
+
"lefthook": "2.1.5",
|
|
1718
|
+
"oxfmt": "0.44.0",
|
|
1719
|
+
"oxlint": "1.59.0",
|
|
1720
|
+
"prettier": "3.8.2",
|
|
1719
1721
|
"release-it": "19.2.4",
|
|
1720
1722
|
"tsdown": "0.21.7",
|
|
1721
1723
|
"typescript": "6.0.2",
|
|
1722
|
-
"vitest": "4.1.
|
|
1724
|
+
"vitest": "4.1.4",
|
|
1723
1725
|
"pnpm": "10.33.0"
|
|
1724
1726
|
}["pnpm"]}`
|
|
1725
1727
|
};
|
|
@@ -2168,7 +2170,7 @@ const REQUIRED_ENTRIES = [
|
|
|
2168
2170
|
const OPTIONAL_ENTRIES = [
|
|
2169
2171
|
".tooling-migrate.md",
|
|
2170
2172
|
".tooling-archived/",
|
|
2171
|
-
".claude/hooks/bst-hook.audit.local.jsonl"
|
|
2173
|
+
".claude/hooks/bst-hook.audit.local.jsonl*"
|
|
2172
2174
|
];
|
|
2173
2175
|
const ALL_ENTRIES = [...REQUIRED_ENTRIES, ...OPTIONAL_ENTRIES];
|
|
2174
2176
|
/** Normalize a gitignore entry for comparison: strip leading `/` and trailing `/`. */
|
|
@@ -3368,7 +3370,7 @@ async function runGenerators(ctx) {
|
|
|
3368
3370
|
function generateMigratePrompt(results, config, detected) {
|
|
3369
3371
|
const sections = [];
|
|
3370
3372
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3371
|
-
sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.
|
|
3373
|
+
sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.51.0 repo:sync\` on ${timestamp}_\n`);
|
|
3372
3374
|
const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
|
|
3373
3375
|
if (changed.length > 0) {
|
|
3374
3376
|
const items = changed.map((r) => {
|
|
@@ -5529,6 +5531,12 @@ function generateCheckOverlay(services) {
|
|
|
5529
5531
|
}
|
|
5530
5532
|
return stringify({ services: serviceOverrides });
|
|
5531
5533
|
}
|
|
5534
|
+
/** Detect a smoketest command from a `test:smoke` script in the root package.json. */
|
|
5535
|
+
function detectSmoketest(cwd) {
|
|
5536
|
+
try {
|
|
5537
|
+
if (parsePackageJson(readFileSync(path.join(cwd, "package.json"), "utf-8"))?.scripts?.["test:smoke"]) return ["pnpm", "test:smoke"];
|
|
5538
|
+
} catch (_error) {}
|
|
5539
|
+
}
|
|
5532
5540
|
/** Auto-detect compose config from conventional file locations. */
|
|
5533
5541
|
function computeCheckDefaults(cwd) {
|
|
5534
5542
|
const { dir, files } = detectComposeFiles(cwd);
|
|
@@ -5538,13 +5546,15 @@ function computeCheckDefaults(cwd) {
|
|
|
5538
5546
|
const envFile = detectCheckEnvFile(dir);
|
|
5539
5547
|
const services = parseComposeServices(dir, files);
|
|
5540
5548
|
const healthChecks = deriveHealthChecks(services);
|
|
5549
|
+
const smoketest = detectSmoketest(cwd);
|
|
5541
5550
|
return {
|
|
5542
5551
|
composeCwd: dir !== cwd ? dir : void 0,
|
|
5543
5552
|
composeFiles: files,
|
|
5544
5553
|
checkOverlay,
|
|
5545
5554
|
envFile,
|
|
5546
5555
|
services: services.map((s) => s.name),
|
|
5547
|
-
healthChecks: healthChecks.length > 0 ? healthChecks : void 0
|
|
5556
|
+
healthChecks: healthChecks.length > 0 ? healthChecks : void 0,
|
|
5557
|
+
smoketest
|
|
5548
5558
|
};
|
|
5549
5559
|
}
|
|
5550
5560
|
/** Create a DockerFileReader backed by the real filesystem. */
|
|
@@ -5605,11 +5615,15 @@ const dockerCheckCommand = defineCommand({
|
|
|
5605
5615
|
},
|
|
5606
5616
|
async run({ args }) {
|
|
5607
5617
|
const cwd = process.cwd();
|
|
5608
|
-
|
|
5618
|
+
const toolingConfig = loadToolingConfig(cwd);
|
|
5619
|
+
if (toolingConfig?.dockerCheck === false) {
|
|
5609
5620
|
log.info("Docker check is disabled in .tooling.json");
|
|
5610
5621
|
return;
|
|
5611
5622
|
}
|
|
5612
|
-
const defaults =
|
|
5623
|
+
const defaults = {
|
|
5624
|
+
...computeCheckDefaults(cwd),
|
|
5625
|
+
...toolingConfig?.dockerCheck
|
|
5626
|
+
};
|
|
5613
5627
|
if (!defaults.composeFiles || defaults.composeFiles.length === 0) throw new FatalError("No compose files found. Expected docker-compose.yaml or compose.yaml in ./ or docker/.");
|
|
5614
5628
|
if (!defaults.checkOverlay) {
|
|
5615
5629
|
const composeCwd = defaults.composeCwd ?? cwd;
|
|
@@ -5648,6 +5662,8 @@ const dockerCheckCommand = defineCommand({
|
|
|
5648
5662
|
buildCommand: defaults.buildCommand,
|
|
5649
5663
|
buildCwd: defaults.buildCwd,
|
|
5650
5664
|
healthChecks: defaults.healthChecks ? toHttpHealthChecks(defaults.healthChecks) : [],
|
|
5665
|
+
smoketest: defaults.smoketest,
|
|
5666
|
+
smoketestCwd: defaults.smoketestCwd,
|
|
5651
5667
|
timeoutMs: args.timeout ? Number.parseInt(args.timeout, 10) : defaults.timeoutMs,
|
|
5652
5668
|
pollIntervalMs: args["poll-interval"] ? Number.parseInt(args["poll-interval"], 10) : defaults.pollIntervalMs,
|
|
5653
5669
|
verbose: args.verbose === true || isEnvVerbose()
|
|
@@ -6459,7 +6475,7 @@ const webhookSendCommand = defineCommand({
|
|
|
6459
6475
|
const main = defineCommand({
|
|
6460
6476
|
meta: {
|
|
6461
6477
|
name: "bst",
|
|
6462
|
-
version: "0.
|
|
6478
|
+
version: "0.51.0",
|
|
6463
6479
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
6464
6480
|
},
|
|
6465
6481
|
subCommands: {
|
|
@@ -6480,7 +6496,7 @@ const main = defineCommand({
|
|
|
6480
6496
|
"webhook:send": webhookSendCommand
|
|
6481
6497
|
}
|
|
6482
6498
|
});
|
|
6483
|
-
console.log(`@bensandee/tooling v0.
|
|
6499
|
+
console.log(`@bensandee/tooling v0.51.0`);
|
|
6484
6500
|
async function run() {
|
|
6485
6501
|
await runMain(main);
|
|
6486
6502
|
process.exit(process.exitCode ?? 0);
|
|
@@ -128,6 +128,13 @@ function buildImages(executor, config) {
|
|
|
128
128
|
if (!file) return;
|
|
129
129
|
executor.execInherit(file, args, { cwd: config.buildCwd ?? config.compose.cwd });
|
|
130
130
|
}
|
|
131
|
+
/** Run the smoketest command against the running stack. */
|
|
132
|
+
function runSmoketest(executor, config) {
|
|
133
|
+
if (!config.smoketest) return;
|
|
134
|
+
const [file, ...args] = config.smoketest;
|
|
135
|
+
if (!file) return;
|
|
136
|
+
executor.execInherit(file, args, { cwd: config.smoketestCwd ?? config.compose.cwd });
|
|
137
|
+
}
|
|
131
138
|
/** Start the compose stack in detached mode. */
|
|
132
139
|
function composeUp(executor, config) {
|
|
133
140
|
executor.execInherit("docker", [
|
|
@@ -263,6 +270,24 @@ async function runDockerCheck(executor, config) {
|
|
|
263
270
|
}
|
|
264
271
|
}
|
|
265
272
|
if (allContainersHealthy && [...healthStatus.values()].every(Boolean)) {
|
|
273
|
+
if (config.smoketest) {
|
|
274
|
+
executor.log(`Running smoketest: ${config.smoketest.join(" ")}`);
|
|
275
|
+
try {
|
|
276
|
+
runSmoketest(executor, config);
|
|
277
|
+
executor.log("Smoketest passed!");
|
|
278
|
+
} catch (error) {
|
|
279
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
280
|
+
executor.logError(`Smoketest failed: ${message}`);
|
|
281
|
+
for (const service of compose.services) composeLogs(executor, compose, service);
|
|
282
|
+
cleanup();
|
|
283
|
+
return {
|
|
284
|
+
success: false,
|
|
285
|
+
reason: "smoketest-failed",
|
|
286
|
+
message,
|
|
287
|
+
elapsedMs: executor.now() - startTime
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
266
291
|
executor.log("Check successful! All systems operational.");
|
|
267
292
|
cleanup();
|
|
268
293
|
return {
|
|
@@ -68,6 +68,10 @@ interface CheckConfig {
|
|
|
68
68
|
buildCwd?: string;
|
|
69
69
|
/** HTTP health checks to poll. All must pass for success. */
|
|
70
70
|
healthChecks: HttpHealthCheck[];
|
|
71
|
+
/** Optional smoketest command to run after all health checks pass (e.g. ["pnpm", "test:smoke"]). */
|
|
72
|
+
smoketest?: string[];
|
|
73
|
+
/** Working directory for the smoketest command (defaults to compose.cwd). */
|
|
74
|
+
smoketestCwd?: string;
|
|
71
75
|
/** Maximum time to wait for all checks to pass, in ms. Default: 120000. */
|
|
72
76
|
timeoutMs?: number;
|
|
73
77
|
/** Interval between polling attempts, in ms. Default: 5000. */
|
|
@@ -81,7 +85,7 @@ type CheckResult = {
|
|
|
81
85
|
elapsedMs: number;
|
|
82
86
|
} | {
|
|
83
87
|
success: false;
|
|
84
|
-
reason: "timeout" | "unhealthy-container" | "error";
|
|
88
|
+
reason: "timeout" | "unhealthy-container" | "smoketest-failed" | "error";
|
|
85
89
|
message: string;
|
|
86
90
|
elapsedMs: number;
|
|
87
91
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as composeDown, c as composeUp, i as composeArgs, l as createRealExecutor, n as checkHttpHealth, o as composeLogs, r as getContainerHealth, s as composePs, t as runDockerCheck } from "../check-
|
|
1
|
+
import { a as composeDown, c as composeUp, i as composeArgs, l as createRealExecutor, n as checkHttpHealth, o as composeLogs, r as getContainerHealth, s as composePs, t as runDockerCheck } from "../check-COzp4S_Q.mjs";
|
|
2
2
|
export { checkHttpHealth, composeArgs, composeDown, composeLogs, composePs, composeUp, createRealExecutor, getContainerHealth, runDockerCheck };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"@types/node": "24.12.0",
|
|
52
52
|
"@types/picomatch": "4.0.3",
|
|
53
53
|
"commit-and-tag-version": "12.7.1",
|
|
54
|
-
"prettier": "3.8.
|
|
54
|
+
"prettier": "3.8.2",
|
|
55
55
|
"release-it": "19.2.4",
|
|
56
56
|
"tsdown": "0.21.7",
|
|
57
|
-
"@bensandee/
|
|
58
|
-
"@bensandee/
|
|
57
|
+
"@bensandee/claude-hooks": "0.7.0",
|
|
58
|
+
"@bensandee/config": "0.10.2"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "tsdown",
|
package/tooling.schema.json
CHANGED
|
@@ -144,6 +144,17 @@
|
|
|
144
144
|
"description": "Working directory for build command",
|
|
145
145
|
"type": "string"
|
|
146
146
|
},
|
|
147
|
+
"smoketest": {
|
|
148
|
+
"description": "Command to run as a smoketest after health checks pass (e.g. [\"pnpm\", \"test:smoke\"])",
|
|
149
|
+
"type": "array",
|
|
150
|
+
"items": {
|
|
151
|
+
"type": "string"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"smoketestCwd": {
|
|
155
|
+
"description": "Working directory for smoketest command",
|
|
156
|
+
"type": "string"
|
|
157
|
+
},
|
|
147
158
|
"timeoutMs": {
|
|
148
159
|
"description": "Overall timeout in milliseconds",
|
|
149
160
|
"type": "integer",
|