@bensandee/tooling 0.25.3 → 0.26.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 +25 -3
- package/dist/bin.mjs +33 -35
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -32,10 +32,32 @@ The tool auto-detects project structure, CI platform, project type, and Docker p
|
|
|
32
32
|
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
33
33
|
| `tooling repo:sync [dir]` | Detect, generate, and sync project tooling (idempotent). First run prompts for release strategy, CI platform (if not detected), and formatter (if Prettier found). Subsequent runs are non-interactive. |
|
|
34
34
|
| `tooling repo:sync --check [dir]` | Dry-run drift detection. Exits 1 if files would change. CI-friendly. |
|
|
35
|
-
| `tooling checks:run` | Run project checks (build, typecheck, lint, knip,
|
|
35
|
+
| `tooling checks:run` | Run project checks (build, docker:build, typecheck, lint, test, format, knip, tooling:check, docker:check). Flags: `--skip`, `--add`, `--fail-fast`. |
|
|
36
36
|
|
|
37
37
|
**Flags:** `--yes` (accept all defaults), `--no-ci`, `--no-prompt`, `--eslint-plugin`
|
|
38
38
|
|
|
39
|
+
#### `checks:run`
|
|
40
|
+
|
|
41
|
+
Runs checks in order: build, docker:build, typecheck, lint, test, format (--check), knip, tooling:check, docker:check. Checks without a matching script in `package.json` are silently skipped.
|
|
42
|
+
|
|
43
|
+
The `--skip` flag supports glob patterns via picomatch:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Skip all docker steps
|
|
47
|
+
tooling checks:run --skip 'docker:*'
|
|
48
|
+
|
|
49
|
+
# Skip specific checks
|
|
50
|
+
tooling checks:run --skip build,knip
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `--add` flag appends extra checks (must be defined in `package.json`):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
tooling checks:run --add e2e
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The generated `ci:check` script defaults to `pnpm check --skip 'docker:*'` since CI environments typically lack Docker support.
|
|
60
|
+
|
|
39
61
|
### Release management
|
|
40
62
|
|
|
41
63
|
| Command | Description |
|
|
@@ -99,7 +121,7 @@ To give individual packages a standalone `image:build` script for local testing:
|
|
|
99
121
|
}
|
|
100
122
|
```
|
|
101
123
|
|
|
102
|
-
**Flags:** `--package <dir>` (build a single package)
|
|
124
|
+
**Flags:** `--package <dir>` (build a single package)
|
|
103
125
|
|
|
104
126
|
#### `docker:publish`
|
|
105
127
|
|
|
@@ -109,7 +131,7 @@ Tags generated per package: `latest`, `vX.Y.Z`, `vX.Y`, `vX`
|
|
|
109
131
|
|
|
110
132
|
Each package is tagged independently using its own version, so packages in a monorepo can have different release cadences. Packages without a `version` field are rejected at publish time.
|
|
111
133
|
|
|
112
|
-
**Flags:** `--dry-run` (build and tag only, skip login/push/logout)
|
|
134
|
+
**Flags:** `--dry-run` (build and tag only, skip login/push/logout)
|
|
113
135
|
|
|
114
136
|
**Required environment variables:**
|
|
115
137
|
|
package/dist/bin.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { z } from "zod";
|
|
|
10
10
|
import { FatalError, TransientError, UnexpectedError } from "@bensandee/common";
|
|
11
11
|
import { isMap, isScalar, isSeq, parse as parse$1, parseDocument, stringify } from "yaml";
|
|
12
12
|
import { execSync } from "node:child_process";
|
|
13
|
+
import picomatch from "picomatch";
|
|
13
14
|
import { tmpdir } from "node:os";
|
|
14
15
|
//#region src/types.ts
|
|
15
16
|
const LEGACY_TOOLS = [
|
|
@@ -904,7 +905,7 @@ const STANDARD_SCRIPTS_SINGLE = {
|
|
|
904
905
|
lint: "oxlint",
|
|
905
906
|
knip: "knip",
|
|
906
907
|
check: "pnpm exec tooling checks:run",
|
|
907
|
-
"ci:check": "pnpm check",
|
|
908
|
+
"ci:check": "pnpm check --skip 'docker:*'",
|
|
908
909
|
"tooling:check": "pnpm exec tooling repo:sync --check",
|
|
909
910
|
"tooling:sync": "pnpm exec tooling repo:sync"
|
|
910
911
|
};
|
|
@@ -915,7 +916,7 @@ const STANDARD_SCRIPTS_MONOREPO = {
|
|
|
915
916
|
lint: "oxlint",
|
|
916
917
|
knip: "knip",
|
|
917
918
|
check: "pnpm exec tooling checks:run",
|
|
918
|
-
"ci:check": "pnpm check",
|
|
919
|
+
"ci:check": "pnpm check --skip 'docker:*'",
|
|
919
920
|
"tooling:check": "pnpm exec tooling repo:sync --check",
|
|
920
921
|
"tooling:sync": "pnpm exec tooling repo:sync"
|
|
921
922
|
};
|
|
@@ -983,7 +984,7 @@ function getAddedDevDepNames(config) {
|
|
|
983
984
|
const deps = { ...ROOT_DEV_DEPS };
|
|
984
985
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
985
986
|
deps["@bensandee/config"] = "0.9.0";
|
|
986
|
-
deps["@bensandee/tooling"] = "0.
|
|
987
|
+
deps["@bensandee/tooling"] = "0.26.0";
|
|
987
988
|
if (config.formatter === "oxfmt") deps["oxfmt"] = "0.35.0";
|
|
988
989
|
if (config.formatter === "prettier") deps["prettier"] = "3.8.1";
|
|
989
990
|
addReleaseDeps(deps, config);
|
|
@@ -1008,7 +1009,7 @@ async function generatePackageJson(ctx) {
|
|
|
1008
1009
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1009
1010
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1010
1011
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.0";
|
|
1011
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1012
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.26.0";
|
|
1012
1013
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.2";
|
|
1013
1014
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
1014
1015
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
@@ -2779,9 +2780,6 @@ function imageRef(namespace, imageName, tag) {
|
|
|
2779
2780
|
function log$1(message) {
|
|
2780
2781
|
console.log(message);
|
|
2781
2782
|
}
|
|
2782
|
-
function debug$1(verbose, message) {
|
|
2783
|
-
if (verbose) console.log(`[debug] ${message}`);
|
|
2784
|
-
}
|
|
2785
2783
|
/** Read the repo name from root package.json. */
|
|
2786
2784
|
function readRepoName(executor, cwd) {
|
|
2787
2785
|
const rootPkgRaw = executor.readFile(path.join(cwd, "package.json"));
|
|
@@ -2791,7 +2789,7 @@ function readRepoName(executor, cwd) {
|
|
|
2791
2789
|
return repoName;
|
|
2792
2790
|
}
|
|
2793
2791
|
/** Build a single docker image from its config. Paths are resolved relative to cwd. */
|
|
2794
|
-
function buildImage(executor, pkg, cwd,
|
|
2792
|
+
function buildImage(executor, pkg, cwd, extraArgs) {
|
|
2795
2793
|
const dockerfilePath = path.resolve(cwd, pkg.docker.dockerfile);
|
|
2796
2794
|
const contextPath = path.resolve(cwd, pkg.docker.context);
|
|
2797
2795
|
const command = [
|
|
@@ -2801,10 +2799,7 @@ function buildImage(executor, pkg, cwd, verbose, extraArgs) {
|
|
|
2801
2799
|
...extraArgs,
|
|
2802
2800
|
contextPath
|
|
2803
2801
|
].join(" ");
|
|
2804
|
-
|
|
2805
|
-
const buildResult = executor.exec(command);
|
|
2806
|
-
debug$1(verbose, `Build stdout: ${buildResult.stdout}`);
|
|
2807
|
-
if (buildResult.exitCode !== 0) throw new FatalError(`docker build failed for ${pkg.dir} (exit ${buildResult.exitCode}): ${buildResult.stderr}`);
|
|
2802
|
+
executor.execInherit(command);
|
|
2808
2803
|
}
|
|
2809
2804
|
/**
|
|
2810
2805
|
* Detect packages with docker config in .tooling.json and build each one.
|
|
@@ -2818,7 +2813,7 @@ function runDockerBuild(executor, config) {
|
|
|
2818
2813
|
if (config.packageDir) {
|
|
2819
2814
|
const pkg = readSinglePackageDocker(executor, config.cwd, config.packageDir, repoName);
|
|
2820
2815
|
log$1(`Building image for ${pkg.dir} (${pkg.imageName}:latest)...`);
|
|
2821
|
-
buildImage(executor, pkg, config.cwd, config.
|
|
2816
|
+
buildImage(executor, pkg, config.cwd, config.extraArgs);
|
|
2822
2817
|
log$1(`Built ${pkg.imageName}:latest`);
|
|
2823
2818
|
return { packages: [pkg] };
|
|
2824
2819
|
}
|
|
@@ -2830,7 +2825,7 @@ function runDockerBuild(executor, config) {
|
|
|
2830
2825
|
log$1(`Found ${packages.length} Docker package(s): ${packages.map((p) => p.dir).join(", ")}`);
|
|
2831
2826
|
for (const pkg of packages) {
|
|
2832
2827
|
log$1(`Building image for ${pkg.dir} (${pkg.imageName}:latest)...`);
|
|
2833
|
-
buildImage(executor, pkg, config.cwd, config.
|
|
2828
|
+
buildImage(executor, pkg, config.cwd, config.extraArgs);
|
|
2834
2829
|
}
|
|
2835
2830
|
log$1(`Built ${packages.length} image(s)`);
|
|
2836
2831
|
return { packages };
|
|
@@ -2847,7 +2842,6 @@ function runDockerPublish(executor, config) {
|
|
|
2847
2842
|
const { packages } = runDockerBuild(executor, {
|
|
2848
2843
|
cwd: config.cwd,
|
|
2849
2844
|
packageDir: void 0,
|
|
2850
|
-
verbose: config.verbose,
|
|
2851
2845
|
extraArgs: []
|
|
2852
2846
|
});
|
|
2853
2847
|
if (packages.length === 0) return {
|
|
@@ -3311,6 +3305,16 @@ function createRealExecutor() {
|
|
|
3311
3305
|
};
|
|
3312
3306
|
}
|
|
3313
3307
|
},
|
|
3308
|
+
execInherit(command, options) {
|
|
3309
|
+
execSync(command, {
|
|
3310
|
+
cwd: options?.cwd,
|
|
3311
|
+
env: options?.env ? {
|
|
3312
|
+
...process.env,
|
|
3313
|
+
...options.env
|
|
3314
|
+
} : void 0,
|
|
3315
|
+
stdio: "inherit"
|
|
3316
|
+
});
|
|
3317
|
+
},
|
|
3314
3318
|
fetch: globalThis.fetch,
|
|
3315
3319
|
listChangesetFiles(cwd) {
|
|
3316
3320
|
const dir = path.join(cwd, ".changeset");
|
|
@@ -4162,6 +4166,7 @@ const releaseSimpleCommand = defineCommand({
|
|
|
4162
4166
|
//#region src/commands/repo-run-checks.ts
|
|
4163
4167
|
const CHECKS = [
|
|
4164
4168
|
{ name: "build" },
|
|
4169
|
+
{ name: "docker:build" },
|
|
4165
4170
|
{ name: "typecheck" },
|
|
4166
4171
|
{ name: "lint" },
|
|
4167
4172
|
{ name: "test" },
|
|
@@ -4173,6 +4178,11 @@ const CHECKS = [
|
|
|
4173
4178
|
{ name: "tooling:check" },
|
|
4174
4179
|
{ name: "docker:check" }
|
|
4175
4180
|
];
|
|
4181
|
+
/** Check if a name matches any skip pattern. Supports glob syntax via picomatch. */
|
|
4182
|
+
function shouldSkip(name, patterns) {
|
|
4183
|
+
if (patterns.size === 0) return false;
|
|
4184
|
+
return picomatch.isMatch(name, [...patterns]);
|
|
4185
|
+
}
|
|
4176
4186
|
function defaultGetScripts(targetDir) {
|
|
4177
4187
|
try {
|
|
4178
4188
|
const pkg = parsePackageJson(readFileSync(path.join(targetDir, "package.json"), "utf-8"));
|
|
@@ -4207,7 +4217,7 @@ function runRunChecks(targetDir, options = {}) {
|
|
|
4207
4217
|
const failures = [];
|
|
4208
4218
|
const notDefined = [];
|
|
4209
4219
|
for (const check of allChecks) {
|
|
4210
|
-
if (
|
|
4220
|
+
if (shouldSkip(check.name, skip)) continue;
|
|
4211
4221
|
if (!definedScripts.has(check.name)) {
|
|
4212
4222
|
if (addedNames.has(check.name)) {
|
|
4213
4223
|
p.log.error(`${check.name} not defined in package.json`);
|
|
@@ -4283,16 +4293,10 @@ const publishDockerCommand = defineCommand({
|
|
|
4283
4293
|
name: "docker:publish",
|
|
4284
4294
|
description: "Build, tag, and push Docker images for packages with an image:build script"
|
|
4285
4295
|
},
|
|
4286
|
-
args: {
|
|
4287
|
-
"
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
},
|
|
4291
|
-
verbose: {
|
|
4292
|
-
type: "boolean",
|
|
4293
|
-
description: "Enable detailed debug logging"
|
|
4294
|
-
}
|
|
4295
|
-
},
|
|
4296
|
+
args: { "dry-run": {
|
|
4297
|
+
type: "boolean",
|
|
4298
|
+
description: "Build and tag images but skip login, push, and logout"
|
|
4299
|
+
} },
|
|
4296
4300
|
async run({ args }) {
|
|
4297
4301
|
const config = {
|
|
4298
4302
|
cwd: process.cwd(),
|
|
@@ -4300,8 +4304,7 @@ const publishDockerCommand = defineCommand({
|
|
|
4300
4304
|
registryNamespace: requireEnv("DOCKER_REGISTRY_NAMESPACE"),
|
|
4301
4305
|
username: requireEnv("DOCKER_USERNAME"),
|
|
4302
4306
|
password: requireEnv("DOCKER_PASSWORD"),
|
|
4303
|
-
dryRun: args["dry-run"] === true
|
|
4304
|
-
verbose: args.verbose === true
|
|
4307
|
+
dryRun: args["dry-run"] === true
|
|
4305
4308
|
};
|
|
4306
4309
|
runDockerPublish(createRealExecutor(), config);
|
|
4307
4310
|
}
|
|
@@ -4334,10 +4337,6 @@ const dockerBuildCommand = defineCommand({
|
|
|
4334
4337
|
type: "string",
|
|
4335
4338
|
description: "Build a single package by directory path (e.g. packages/server). Useful as an image:build script."
|
|
4336
4339
|
},
|
|
4337
|
-
verbose: {
|
|
4338
|
-
type: "boolean",
|
|
4339
|
-
description: "Enable detailed debug logging"
|
|
4340
|
-
},
|
|
4341
4340
|
_: {
|
|
4342
4341
|
type: "positional",
|
|
4343
4342
|
required: false,
|
|
@@ -4358,7 +4357,6 @@ const dockerBuildCommand = defineCommand({
|
|
|
4358
4357
|
runDockerBuild(executor, {
|
|
4359
4358
|
cwd,
|
|
4360
4359
|
packageDir,
|
|
4361
|
-
verbose: args.verbose === true,
|
|
4362
4360
|
extraArgs: extraArgs.filter((a) => a.length > 0)
|
|
4363
4361
|
});
|
|
4364
4362
|
}
|
|
@@ -4659,7 +4657,7 @@ const dockerCheckCommand = defineCommand({
|
|
|
4659
4657
|
const main = defineCommand({
|
|
4660
4658
|
meta: {
|
|
4661
4659
|
name: "tooling",
|
|
4662
|
-
version: "0.
|
|
4660
|
+
version: "0.26.0",
|
|
4663
4661
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
4664
4662
|
},
|
|
4665
4663
|
subCommands: {
|
|
@@ -4675,7 +4673,7 @@ const main = defineCommand({
|
|
|
4675
4673
|
"docker:check": dockerCheckCommand
|
|
4676
4674
|
}
|
|
4677
4675
|
});
|
|
4678
|
-
console.log(`@bensandee/tooling v0.
|
|
4676
|
+
console.log(`@bensandee/tooling v0.26.0`);
|
|
4679
4677
|
async function run() {
|
|
4680
4678
|
await runMain(main);
|
|
4681
4679
|
process.exit(process.exitCode ?? 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tooling": "./dist/bin.mjs"
|
|
@@ -34,12 +34,14 @@
|
|
|
34
34
|
"citty": "^0.2.1",
|
|
35
35
|
"json5": "^2.2.3",
|
|
36
36
|
"jsonc-parser": "^3.3.1",
|
|
37
|
+
"picomatch": "^4.0.3",
|
|
37
38
|
"yaml": "^2.8.2",
|
|
38
39
|
"zod": "^4.3.6",
|
|
39
40
|
"@bensandee/common": "0.1.2"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/node": "24.12.0",
|
|
44
|
+
"@types/picomatch": "^4.0.2",
|
|
43
45
|
"tsdown": "0.21.2",
|
|
44
46
|
"typescript": "5.9.3",
|
|
45
47
|
"vitest": "4.0.18",
|