@bensandee/tooling 0.26.0 → 0.27.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.
- package/dist/bin.mjs +349 -202
- package/dist/index.d.mts +4 -0
- package/package.json +9 -8
- package/tooling.schema.json +74 -55
package/dist/bin.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { l as createRealExecutor$1, t as runDockerCheck, u as isExecSyncError } from "./check-D41R218h.mjs";
|
|
3
3
|
import { defineCommand, runMain } from "citty";
|
|
4
|
-
import * as
|
|
4
|
+
import * as clack from "@clack/prompts";
|
|
5
|
+
import { isCancel, select } from "@clack/prompts";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
7
8
|
import JSON5 from "json5";
|
|
@@ -12,6 +13,22 @@ import { isMap, isScalar, isSeq, parse as parse$1, parseDocument, stringify } fr
|
|
|
12
13
|
import { execSync } from "node:child_process";
|
|
13
14
|
import picomatch from "picomatch";
|
|
14
15
|
import { tmpdir } from "node:os";
|
|
16
|
+
//#region src/utils/log.ts
|
|
17
|
+
const out = (msg) => console.log(msg);
|
|
18
|
+
const isCI = Boolean(process.env["CI"]);
|
|
19
|
+
const log$2 = isCI ? {
|
|
20
|
+
info: out,
|
|
21
|
+
warn: (msg) => out(`[warn] ${msg}`),
|
|
22
|
+
error: (msg) => out(`[error] ${msg}`),
|
|
23
|
+
success: (msg) => out(`✓ ${msg}`)
|
|
24
|
+
} : clack.log;
|
|
25
|
+
function note(body, title) {
|
|
26
|
+
if (isCI) {
|
|
27
|
+
if (title) out(`--- ${title} ---`);
|
|
28
|
+
out(body);
|
|
29
|
+
} else clack.note(body, title);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
15
32
|
//#region src/types.ts
|
|
16
33
|
const LEGACY_TOOLS = [
|
|
17
34
|
"eslint",
|
|
@@ -205,6 +222,8 @@ function computeDefaults(targetDir) {
|
|
|
205
222
|
ci: detectCiPlatform(targetDir),
|
|
206
223
|
setupRenovate: true,
|
|
207
224
|
releaseStrategy: "none",
|
|
225
|
+
publishNpm: false,
|
|
226
|
+
publishDocker: false,
|
|
208
227
|
projectType: isMonorepo ? "default" : detectProjectType(targetDir),
|
|
209
228
|
detectPackageTypes: true
|
|
210
229
|
};
|
|
@@ -257,10 +276,10 @@ function getMonorepoPackages(targetDir) {
|
|
|
257
276
|
//#endregion
|
|
258
277
|
//#region src/prompts/init-prompts.ts
|
|
259
278
|
function isCancelled(value) {
|
|
260
|
-
return
|
|
279
|
+
return clack.isCancel(value);
|
|
261
280
|
}
|
|
262
281
|
async function runInitPrompts(targetDir, saved) {
|
|
263
|
-
|
|
282
|
+
clack.intro("@bensandee/tooling repo:sync");
|
|
264
283
|
const existingPkg = readPackageJson(targetDir);
|
|
265
284
|
const detected = detectProject(targetDir);
|
|
266
285
|
const defaults = computeDefaults(targetDir);
|
|
@@ -277,7 +296,7 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
277
296
|
const projectType = saved?.projectType ?? defaults.projectType;
|
|
278
297
|
const detectPackageTypes = saved?.detectPackageTypes ?? defaults.detectPackageTypes;
|
|
279
298
|
if (detected.legacyConfigs.some((l) => l.tool === "prettier") && isFirstInit) {
|
|
280
|
-
const formatterAnswer = await
|
|
299
|
+
const formatterAnswer = await clack.select({
|
|
281
300
|
message: "Existing Prettier config found. Keep Prettier or migrate to oxfmt?",
|
|
282
301
|
initialValue: "prettier",
|
|
283
302
|
options: [{
|
|
@@ -290,14 +309,14 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
290
309
|
}]
|
|
291
310
|
});
|
|
292
311
|
if (isCancelled(formatterAnswer)) {
|
|
293
|
-
|
|
312
|
+
clack.cancel("Cancelled.");
|
|
294
313
|
process.exit(0);
|
|
295
314
|
}
|
|
296
315
|
formatter = formatterAnswer;
|
|
297
316
|
}
|
|
298
317
|
const detectedCi = detectCiPlatform(targetDir);
|
|
299
318
|
if (isFirstInit && detectedCi === "none") {
|
|
300
|
-
const ciAnswer = await
|
|
319
|
+
const ciAnswer = await clack.select({
|
|
301
320
|
message: "CI workflow",
|
|
302
321
|
initialValue: "forgejo",
|
|
303
322
|
options: [
|
|
@@ -316,14 +335,14 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
316
335
|
]
|
|
317
336
|
});
|
|
318
337
|
if (isCancelled(ciAnswer)) {
|
|
319
|
-
|
|
338
|
+
clack.cancel("Cancelled.");
|
|
320
339
|
process.exit(0);
|
|
321
340
|
}
|
|
322
341
|
ci = ciAnswer;
|
|
323
342
|
}
|
|
324
343
|
const hasExistingRelease = detected.hasReleaseItConfig || detected.hasSimpleReleaseConfig || detected.hasChangesetsConfig;
|
|
325
344
|
if (isFirstInit && !hasExistingRelease) {
|
|
326
|
-
const releaseAnswer = await
|
|
345
|
+
const releaseAnswer = await clack.select({
|
|
327
346
|
message: "Release management",
|
|
328
347
|
initialValue: defaults.releaseStrategy,
|
|
329
348
|
options: [
|
|
@@ -349,12 +368,40 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
349
368
|
]
|
|
350
369
|
});
|
|
351
370
|
if (isCancelled(releaseAnswer)) {
|
|
352
|
-
|
|
371
|
+
clack.cancel("Cancelled.");
|
|
353
372
|
process.exit(0);
|
|
354
373
|
}
|
|
355
374
|
releaseStrategy = releaseAnswer;
|
|
356
375
|
}
|
|
357
|
-
|
|
376
|
+
let publishNpm = saved?.publishNpm ?? false;
|
|
377
|
+
if (isFirstInit && releaseStrategy !== "none") {
|
|
378
|
+
if (getPublishablePackages(targetDir, structure).length > 0) {
|
|
379
|
+
const answer = await clack.confirm({
|
|
380
|
+
message: "Publish packages to npm?",
|
|
381
|
+
initialValue: false
|
|
382
|
+
});
|
|
383
|
+
if (isCancelled(answer)) {
|
|
384
|
+
clack.cancel("Cancelled.");
|
|
385
|
+
process.exit(0);
|
|
386
|
+
}
|
|
387
|
+
publishNpm = answer;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
let publishDocker = saved?.publishDocker ?? false;
|
|
391
|
+
if (isFirstInit) {
|
|
392
|
+
if (existsSync(path.join(targetDir, "Dockerfile")) || existsSync(path.join(targetDir, "docker/Dockerfile"))) {
|
|
393
|
+
const answer = await clack.confirm({
|
|
394
|
+
message: "Publish Docker images to a registry?",
|
|
395
|
+
initialValue: false
|
|
396
|
+
});
|
|
397
|
+
if (isCancelled(answer)) {
|
|
398
|
+
clack.cancel("Cancelled.");
|
|
399
|
+
process.exit(0);
|
|
400
|
+
}
|
|
401
|
+
publishDocker = answer;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
clack.outro("Configuration complete!");
|
|
358
405
|
return {
|
|
359
406
|
name,
|
|
360
407
|
isNew: !isExisting,
|
|
@@ -365,6 +412,8 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
365
412
|
ci,
|
|
366
413
|
setupRenovate,
|
|
367
414
|
releaseStrategy,
|
|
415
|
+
publishNpm,
|
|
416
|
+
publishDocker,
|
|
368
417
|
projectType,
|
|
369
418
|
detectPackageTypes,
|
|
370
419
|
targetDir
|
|
@@ -482,51 +531,53 @@ function createDryRunContext(config) {
|
|
|
482
531
|
//#region src/utils/tooling-config.ts
|
|
483
532
|
const CONFIG_FILE = ".tooling.json";
|
|
484
533
|
const DeclarativeHealthCheckSchema = z.object({
|
|
485
|
-
name: z.string(),
|
|
486
|
-
url: z.string(),
|
|
487
|
-
status: z.number().int().optional()
|
|
534
|
+
name: z.string().meta({ description: "Service name" }),
|
|
535
|
+
url: z.string().meta({ description: "Health check URL" }),
|
|
536
|
+
status: z.number().int().optional().meta({ description: "Expected HTTP status code" })
|
|
488
537
|
});
|
|
489
538
|
const DockerCheckConfigSchema = z.object({
|
|
490
|
-
composeFiles: z.array(z.string()).optional(),
|
|
491
|
-
envFile: z.string().optional(),
|
|
492
|
-
services: z.array(z.string()).optional(),
|
|
493
|
-
healthChecks: z.array(DeclarativeHealthCheckSchema).optional(),
|
|
494
|
-
buildCommand: z.string().optional(),
|
|
495
|
-
buildCwd: z.string().optional(),
|
|
496
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
497
|
-
pollIntervalMs: z.number().int().positive().optional()
|
|
539
|
+
composeFiles: z.array(z.string()).optional().meta({ description: "Compose files to use" }),
|
|
540
|
+
envFile: z.string().optional().meta({ description: "Environment file for compose" }),
|
|
541
|
+
services: z.array(z.string()).optional().meta({ description: "Services to check (default: all)" }),
|
|
542
|
+
healthChecks: z.array(DeclarativeHealthCheckSchema).optional().meta({ description: "Health check definitions" }),
|
|
543
|
+
buildCommand: z.string().optional().meta({ description: "Command to build images before checking" }),
|
|
544
|
+
buildCwd: z.string().optional().meta({ description: "Working directory for build command" }),
|
|
545
|
+
timeoutMs: z.number().int().positive().optional().meta({ description: "Overall timeout in milliseconds" }),
|
|
546
|
+
pollIntervalMs: z.number().int().positive().optional().meta({ description: "Poll interval in milliseconds" })
|
|
498
547
|
});
|
|
499
548
|
const ToolingConfigSchema = z.strictObject({
|
|
500
|
-
$schema: z.string().optional(),
|
|
501
|
-
structure: z.enum(["single", "monorepo"]).optional(),
|
|
502
|
-
useEslintPlugin: z.boolean().optional(),
|
|
503
|
-
formatter: z.enum(["oxfmt", "prettier"]).optional(),
|
|
504
|
-
setupVitest: z.boolean().optional(),
|
|
549
|
+
$schema: z.string().optional().meta({ description: "JSON Schema reference (ignored by tooling)" }),
|
|
550
|
+
structure: z.enum(["single", "monorepo"]).optional().meta({ description: "Project structure" }),
|
|
551
|
+
useEslintPlugin: z.boolean().optional().meta({ description: "Include @bensandee/eslint-plugin oxlint plugin" }),
|
|
552
|
+
formatter: z.enum(["oxfmt", "prettier"]).optional().meta({ description: "Formatter choice" }),
|
|
553
|
+
setupVitest: z.boolean().optional().meta({ description: "Generate vitest config and example test" }),
|
|
505
554
|
ci: z.enum([
|
|
506
555
|
"github",
|
|
507
556
|
"forgejo",
|
|
508
557
|
"none"
|
|
509
|
-
]).optional(),
|
|
510
|
-
setupRenovate: z.boolean().optional(),
|
|
558
|
+
]).optional().meta({ description: "CI platform" }),
|
|
559
|
+
setupRenovate: z.boolean().optional().meta({ description: "Generate Renovate config" }),
|
|
511
560
|
releaseStrategy: z.enum([
|
|
512
561
|
"release-it",
|
|
513
562
|
"simple",
|
|
514
563
|
"changesets",
|
|
515
564
|
"none"
|
|
516
|
-
]).optional(),
|
|
565
|
+
]).optional().meta({ description: "Release management strategy" }),
|
|
566
|
+
publishNpm: z.boolean().optional().meta({ description: "Publish packages to npm (opt-in)" }),
|
|
567
|
+
publishDocker: z.boolean().optional().meta({ description: "Publish Docker images to a registry (opt-in)" }),
|
|
517
568
|
projectType: z.enum([
|
|
518
569
|
"default",
|
|
519
570
|
"node",
|
|
520
571
|
"react",
|
|
521
572
|
"library"
|
|
522
|
-
]).optional(),
|
|
523
|
-
detectPackageTypes: z.boolean().optional(),
|
|
524
|
-
setupDocker: z.boolean().optional(),
|
|
573
|
+
]).optional().meta({ description: "Project type (determines tsconfig base)" }),
|
|
574
|
+
detectPackageTypes: z.boolean().optional().meta({ description: "Auto-detect project types for monorepo packages" }),
|
|
575
|
+
setupDocker: z.boolean().optional().meta({ description: "Generate Docker build/check scripts" }),
|
|
525
576
|
docker: z.record(z.string(), z.object({
|
|
526
|
-
dockerfile: z.string(),
|
|
527
|
-
context: z.string().default(".")
|
|
528
|
-
})).optional(),
|
|
529
|
-
dockerCheck: z.union([z.literal(false), DockerCheckConfigSchema]).optional()
|
|
577
|
+
dockerfile: z.string().meta({ description: "Path to Dockerfile relative to package" }),
|
|
578
|
+
context: z.string().default(".").meta({ description: "Docker build context relative to package" })
|
|
579
|
+
})).optional().meta({ description: "Docker package overrides (package name → config)" }),
|
|
580
|
+
dockerCheck: z.union([z.literal(false), DockerCheckConfigSchema]).optional().meta({ description: "Docker health check configuration or false to disable" })
|
|
530
581
|
});
|
|
531
582
|
/** Load saved tooling config from the target directory. Returns undefined if missing, throws on invalid. */
|
|
532
583
|
function loadToolingConfig(targetDir) {
|
|
@@ -546,6 +597,8 @@ const OVERRIDE_KEYS = [
|
|
|
546
597
|
"ci",
|
|
547
598
|
"setupRenovate",
|
|
548
599
|
"releaseStrategy",
|
|
600
|
+
"publishNpm",
|
|
601
|
+
"publishDocker",
|
|
549
602
|
"projectType",
|
|
550
603
|
"detectPackageTypes"
|
|
551
604
|
];
|
|
@@ -591,6 +644,8 @@ function mergeWithSavedConfig(detected, saved) {
|
|
|
591
644
|
ci: saved.ci ?? detected.ci,
|
|
592
645
|
setupRenovate: saved.setupRenovate ?? detected.setupRenovate,
|
|
593
646
|
releaseStrategy: saved.releaseStrategy ?? detected.releaseStrategy,
|
|
647
|
+
publishNpm: saved.publishNpm ?? detected.publishNpm,
|
|
648
|
+
publishDocker: saved.publishDocker ?? detected.publishDocker,
|
|
594
649
|
projectType: saved.projectType ?? detected.projectType,
|
|
595
650
|
detectPackageTypes: saved.detectPackageTypes ?? detected.detectPackageTypes
|
|
596
651
|
};
|
|
@@ -609,6 +664,10 @@ function ensureSchemaComment(content, ci) {
|
|
|
609
664
|
if (content.includes("yaml-language-server")) return content;
|
|
610
665
|
return FORGEJO_SCHEMA_COMMENT + content;
|
|
611
666
|
}
|
|
667
|
+
/** Migrate content from old tooling binary name to new. */
|
|
668
|
+
function migrateToolingBinary(content) {
|
|
669
|
+
return content.replaceAll("pnpm exec tooling ", "pnpm exec bst ");
|
|
670
|
+
}
|
|
612
671
|
/** Check if a YAML file has an opt-out comment in the first 10 lines. */
|
|
613
672
|
function isToolingIgnored(content) {
|
|
614
673
|
return content.split("\n", 10).some((line) => line.includes(IGNORE_PATTERN));
|
|
@@ -773,7 +832,7 @@ jobs:
|
|
|
773
832
|
DOCKER_REGISTRY_NAMESPACE: ${actionsExpr$2("vars.DOCKER_REGISTRY_NAMESPACE")}
|
|
774
833
|
DOCKER_USERNAME: ${actionsExpr$2("secrets.DOCKER_USERNAME")}
|
|
775
834
|
DOCKER_PASSWORD: ${actionsExpr$2("secrets.DOCKER_PASSWORD")}
|
|
776
|
-
run: pnpm exec
|
|
835
|
+
run: pnpm exec bst docker:publish
|
|
777
836
|
`;
|
|
778
837
|
}
|
|
779
838
|
function requiredDeploySteps() {
|
|
@@ -796,7 +855,7 @@ function requiredDeploySteps() {
|
|
|
796
855
|
},
|
|
797
856
|
{
|
|
798
857
|
match: { run: "docker:publish" },
|
|
799
|
-
step: { run: "pnpm exec
|
|
858
|
+
step: { run: "pnpm exec bst docker:publish" }
|
|
800
859
|
}
|
|
801
860
|
];
|
|
802
861
|
}
|
|
@@ -833,7 +892,7 @@ function hasDockerPackages(ctx) {
|
|
|
833
892
|
}
|
|
834
893
|
async function generateDeployCi(ctx) {
|
|
835
894
|
const filePath = "deploy-ci";
|
|
836
|
-
if (!
|
|
895
|
+
if (!ctx.config.publishDocker || ctx.config.ci === "none") return {
|
|
837
896
|
filePath,
|
|
838
897
|
action: "skipped",
|
|
839
898
|
description: "Deploy CI workflow not applicable"
|
|
@@ -843,30 +902,33 @@ async function generateDeployCi(ctx) {
|
|
|
843
902
|
const nodeVersionYaml = hasEnginesNode$2(ctx) ? "node-version-file: package.json" : "node-version: \"24\"";
|
|
844
903
|
const content = deployWorkflow(ctx.config.ci, nodeVersionYaml);
|
|
845
904
|
if (ctx.exists(workflowPath)) {
|
|
846
|
-
const
|
|
847
|
-
if (
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
905
|
+
const raw = ctx.read(workflowPath);
|
|
906
|
+
if (raw) {
|
|
907
|
+
const existing = migrateToolingBinary(raw);
|
|
908
|
+
if (existing === content || ensureSchemaComment(existing, ctx.config.ci) === content) {
|
|
909
|
+
if (existing !== raw) {
|
|
910
|
+
ctx.write(workflowPath, ensureSchemaComment(existing, ctx.config.ci));
|
|
911
|
+
return {
|
|
912
|
+
filePath: workflowPath,
|
|
913
|
+
action: "updated",
|
|
914
|
+
description: "Migrated tooling binary name in deploy workflow"
|
|
915
|
+
};
|
|
916
|
+
}
|
|
857
917
|
return {
|
|
858
918
|
filePath: workflowPath,
|
|
859
|
-
action: "
|
|
860
|
-
description: "
|
|
919
|
+
action: "skipped",
|
|
920
|
+
description: "Deploy workflow already up to date"
|
|
861
921
|
};
|
|
862
922
|
}
|
|
863
|
-
|
|
864
|
-
|
|
923
|
+
const merged = mergeWorkflowSteps(existing, "deploy", requiredDeploySteps());
|
|
924
|
+
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
925
|
+
if (!merged.changed) {
|
|
926
|
+
if (withComment !== raw) {
|
|
865
927
|
ctx.write(workflowPath, withComment);
|
|
866
928
|
return {
|
|
867
929
|
filePath: workflowPath,
|
|
868
930
|
action: "updated",
|
|
869
|
-
description: "Added
|
|
931
|
+
description: existing !== raw ? "Migrated tooling binary name in deploy workflow" : "Added schema comment to deploy workflow"
|
|
870
932
|
};
|
|
871
933
|
}
|
|
872
934
|
return {
|
|
@@ -875,11 +937,11 @@ async function generateDeployCi(ctx) {
|
|
|
875
937
|
description: "Existing deploy workflow preserved"
|
|
876
938
|
};
|
|
877
939
|
}
|
|
878
|
-
ctx.write(workflowPath,
|
|
940
|
+
ctx.write(workflowPath, withComment);
|
|
879
941
|
return {
|
|
880
942
|
filePath: workflowPath,
|
|
881
943
|
action: "updated",
|
|
882
|
-
description: "
|
|
944
|
+
description: "Added missing steps to deploy workflow"
|
|
883
945
|
};
|
|
884
946
|
}
|
|
885
947
|
return {
|
|
@@ -904,10 +966,10 @@ const STANDARD_SCRIPTS_SINGLE = {
|
|
|
904
966
|
test: "vitest run",
|
|
905
967
|
lint: "oxlint",
|
|
906
968
|
knip: "knip",
|
|
907
|
-
check: "
|
|
969
|
+
check: "bst checks:run",
|
|
908
970
|
"ci:check": "pnpm check --skip 'docker:*'",
|
|
909
|
-
"tooling:check": "
|
|
910
|
-
"tooling:sync": "
|
|
971
|
+
"tooling:check": "bst repo:sync --check",
|
|
972
|
+
"tooling:sync": "bst repo:sync"
|
|
911
973
|
};
|
|
912
974
|
const STANDARD_SCRIPTS_MONOREPO = {
|
|
913
975
|
build: "pnpm -r build",
|
|
@@ -915,20 +977,28 @@ const STANDARD_SCRIPTS_MONOREPO = {
|
|
|
915
977
|
typecheck: "pnpm -r --parallel run typecheck",
|
|
916
978
|
lint: "oxlint",
|
|
917
979
|
knip: "knip",
|
|
918
|
-
check: "
|
|
980
|
+
check: "bst checks:run",
|
|
919
981
|
"ci:check": "pnpm check --skip 'docker:*'",
|
|
920
|
-
"tooling:check": "
|
|
921
|
-
"tooling:sync": "
|
|
982
|
+
"tooling:check": "bst repo:sync --check",
|
|
983
|
+
"tooling:sync": "bst repo:sync"
|
|
922
984
|
};
|
|
923
985
|
/** Scripts that tooling owns — map from script name to keyword that must appear in the value. */
|
|
924
986
|
const MANAGED_SCRIPTS = {
|
|
925
|
-
check: "checks:run",
|
|
987
|
+
check: "bst checks:run",
|
|
926
988
|
"ci:check": "pnpm check",
|
|
927
|
-
"tooling:check": "repo:sync --check",
|
|
928
|
-
"tooling:sync": "repo:sync",
|
|
929
|
-
"
|
|
930
|
-
"docker:
|
|
989
|
+
"tooling:check": "bst repo:sync --check",
|
|
990
|
+
"tooling:sync": "bst repo:sync",
|
|
991
|
+
"trigger-release": "bst release:trigger",
|
|
992
|
+
"docker:build": "bst docker:build",
|
|
993
|
+
"docker:check": "bst docker:check"
|
|
931
994
|
};
|
|
995
|
+
/** Check if an existing script value satisfies a managed script requirement.
|
|
996
|
+
* Accepts both `bst <cmd>` and `bin.mjs <cmd>` (used in the tooling repo itself). */
|
|
997
|
+
function matchesManagedScript(scriptValue, expectedFragment) {
|
|
998
|
+
if (scriptValue.includes(expectedFragment)) return true;
|
|
999
|
+
const binMjsFragment = expectedFragment.replace(/^bst /, "bin.mjs ");
|
|
1000
|
+
return scriptValue.includes(binMjsFragment);
|
|
1001
|
+
}
|
|
932
1002
|
/** Deprecated scripts to remove during migration. */
|
|
933
1003
|
const DEPRECATED_SCRIPTS = ["tooling:init", "tooling:update"];
|
|
934
1004
|
/** DevDeps that belong in every project (single repo) or per-package (monorepo). */
|
|
@@ -983,8 +1053,8 @@ function addReleaseDeps(deps, config) {
|
|
|
983
1053
|
function getAddedDevDepNames(config) {
|
|
984
1054
|
const deps = { ...ROOT_DEV_DEPS };
|
|
985
1055
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
986
|
-
deps["@bensandee/config"] = "0.9.
|
|
987
|
-
deps["@bensandee/tooling"] = "0.
|
|
1056
|
+
deps["@bensandee/config"] = "0.9.1";
|
|
1057
|
+
deps["@bensandee/tooling"] = "0.27.1";
|
|
988
1058
|
if (config.formatter === "oxfmt") deps["oxfmt"] = "0.35.0";
|
|
989
1059
|
if (config.formatter === "prettier") deps["prettier"] = "3.8.1";
|
|
990
1060
|
addReleaseDeps(deps, config);
|
|
@@ -1001,15 +1071,15 @@ async function generatePackageJson(ctx) {
|
|
|
1001
1071
|
format: formatScript
|
|
1002
1072
|
};
|
|
1003
1073
|
if (ctx.config.releaseStrategy === "changesets") allScripts["changeset"] = "changeset";
|
|
1004
|
-
if (ctx.config.releaseStrategy !== "none" && ctx.config.releaseStrategy !== "changesets") allScripts["trigger-release"] = "
|
|
1074
|
+
if (ctx.config.releaseStrategy !== "none" && ctx.config.releaseStrategy !== "changesets") allScripts["trigger-release"] = "bst release:trigger";
|
|
1005
1075
|
if (hasDockerPackages(ctx)) {
|
|
1006
|
-
allScripts["docker:build"] = "
|
|
1007
|
-
allScripts["docker:check"] = "
|
|
1076
|
+
allScripts["docker:build"] = "bst docker:build";
|
|
1077
|
+
allScripts["docker:check"] = "bst docker:check";
|
|
1008
1078
|
}
|
|
1009
1079
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1010
1080
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1011
|
-
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.
|
|
1012
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1081
|
+
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.1";
|
|
1082
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.27.1";
|
|
1013
1083
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.2";
|
|
1014
1084
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
1015
1085
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
@@ -1027,10 +1097,14 @@ async function generatePackageJson(ctx) {
|
|
|
1027
1097
|
changes.push("set type: \"module\"");
|
|
1028
1098
|
}
|
|
1029
1099
|
const existingScripts = pkg.scripts ?? {};
|
|
1100
|
+
for (const [key, value] of Object.entries(existingScripts)) if (typeof value === "string" && value.includes("pnpm exec tooling ")) {
|
|
1101
|
+
existingScripts[key] = migrateToolingBinary(value);
|
|
1102
|
+
changes.push(`migrated script: ${key}`);
|
|
1103
|
+
}
|
|
1030
1104
|
for (const [key, value] of Object.entries(allScripts)) if (!(key in existingScripts)) {
|
|
1031
1105
|
existingScripts[key] = value;
|
|
1032
1106
|
changes.push(`added script: ${key}`);
|
|
1033
|
-
} else if (key in MANAGED_SCRIPTS && !existingScripts[key]
|
|
1107
|
+
} else if (key in MANAGED_SCRIPTS && !matchesManagedScript(existingScripts[key] ?? "", MANAGED_SCRIPTS[key] ?? "")) {
|
|
1034
1108
|
existingScripts[key] = value;
|
|
1035
1109
|
changes.push(`updated script: ${key}`);
|
|
1036
1110
|
}
|
|
@@ -1770,8 +1844,14 @@ const ClaudeSettingsSchema = z.object({
|
|
|
1770
1844
|
});
|
|
1771
1845
|
function parseClaudeSettings(raw) {
|
|
1772
1846
|
try {
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1847
|
+
const json = JSON.parse(raw);
|
|
1848
|
+
const rawResult = z.record(z.string(), z.unknown()).safeParse(json);
|
|
1849
|
+
const settingsResult = ClaudeSettingsSchema.safeParse(json);
|
|
1850
|
+
if (!rawResult.success || !settingsResult.success) return void 0;
|
|
1851
|
+
return {
|
|
1852
|
+
settings: settingsResult.data,
|
|
1853
|
+
rawJson: rawResult.data
|
|
1854
|
+
};
|
|
1775
1855
|
} catch {
|
|
1776
1856
|
return;
|
|
1777
1857
|
}
|
|
@@ -1830,7 +1910,6 @@ function buildSettings(ctx) {
|
|
|
1830
1910
|
"Bash(wc *)",
|
|
1831
1911
|
"Bash(test *)",
|
|
1832
1912
|
"Bash([ *)",
|
|
1833
|
-
"Bash(find *)",
|
|
1834
1913
|
"Bash(grep *)",
|
|
1835
1914
|
"Bash(which *)",
|
|
1836
1915
|
"Bash(node -e *)",
|
|
@@ -1897,7 +1976,7 @@ function buildSettings(ctx) {
|
|
|
1897
1976
|
instructions: [
|
|
1898
1977
|
"Use pnpm, not npm/yarn/npx. Run binaries with `pnpm exec`.",
|
|
1899
1978
|
"No typecasts (as/any). Use zod schemas, type guards, or narrowing instead.",
|
|
1900
|
-
"Fix lint violations instead of suppressing them. Only add disable comments when suppression is genuinely the best option.",
|
|
1979
|
+
"Fix lint violations instead of suppressing them. Only add disable comments when suppression is genuinely the best option. For no-empty-function: add a `{ /* no-op */ }` comment body instead of a disable comment.",
|
|
1901
1980
|
"Prefer extensionless imports; if an extension is required, use .ts over .js."
|
|
1902
1981
|
],
|
|
1903
1982
|
enabledPlugins,
|
|
@@ -1921,27 +2000,44 @@ function writeOrMergeSettings(ctx, filePath, generated) {
|
|
|
1921
2000
|
action: "skipped",
|
|
1922
2001
|
description: "Could not parse existing settings"
|
|
1923
2002
|
};
|
|
1924
|
-
const
|
|
1925
|
-
const
|
|
1926
|
-
const
|
|
1927
|
-
const
|
|
1928
|
-
|
|
1929
|
-
const
|
|
1930
|
-
|
|
2003
|
+
const { settings, rawJson } = parsed;
|
|
2004
|
+
const missingAllow = generated.permissions.allow.filter((rule) => !settings.permissions.allow.includes(rule));
|
|
2005
|
+
const missingDeny = generated.permissions.deny.filter((rule) => !settings.permissions.deny.includes(rule));
|
|
2006
|
+
const mergedInstructions = [...settings.instructions];
|
|
2007
|
+
let instructionChanges = 0;
|
|
2008
|
+
for (const inst of generated.instructions) {
|
|
2009
|
+
if (mergedInstructions.includes(inst)) continue;
|
|
2010
|
+
const prefixIdx = mergedInstructions.findIndex((e) => inst.startsWith(e) || e.startsWith(inst));
|
|
2011
|
+
if (prefixIdx !== -1) mergedInstructions[prefixIdx] = inst;
|
|
2012
|
+
else mergedInstructions.push(inst);
|
|
2013
|
+
instructionChanges++;
|
|
2014
|
+
}
|
|
2015
|
+
const missingPlugins = Object.entries(generated.enabledPlugins).filter(([key]) => !(key in settings.enabledPlugins));
|
|
2016
|
+
const missingMarketplaces = Object.entries(generated.extraKnownMarketplaces).filter(([key]) => !(key in settings.extraKnownMarketplaces));
|
|
2017
|
+
const changed = missingAllow.length + missingDeny.length + instructionChanges + missingPlugins.length + missingMarketplaces.length;
|
|
2018
|
+
if (changed === 0) return {
|
|
1931
2019
|
filePath,
|
|
1932
2020
|
action: "skipped",
|
|
1933
2021
|
description: "Already has all rules and instructions"
|
|
1934
2022
|
};
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2023
|
+
rawJson["permissions"] = {
|
|
2024
|
+
allow: [...settings.permissions.allow, ...missingAllow],
|
|
2025
|
+
deny: [...settings.permissions.deny, ...missingDeny]
|
|
2026
|
+
};
|
|
2027
|
+
rawJson["instructions"] = mergedInstructions;
|
|
2028
|
+
const updatedPlugins = { ...settings.enabledPlugins };
|
|
2029
|
+
for (const [key, value] of missingPlugins) updatedPlugins[key] = value;
|
|
2030
|
+
const updatedMarketplaces = { ...settings.extraKnownMarketplaces };
|
|
2031
|
+
for (const [key, value] of missingMarketplaces) updatedMarketplaces[key] = value;
|
|
2032
|
+
if (Object.keys(updatedPlugins).length > 0) rawJson["enabledPlugins"] = updatedPlugins;
|
|
2033
|
+
else delete rawJson["enabledPlugins"];
|
|
2034
|
+
if (Object.keys(updatedMarketplaces).length > 0) rawJson["extraKnownMarketplaces"] = updatedMarketplaces;
|
|
2035
|
+
else delete rawJson["extraKnownMarketplaces"];
|
|
2036
|
+
ctx.write(filePath, JSON.stringify(rawJson, null, 2) + "\n");
|
|
1941
2037
|
return {
|
|
1942
2038
|
filePath,
|
|
1943
2039
|
action: "updated",
|
|
1944
|
-
description: `
|
|
2040
|
+
description: `Updated ${String(changed)} rules/instructions`
|
|
1945
2041
|
};
|
|
1946
2042
|
}
|
|
1947
2043
|
ctx.write(filePath, serializeSettings$1(generated));
|
|
@@ -2115,13 +2211,13 @@ permissions:
|
|
|
2115
2211
|
- name: Release
|
|
2116
2212
|
env:
|
|
2117
2213
|
GITHUB_TOKEN: \${{ github.token }}
|
|
2118
|
-
run: pnpm exec
|
|
2214
|
+
run: pnpm exec bst release:simple` : `
|
|
2119
2215
|
- name: Release
|
|
2120
2216
|
env:
|
|
2121
2217
|
FORGEJO_SERVER_URL: \${{ github.server_url }}
|
|
2122
2218
|
FORGEJO_REPOSITORY: \${{ github.repository }}
|
|
2123
2219
|
FORGEJO_TOKEN: \${{ secrets.FORGEJO_TOKEN }}
|
|
2124
|
-
run: pnpm exec
|
|
2220
|
+
run: pnpm exec bst release:simple`;
|
|
2125
2221
|
return `${workflowSchemaComment(ci)}name: Release
|
|
2126
2222
|
on:
|
|
2127
2223
|
workflow_dispatch:
|
|
@@ -2161,7 +2257,7 @@ function changesetsReleaseStep(ci, publishesNpm) {
|
|
|
2161
2257
|
FORGEJO_TOKEN: actionsExpr("secrets.FORGEJO_TOKEN"),
|
|
2162
2258
|
...publishesNpm && { NODE_AUTH_TOKEN: actionsExpr("secrets.NPM_TOKEN") }
|
|
2163
2259
|
},
|
|
2164
|
-
run: "pnpm exec
|
|
2260
|
+
run: "pnpm exec bst release:changesets"
|
|
2165
2261
|
}
|
|
2166
2262
|
};
|
|
2167
2263
|
}
|
|
@@ -2209,13 +2305,13 @@ function requiredReleaseSteps(strategy, nodeVersionYaml, publishesNpm) {
|
|
|
2209
2305
|
case "simple":
|
|
2210
2306
|
steps.push({
|
|
2211
2307
|
match: { run: "release:simple" },
|
|
2212
|
-
step: { run: "pnpm exec
|
|
2308
|
+
step: { run: "pnpm exec bst release:simple" }
|
|
2213
2309
|
});
|
|
2214
2310
|
break;
|
|
2215
2311
|
case "changesets":
|
|
2216
2312
|
steps.push({
|
|
2217
2313
|
match: { run: "changeset" },
|
|
2218
|
-
step: { run: "pnpm exec
|
|
2314
|
+
step: { run: "pnpm exec bst release:changesets" }
|
|
2219
2315
|
});
|
|
2220
2316
|
break;
|
|
2221
2317
|
}
|
|
@@ -2230,18 +2326,30 @@ function buildWorkflow(strategy, ci, nodeVersionYaml, publishesNpm) {
|
|
|
2230
2326
|
}
|
|
2231
2327
|
function generateChangesetsReleaseCi(ctx, publishesNpm) {
|
|
2232
2328
|
const ciPath = ciWorkflowPath(ctx.config.ci, ctx.config.releaseStrategy);
|
|
2233
|
-
const
|
|
2234
|
-
if (!
|
|
2329
|
+
const raw = ctx.read(ciPath);
|
|
2330
|
+
if (!raw) return {
|
|
2235
2331
|
filePath: ciPath,
|
|
2236
2332
|
action: "skipped",
|
|
2237
2333
|
description: "CI workflow not found — run check generator first"
|
|
2238
2334
|
};
|
|
2335
|
+
const existing = migrateToolingBinary(raw);
|
|
2239
2336
|
const merged = mergeWorkflowSteps(existing, "check", [changesetsReleaseStep(ctx.config.ci, publishesNpm)]);
|
|
2240
|
-
if (!merged.changed)
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2337
|
+
if (!merged.changed) {
|
|
2338
|
+
if (existing !== raw) {
|
|
2339
|
+
const withComment = ensureSchemaComment(existing, ctx.config.ci);
|
|
2340
|
+
ctx.write(ciPath, withComment);
|
|
2341
|
+
return {
|
|
2342
|
+
filePath: ciPath,
|
|
2343
|
+
action: "updated",
|
|
2344
|
+
description: "Migrated tooling binary name in CI workflow"
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
return {
|
|
2348
|
+
filePath: ciPath,
|
|
2349
|
+
action: "skipped",
|
|
2350
|
+
description: "Release step in CI workflow already up to date"
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2245
2353
|
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
2246
2354
|
ctx.write(ciPath, withComment);
|
|
2247
2355
|
return {
|
|
@@ -2257,7 +2365,7 @@ async function generateReleaseCi(ctx) {
|
|
|
2257
2365
|
action: "skipped",
|
|
2258
2366
|
description: "Release CI workflow not applicable"
|
|
2259
2367
|
};
|
|
2260
|
-
const publishesNpm =
|
|
2368
|
+
const publishesNpm = ctx.config.publishNpm === true;
|
|
2261
2369
|
if (ctx.config.releaseStrategy === "changesets") return generateChangesetsReleaseCi(ctx, publishesNpm);
|
|
2262
2370
|
const isGitHub = ctx.config.ci === "github";
|
|
2263
2371
|
const workflowPath = isGitHub ? ".github/workflows/release.yml" : ".forgejo/workflows/release.yml";
|
|
@@ -2269,30 +2377,33 @@ async function generateReleaseCi(ctx) {
|
|
|
2269
2377
|
description: "Release CI workflow not applicable"
|
|
2270
2378
|
};
|
|
2271
2379
|
if (ctx.exists(workflowPath)) {
|
|
2272
|
-
const
|
|
2273
|
-
if (
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2380
|
+
const raw = ctx.read(workflowPath);
|
|
2381
|
+
if (raw) {
|
|
2382
|
+
const existing = migrateToolingBinary(raw);
|
|
2383
|
+
if (existing === content || ensureSchemaComment(existing, ctx.config.ci) === content) {
|
|
2384
|
+
if (existing !== raw) {
|
|
2385
|
+
ctx.write(workflowPath, ensureSchemaComment(existing, ctx.config.ci));
|
|
2386
|
+
return {
|
|
2387
|
+
filePath: workflowPath,
|
|
2388
|
+
action: "updated",
|
|
2389
|
+
description: "Migrated tooling binary name in release workflow"
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2283
2392
|
return {
|
|
2284
2393
|
filePath: workflowPath,
|
|
2285
|
-
action: "
|
|
2286
|
-
description: "
|
|
2394
|
+
action: "skipped",
|
|
2395
|
+
description: "Release workflow already up to date"
|
|
2287
2396
|
};
|
|
2288
2397
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2398
|
+
const merged = mergeWorkflowSteps(existing, "release", requiredReleaseSteps(ctx.config.releaseStrategy, nodeVersionYaml, publishesNpm));
|
|
2399
|
+
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
2400
|
+
if (!merged.changed) {
|
|
2401
|
+
if (withComment !== raw) {
|
|
2291
2402
|
ctx.write(workflowPath, withComment);
|
|
2292
2403
|
return {
|
|
2293
2404
|
filePath: workflowPath,
|
|
2294
2405
|
action: "updated",
|
|
2295
|
-
description: "Added
|
|
2406
|
+
description: existing !== raw ? "Migrated tooling binary name in release workflow" : "Added schema comment to release workflow"
|
|
2296
2407
|
};
|
|
2297
2408
|
}
|
|
2298
2409
|
return {
|
|
@@ -2301,11 +2412,11 @@ async function generateReleaseCi(ctx) {
|
|
|
2301
2412
|
description: "Existing release workflow preserved"
|
|
2302
2413
|
};
|
|
2303
2414
|
}
|
|
2304
|
-
ctx.write(workflowPath,
|
|
2415
|
+
ctx.write(workflowPath, withComment);
|
|
2305
2416
|
return {
|
|
2306
2417
|
filePath: workflowPath,
|
|
2307
2418
|
action: "updated",
|
|
2308
|
-
description: "
|
|
2419
|
+
description: "Added missing steps to release workflow"
|
|
2309
2420
|
};
|
|
2310
2421
|
}
|
|
2311
2422
|
return {
|
|
@@ -3085,15 +3196,19 @@ function contextAsDockerReader(ctx) {
|
|
|
3085
3196
|
}
|
|
3086
3197
|
/** Log what was detected so the user understands generator decisions. */
|
|
3087
3198
|
function logDetectionSummary(ctx) {
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3199
|
+
if (ctx.config.publishDocker) {
|
|
3200
|
+
const dockerPackages = detectDockerPackages(contextAsDockerReader(ctx), ctx.targetDir, ctx.config.name);
|
|
3201
|
+
if (dockerPackages.length > 0) log$2.info(`Docker images: ${dockerPackages.map((pkg) => pkg.imageName).join(", ")}`);
|
|
3202
|
+
}
|
|
3203
|
+
if (ctx.config.publishNpm) {
|
|
3204
|
+
const publishable = getPublishablePackages(ctx.targetDir, ctx.config.structure, ctx.packageJson);
|
|
3205
|
+
if (publishable.length > 0) log$2.info(`npm packages: ${publishable.map((pkg) => pkg.name).join(", ")}`);
|
|
3206
|
+
}
|
|
3092
3207
|
}
|
|
3093
3208
|
async function runInit(config, options = {}) {
|
|
3094
3209
|
const detected = detectProject(config.targetDir);
|
|
3095
3210
|
const { ctx, archivedFiles } = createContext(config, options.confirmOverwrite ?? (async (relativePath) => {
|
|
3096
|
-
const result = await
|
|
3211
|
+
const result = await select({
|
|
3097
3212
|
message: `${relativePath} already exists. What do you want to do?`,
|
|
3098
3213
|
options: [{
|
|
3099
3214
|
value: "overwrite",
|
|
@@ -3103,10 +3218,9 @@ async function runInit(config, options = {}) {
|
|
|
3103
3218
|
label: "Skip"
|
|
3104
3219
|
}]
|
|
3105
3220
|
});
|
|
3106
|
-
if (
|
|
3221
|
+
if (isCancel(result)) return "skip";
|
|
3107
3222
|
return result;
|
|
3108
3223
|
}));
|
|
3109
|
-
if (config.releaseStrategy !== "none" && !ctx.packageJson?.repository) p.log.warn(`package.json is missing a "repository" field — required for release strategy "${config.releaseStrategy}"`);
|
|
3110
3224
|
logDetectionSummary(ctx);
|
|
3111
3225
|
const results = await runGenerators(ctx);
|
|
3112
3226
|
const alreadyArchived = new Set(results.filter((r) => r.action === "archived").map((r) => r.filePath));
|
|
@@ -3117,8 +3231,12 @@ async function runInit(config, options = {}) {
|
|
|
3117
3231
|
});
|
|
3118
3232
|
const created = results.filter((r) => r.action === "created");
|
|
3119
3233
|
const updated = results.filter((r) => r.action === "updated");
|
|
3120
|
-
|
|
3121
|
-
|
|
3234
|
+
const hasChanges = created.length > 0 || updated.length > 0 || archivedFiles.length > 0;
|
|
3235
|
+
const prompt = generateMigratePrompt(results, config, detected);
|
|
3236
|
+
const promptPath = ".tooling-migrate.md";
|
|
3237
|
+
ctx.write(promptPath, prompt);
|
|
3238
|
+
if (!hasChanges && options.noPrompt) {
|
|
3239
|
+
log$2.success("Repository is up to date.");
|
|
3122
3240
|
return results;
|
|
3123
3241
|
}
|
|
3124
3242
|
if (results.some((r) => r.action === "archived" && r.filePath.startsWith(".husky/"))) try {
|
|
@@ -3131,18 +3249,15 @@ async function runInit(config, options = {}) {
|
|
|
3131
3249
|
const summaryLines = [];
|
|
3132
3250
|
if (created.length > 0) summaryLines.push(`Created: ${created.map((r) => r.filePath).join(", ")}`);
|
|
3133
3251
|
if (updated.length > 0) summaryLines.push(`Updated: ${updated.map((r) => r.filePath).join(", ")}`);
|
|
3134
|
-
|
|
3252
|
+
note(summaryLines.join("\n"), "Summary");
|
|
3135
3253
|
if (!options.noPrompt) {
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
ctx.write(promptPath, prompt);
|
|
3139
|
-
p.log.info(`Migration prompt written to ${promptPath}`);
|
|
3140
|
-
p.log.info("In Claude Code, run: \"Execute the steps in .tooling-migrate.md\"");
|
|
3254
|
+
log$2.info(`Migration prompt written to ${promptPath}`);
|
|
3255
|
+
log$2.info("In Claude Code, run: \"Execute the steps in .tooling-migrate.md\"");
|
|
3141
3256
|
}
|
|
3142
3257
|
const bensandeeDeps = getAddedDevDepNames(config).filter((name) => name.startsWith("@bensandee/"));
|
|
3143
3258
|
const hasLockfile = ctx.exists("pnpm-lock.yaml");
|
|
3144
3259
|
if (bensandeeDeps.length > 0 && hasLockfile) {
|
|
3145
|
-
|
|
3260
|
+
log$2.info("Updating @bensandee/* packages...");
|
|
3146
3261
|
try {
|
|
3147
3262
|
execSync(`pnpm update --latest ${bensandeeDeps.join(" ")}`, {
|
|
3148
3263
|
cwd: config.targetDir,
|
|
@@ -3150,10 +3265,17 @@ async function runInit(config, options = {}) {
|
|
|
3150
3265
|
timeout: 6e4
|
|
3151
3266
|
});
|
|
3152
3267
|
} catch (_error) {
|
|
3153
|
-
|
|
3268
|
+
log$2.warn("Could not update @bensandee/* packages — run pnpm install manually");
|
|
3154
3269
|
}
|
|
3155
3270
|
}
|
|
3156
|
-
|
|
3271
|
+
if (hasChanges && ctx.exists("package.json")) try {
|
|
3272
|
+
execSync("pnpm format", {
|
|
3273
|
+
cwd: config.targetDir,
|
|
3274
|
+
stdio: "ignore",
|
|
3275
|
+
timeout: 3e4
|
|
3276
|
+
});
|
|
3277
|
+
} catch (_error) {}
|
|
3278
|
+
note([
|
|
3157
3279
|
"1. Run: pnpm install",
|
|
3158
3280
|
"2. Run: pnpm check",
|
|
3159
3281
|
...options.noPrompt ? [] : ["3. In Claude Code, run: \"Execute the steps in .tooling-migrate.md\""]
|
|
@@ -3235,22 +3357,22 @@ async function runCheck(targetDir) {
|
|
|
3235
3357
|
return true;
|
|
3236
3358
|
});
|
|
3237
3359
|
if (actionable.length === 0) {
|
|
3238
|
-
|
|
3360
|
+
log$2.success("Repository is up to date.");
|
|
3239
3361
|
return 0;
|
|
3240
3362
|
}
|
|
3241
|
-
|
|
3363
|
+
log$2.warn(`${actionable.length} file(s) would be changed by repo:sync`);
|
|
3242
3364
|
for (const r of actionable) {
|
|
3243
|
-
|
|
3365
|
+
log$2.info(` ${r.action}: ${r.filePath} — ${r.description}`);
|
|
3244
3366
|
const newContent = pendingWrites.get(r.filePath);
|
|
3245
3367
|
if (!newContent) continue;
|
|
3246
3368
|
const existingPath = path.join(targetDir, r.filePath);
|
|
3247
3369
|
const existing = existsSync(existingPath) ? readFileSync(existingPath, "utf-8") : void 0;
|
|
3248
3370
|
if (!existing) {
|
|
3249
3371
|
const lineCount = newContent.split("\n").length - 1;
|
|
3250
|
-
|
|
3372
|
+
log$2.info(` + ${lineCount} new lines`);
|
|
3251
3373
|
} else {
|
|
3252
3374
|
const diff = lineDiff(existing, newContent);
|
|
3253
|
-
for (const line of diff)
|
|
3375
|
+
for (const line of diff) log$2.info(` ${line}`);
|
|
3254
3376
|
}
|
|
3255
3377
|
}
|
|
3256
3378
|
return 1;
|
|
@@ -3497,7 +3619,7 @@ async function createRelease(executor, conn, tag) {
|
|
|
3497
3619
|
//#region src/release/log.ts
|
|
3498
3620
|
/** Log a debug message when verbose mode is enabled. */
|
|
3499
3621
|
function debug(config, message) {
|
|
3500
|
-
if (config.verbose)
|
|
3622
|
+
if (config.verbose) log$2.info(`[debug] ${message}`);
|
|
3501
3623
|
}
|
|
3502
3624
|
/** Log the result of an exec call when verbose mode is enabled. */
|
|
3503
3625
|
function debugExec(config, label, result) {
|
|
@@ -3505,7 +3627,7 @@ function debugExec(config, label, result) {
|
|
|
3505
3627
|
const lines = [`[debug] ${label} (exit code ${String(result.exitCode)})`];
|
|
3506
3628
|
if (result.stdout.trim()) lines.push(` stdout: ${result.stdout.trim()}`);
|
|
3507
3629
|
if (result.stderr.trim()) lines.push(` stderr: ${result.stderr.trim()}`);
|
|
3508
|
-
|
|
3630
|
+
log$2.info(lines.join("\n"));
|
|
3509
3631
|
}
|
|
3510
3632
|
//#endregion
|
|
3511
3633
|
//#region src/release/version.ts
|
|
@@ -3577,7 +3699,7 @@ function buildPrContent(executor, cwd, packagesBefore) {
|
|
|
3577
3699
|
}
|
|
3578
3700
|
/** Mode 1: version packages and create/update a PR. */
|
|
3579
3701
|
async function runVersionMode(executor, config) {
|
|
3580
|
-
|
|
3702
|
+
log$2.info("Changesets detected — versioning packages");
|
|
3581
3703
|
const packagesBefore = executor.listWorkspacePackages(config.cwd);
|
|
3582
3704
|
debug(config, `Packages before versioning: ${packagesBefore.map((pkg) => `${pkg.name}@${pkg.version}`).join(", ") || "(none)"}`);
|
|
3583
3705
|
const changesetConfigPath = path.join(config.cwd, ".changeset", "config.json");
|
|
@@ -3603,19 +3725,19 @@ async function runVersionMode(executor, config) {
|
|
|
3603
3725
|
const addResult = executor.exec("git add -A", { cwd: config.cwd });
|
|
3604
3726
|
if (addResult.exitCode !== 0) throw new FatalError(`git add failed: ${addResult.stderr || addResult.stdout}`);
|
|
3605
3727
|
const remainingChangesets = executor.listChangesetFiles(config.cwd);
|
|
3606
|
-
if (remainingChangesets.length > 0)
|
|
3728
|
+
if (remainingChangesets.length > 0) log$2.warn(`Changeset files still present after versioning: ${remainingChangesets.join(", ")}`);
|
|
3607
3729
|
debug(config, `Changeset files after versioning: ${remainingChangesets.length > 0 ? remainingChangesets.join(", ") : "(none — all consumed)"}`);
|
|
3608
3730
|
const commitResult = executor.exec("git commit -m \"chore: version packages\"", { cwd: config.cwd });
|
|
3609
3731
|
debugExec(config, "git commit", commitResult);
|
|
3610
3732
|
if (commitResult.exitCode !== 0) {
|
|
3611
|
-
|
|
3733
|
+
log$2.info("Nothing to commit after versioning");
|
|
3612
3734
|
return {
|
|
3613
3735
|
mode: "version",
|
|
3614
3736
|
pr: "none"
|
|
3615
3737
|
};
|
|
3616
3738
|
}
|
|
3617
3739
|
if (config.dryRun) {
|
|
3618
|
-
|
|
3740
|
+
log$2.info("[dry-run] Would push and create/update PR");
|
|
3619
3741
|
return {
|
|
3620
3742
|
mode: "version",
|
|
3621
3743
|
pr: "none"
|
|
@@ -3638,7 +3760,7 @@ async function runVersionMode(executor, config) {
|
|
|
3638
3760
|
base: "main",
|
|
3639
3761
|
body
|
|
3640
3762
|
});
|
|
3641
|
-
|
|
3763
|
+
log$2.info("Created version PR");
|
|
3642
3764
|
return {
|
|
3643
3765
|
mode: "version",
|
|
3644
3766
|
pr: "created"
|
|
@@ -3648,7 +3770,7 @@ async function runVersionMode(executor, config) {
|
|
|
3648
3770
|
title,
|
|
3649
3771
|
body
|
|
3650
3772
|
});
|
|
3651
|
-
|
|
3773
|
+
log$2.info(`Updated version PR #${String(existingPr)}`);
|
|
3652
3774
|
return {
|
|
3653
3775
|
mode: "version",
|
|
3654
3776
|
pr: "updated"
|
|
@@ -3673,7 +3795,7 @@ async function retryAsync(fn) {
|
|
|
3673
3795
|
}
|
|
3674
3796
|
/** Mode 2: publish to npm, push tags, and create Forgejo releases. */
|
|
3675
3797
|
async function runPublishMode(executor, config) {
|
|
3676
|
-
|
|
3798
|
+
log$2.info("No changesets — publishing packages");
|
|
3677
3799
|
const publishResult = executor.exec("pnpm changeset publish", { cwd: config.cwd });
|
|
3678
3800
|
debugExec(config, "pnpm changeset publish", publishResult);
|
|
3679
3801
|
if (publishResult.exitCode !== 0) throw new FatalError(`pnpm changeset publish failed (exit code ${String(publishResult.exitCode)}):\n${publishResult.stderr}`);
|
|
@@ -3688,11 +3810,11 @@ async function runPublishMode(executor, config) {
|
|
|
3688
3810
|
debug(config, `Reconciled tags to push: ${tagsToPush.length > 0 ? tagsToPush.join(", ") : "(none)"}`);
|
|
3689
3811
|
if (config.dryRun) {
|
|
3690
3812
|
if (tagsToPush.length === 0) {
|
|
3691
|
-
|
|
3813
|
+
log$2.info("No packages were published");
|
|
3692
3814
|
return { mode: "none" };
|
|
3693
3815
|
}
|
|
3694
|
-
|
|
3695
|
-
|
|
3816
|
+
log$2.info(`Tags to process: ${tagsToPush.join(", ")}`);
|
|
3817
|
+
log$2.info("[dry-run] Would push tags and create releases");
|
|
3696
3818
|
return {
|
|
3697
3819
|
mode: "publish",
|
|
3698
3820
|
tags: tagsToPush
|
|
@@ -3708,10 +3830,10 @@ async function runPublishMode(executor, config) {
|
|
|
3708
3830
|
for (const tag of remoteExpectedTags) if (!await findRelease(executor, conn, tag)) tagsWithMissingReleases.push(tag);
|
|
3709
3831
|
const allTags = [...tagsToPush, ...tagsWithMissingReleases];
|
|
3710
3832
|
if (allTags.length === 0) {
|
|
3711
|
-
|
|
3833
|
+
log$2.info("No packages were published");
|
|
3712
3834
|
return { mode: "none" };
|
|
3713
3835
|
}
|
|
3714
|
-
|
|
3836
|
+
log$2.info(`Tags to process: ${allTags.join(", ")}`);
|
|
3715
3837
|
const errors = [];
|
|
3716
3838
|
for (const tag of allTags) try {
|
|
3717
3839
|
if (!remoteSet.has(tag)) {
|
|
@@ -3722,7 +3844,7 @@ async function runPublishMode(executor, config) {
|
|
|
3722
3844
|
const pushTagResult = executor.exec(`git push origin refs/tags/${tag}`, { cwd: config.cwd });
|
|
3723
3845
|
if (pushTagResult.exitCode !== 0) throw new FatalError(`Failed to push tag ${tag}: ${pushTagResult.stderr || pushTagResult.stdout}`);
|
|
3724
3846
|
}
|
|
3725
|
-
if (await findRelease(executor, conn, tag))
|
|
3847
|
+
if (await findRelease(executor, conn, tag)) log$2.warn(`Release for ${tag} already exists — skipping`);
|
|
3726
3848
|
else {
|
|
3727
3849
|
await retryAsync(async () => {
|
|
3728
3850
|
try {
|
|
@@ -3732,14 +3854,14 @@ async function runPublishMode(executor, config) {
|
|
|
3732
3854
|
throw error;
|
|
3733
3855
|
}
|
|
3734
3856
|
});
|
|
3735
|
-
|
|
3857
|
+
log$2.info(`Created release for ${tag}`);
|
|
3736
3858
|
}
|
|
3737
3859
|
} catch (error) {
|
|
3738
3860
|
errors.push({
|
|
3739
3861
|
tag,
|
|
3740
3862
|
error
|
|
3741
3863
|
});
|
|
3742
|
-
|
|
3864
|
+
log$2.warn(`Failed to process ${tag}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3743
3865
|
}
|
|
3744
3866
|
if (errors.length > 0) throw new TransientError(`Failed to create releases for: ${errors.map((e) => e.tag).join(", ")}`);
|
|
3745
3867
|
return {
|
|
@@ -3909,12 +4031,12 @@ async function triggerForgejo(conn, ref) {
|
|
|
3909
4031
|
body: JSON.stringify({ ref })
|
|
3910
4032
|
});
|
|
3911
4033
|
if (!res.ok) throw new FatalError(`Failed to trigger Forgejo workflow: ${res.status} ${res.statusText}`);
|
|
3912
|
-
|
|
4034
|
+
log$2.info(`Triggered release workflow on Forgejo (ref: ${ref})`);
|
|
3913
4035
|
}
|
|
3914
4036
|
function triggerGitHub(ref) {
|
|
3915
4037
|
const result = createRealExecutor().exec(`gh workflow run release.yml --ref ${ref}`, { cwd: process.cwd() });
|
|
3916
4038
|
if (result.exitCode !== 0) throw new FatalError(`Failed to trigger GitHub workflow: ${result.stderr || result.stdout || "unknown error"}`);
|
|
3917
|
-
|
|
4039
|
+
log$2.info(`Triggered release workflow on GitHub (ref: ${ref})`);
|
|
3918
4040
|
}
|
|
3919
4041
|
//#endregion
|
|
3920
4042
|
//#region src/commands/forgejo-create-release.ts
|
|
@@ -3934,11 +4056,11 @@ const createForgejoReleaseCommand = defineCommand({
|
|
|
3934
4056
|
const executor = createRealExecutor();
|
|
3935
4057
|
const conn = resolved.conn;
|
|
3936
4058
|
if (await findRelease(executor, conn, args.tag)) {
|
|
3937
|
-
|
|
4059
|
+
log$2.info(`Release for ${args.tag} already exists — skipping`);
|
|
3938
4060
|
return;
|
|
3939
4061
|
}
|
|
3940
4062
|
await createRelease(executor, conn, args.tag);
|
|
3941
|
-
|
|
4063
|
+
log$2.info(`Created Forgejo release for ${args.tag}`);
|
|
3942
4064
|
}
|
|
3943
4065
|
});
|
|
3944
4066
|
//#endregion
|
|
@@ -3965,26 +4087,26 @@ async function mergeForgejo(conn, dryRun) {
|
|
|
3965
4087
|
const prNumber = await findOpenPr(executor, conn, HEAD_BRANCH);
|
|
3966
4088
|
if (prNumber === null) throw new FatalError(`No open PR found for branch ${HEAD_BRANCH}`);
|
|
3967
4089
|
if (dryRun) {
|
|
3968
|
-
|
|
4090
|
+
log$2.info(`[dry-run] Would merge PR #${String(prNumber)} and delete branch ${HEAD_BRANCH}`);
|
|
3969
4091
|
return;
|
|
3970
4092
|
}
|
|
3971
4093
|
await mergePr(executor, conn, prNumber, {
|
|
3972
4094
|
method: "merge",
|
|
3973
4095
|
deleteBranch: true
|
|
3974
4096
|
});
|
|
3975
|
-
|
|
4097
|
+
log$2.info(`Merged PR #${String(prNumber)} and deleted branch ${HEAD_BRANCH}`);
|
|
3976
4098
|
}
|
|
3977
4099
|
function mergeGitHub(dryRun) {
|
|
3978
4100
|
const executor = createRealExecutor();
|
|
3979
4101
|
if (dryRun) {
|
|
3980
4102
|
const prNum = executor.exec(`gh pr view ${HEAD_BRANCH} --json number --jq .number`, { cwd: process.cwd() }).stdout.trim();
|
|
3981
4103
|
if (!prNum) throw new FatalError(`No open PR found for branch ${HEAD_BRANCH}`);
|
|
3982
|
-
|
|
4104
|
+
log$2.info(`[dry-run] Would merge PR #${prNum} and delete branch ${HEAD_BRANCH}`);
|
|
3983
4105
|
return;
|
|
3984
4106
|
}
|
|
3985
4107
|
const result = executor.exec(`gh pr merge ${HEAD_BRANCH} --merge --delete-branch`, { cwd: process.cwd() });
|
|
3986
4108
|
if (result.exitCode !== 0) throw new FatalError(`Failed to merge PR: ${result.stderr || result.stdout || "unknown error"}`);
|
|
3987
|
-
|
|
4109
|
+
log$2.info(`Merged changesets PR and deleted branch ${HEAD_BRANCH}`);
|
|
3988
4110
|
}
|
|
3989
4111
|
//#endregion
|
|
3990
4112
|
//#region src/release/simple.ts
|
|
@@ -4017,7 +4139,7 @@ function readVersion(executor, cwd) {
|
|
|
4017
4139
|
/** Run the full commit-and-tag-version release flow. */
|
|
4018
4140
|
async function runSimpleRelease(executor, config) {
|
|
4019
4141
|
const command = buildCommand(config);
|
|
4020
|
-
|
|
4142
|
+
log$2.info(`Running: ${command}`);
|
|
4021
4143
|
const versionResult = executor.exec(command, { cwd: config.cwd });
|
|
4022
4144
|
debugExec(config, "commit-and-tag-version", versionResult);
|
|
4023
4145
|
if (versionResult.exitCode !== 0) throw new FatalError(`commit-and-tag-version failed (exit code ${String(versionResult.exitCode)}):\n${versionResult.stderr || versionResult.stdout}`);
|
|
@@ -4027,12 +4149,12 @@ async function runSimpleRelease(executor, config) {
|
|
|
4027
4149
|
debugExec(config, "git describe", tagResult);
|
|
4028
4150
|
const tag = tagResult.stdout.trim();
|
|
4029
4151
|
if (!tag) throw new FatalError("Could not determine the new tag from git describe");
|
|
4030
|
-
|
|
4152
|
+
log$2.info(`Version ${version} tagged as ${tag}`);
|
|
4031
4153
|
if (config.dryRun) {
|
|
4032
4154
|
const slidingTags = config.noSlidingTags ? [] : computeSlidingTags(version);
|
|
4033
|
-
|
|
4034
|
-
if (slidingTags.length > 0)
|
|
4035
|
-
if (!config.noRelease && config.platform)
|
|
4155
|
+
log$2.info(`[dry-run] Would push to origin with --follow-tags`);
|
|
4156
|
+
if (slidingTags.length > 0) log$2.info(`[dry-run] Would create sliding tags: ${slidingTags.join(", ")}`);
|
|
4157
|
+
if (!config.noRelease && config.platform) log$2.info(`[dry-run] Would create ${config.platform.type} release for ${tag}`);
|
|
4036
4158
|
return {
|
|
4037
4159
|
version,
|
|
4038
4160
|
tag,
|
|
@@ -4049,7 +4171,7 @@ async function runSimpleRelease(executor, config) {
|
|
|
4049
4171
|
debugExec(config, "git push", pushResult);
|
|
4050
4172
|
if (pushResult.exitCode !== 0) throw new FatalError(`git push failed (exit code ${String(pushResult.exitCode)}):\n${pushResult.stderr || pushResult.stdout}`);
|
|
4051
4173
|
pushed = true;
|
|
4052
|
-
|
|
4174
|
+
log$2.info("Pushed to origin");
|
|
4053
4175
|
}
|
|
4054
4176
|
let slidingTags = [];
|
|
4055
4177
|
if (!config.noSlidingTags && pushed) {
|
|
@@ -4060,8 +4182,8 @@ async function runSimpleRelease(executor, config) {
|
|
|
4060
4182
|
}
|
|
4061
4183
|
const forcePushResult = executor.exec(`git push origin ${slidingTags.join(" ")} --force`, { cwd: config.cwd });
|
|
4062
4184
|
debugExec(config, "force-push sliding tags", forcePushResult);
|
|
4063
|
-
if (forcePushResult.exitCode !== 0)
|
|
4064
|
-
else
|
|
4185
|
+
if (forcePushResult.exitCode !== 0) log$2.warn(`Warning: Failed to push sliding tags: ${forcePushResult.stderr || forcePushResult.stdout}`);
|
|
4186
|
+
else log$2.info(`Created sliding tags: ${slidingTags.join(", ")}`);
|
|
4065
4187
|
}
|
|
4066
4188
|
let releaseCreated = false;
|
|
4067
4189
|
if (!config.noRelease && config.platform) releaseCreated = await createPlatformRelease(executor, config, tag);
|
|
@@ -4081,16 +4203,16 @@ async function createPlatformRelease(executor, config, tag) {
|
|
|
4081
4203
|
return false;
|
|
4082
4204
|
}
|
|
4083
4205
|
await createRelease(executor, config.platform.conn, tag);
|
|
4084
|
-
|
|
4206
|
+
log$2.info(`Created Forgejo release for ${tag}`);
|
|
4085
4207
|
return true;
|
|
4086
4208
|
}
|
|
4087
4209
|
const ghResult = executor.exec(`gh release create ${tag} --generate-notes`, { cwd: config.cwd });
|
|
4088
4210
|
debugExec(config, "gh release create", ghResult);
|
|
4089
4211
|
if (ghResult.exitCode !== 0) {
|
|
4090
|
-
|
|
4212
|
+
log$2.warn(`Warning: Failed to create GitHub release: ${ghResult.stderr || ghResult.stdout}`);
|
|
4091
4213
|
return false;
|
|
4092
4214
|
}
|
|
4093
|
-
|
|
4215
|
+
log$2.info(`Created GitHub release for ${tag}`);
|
|
4094
4216
|
return true;
|
|
4095
4217
|
}
|
|
4096
4218
|
//#endregion
|
|
@@ -4166,17 +4288,17 @@ const releaseSimpleCommand = defineCommand({
|
|
|
4166
4288
|
//#region src/commands/repo-run-checks.ts
|
|
4167
4289
|
const CHECKS = [
|
|
4168
4290
|
{ name: "build" },
|
|
4169
|
-
{ name: "docker:build" },
|
|
4170
4291
|
{ name: "typecheck" },
|
|
4171
4292
|
{ name: "lint" },
|
|
4172
4293
|
{ name: "test" },
|
|
4294
|
+
{ name: "docker:build" },
|
|
4295
|
+
{ name: "tooling:check" },
|
|
4296
|
+
{ name: "docker:check" },
|
|
4173
4297
|
{
|
|
4174
4298
|
name: "format",
|
|
4175
4299
|
args: "--check"
|
|
4176
4300
|
},
|
|
4177
|
-
{ name: "knip" }
|
|
4178
|
-
{ name: "tooling:check" },
|
|
4179
|
-
{ name: "docker:check" }
|
|
4301
|
+
{ name: "knip" }
|
|
4180
4302
|
];
|
|
4181
4303
|
/** Check if a name matches any skip pattern. Supports glob syntax via picomatch. */
|
|
4182
4304
|
function shouldSkip(name, patterns) {
|
|
@@ -4203,7 +4325,30 @@ function defaultExecCommand(cmd, cwd) {
|
|
|
4203
4325
|
return 1;
|
|
4204
4326
|
}
|
|
4205
4327
|
}
|
|
4206
|
-
const
|
|
4328
|
+
const rawLog = (msg) => console.log(msg);
|
|
4329
|
+
const ciReporter = {
|
|
4330
|
+
groupStart: (name) => rawLog(`::group::${name}`),
|
|
4331
|
+
groupEnd: () => rawLog("::endgroup::"),
|
|
4332
|
+
passed: (name, elapsedS) => rawLog(`✓ ${name} (${elapsedS}s)`),
|
|
4333
|
+
failed: (name, elapsedS) => {
|
|
4334
|
+
rawLog(`✗ ${name} (${elapsedS}s)`);
|
|
4335
|
+
rawLog(`::error::${name} failed`);
|
|
4336
|
+
},
|
|
4337
|
+
undefinedCheck: (name) => rawLog(`::error::${name} not defined in package.json`),
|
|
4338
|
+
skippedNotDefined: (names) => rawLog(`Skipped (not defined): ${names.join(", ")}`),
|
|
4339
|
+
allPassed: () => rawLog("✓ All checks passed"),
|
|
4340
|
+
anyFailed: (names) => rawLog(`::error::Failed checks: ${names.join(", ")}`)
|
|
4341
|
+
};
|
|
4342
|
+
const localReporter = {
|
|
4343
|
+
groupStart: (_name) => {},
|
|
4344
|
+
groupEnd: () => {},
|
|
4345
|
+
passed: (name) => log$2.success(name),
|
|
4346
|
+
failed: (name) => log$2.error(`${name} failed`),
|
|
4347
|
+
undefinedCheck: (name) => log$2.error(`${name} not defined in package.json`),
|
|
4348
|
+
skippedNotDefined: (names) => log$2.info(`Skipped (not defined): ${names.join(", ")}`),
|
|
4349
|
+
allPassed: () => log$2.success("All checks passed"),
|
|
4350
|
+
anyFailed: (names) => log$2.error(`Failed checks: ${names.join(", ")}`)
|
|
4351
|
+
};
|
|
4207
4352
|
function runRunChecks(targetDir, options = {}) {
|
|
4208
4353
|
const exec = options.execCommand ?? defaultExecCommand;
|
|
4209
4354
|
const getScripts = options.getScripts ?? defaultGetScripts;
|
|
@@ -4211,6 +4356,7 @@ function runRunChecks(targetDir, options = {}) {
|
|
|
4211
4356
|
const add = options.add ?? [];
|
|
4212
4357
|
const isCI = Boolean(process.env["CI"]);
|
|
4213
4358
|
const failFast = options.failFast ?? !isCI;
|
|
4359
|
+
const reporter = isCI ? ciReporter : localReporter;
|
|
4214
4360
|
const definedScripts = getScripts(targetDir);
|
|
4215
4361
|
const addedNames = new Set(add);
|
|
4216
4362
|
const allChecks = [...CHECKS, ...add.map((name) => ({ name }))];
|
|
@@ -4220,29 +4366,30 @@ function runRunChecks(targetDir, options = {}) {
|
|
|
4220
4366
|
if (shouldSkip(check.name, skip)) continue;
|
|
4221
4367
|
if (!definedScripts.has(check.name)) {
|
|
4222
4368
|
if (addedNames.has(check.name)) {
|
|
4223
|
-
|
|
4369
|
+
reporter.undefinedCheck(check.name);
|
|
4224
4370
|
failures.push(check.name);
|
|
4225
4371
|
} else notDefined.push(check.name);
|
|
4226
4372
|
continue;
|
|
4227
4373
|
}
|
|
4228
4374
|
const cmd = check.args ? `pnpm run ${check.name} ${check.args}` : `pnpm run ${check.name}`;
|
|
4229
|
-
|
|
4375
|
+
reporter.groupStart(check.name);
|
|
4376
|
+
const start = Date.now();
|
|
4230
4377
|
const exitCode = exec(cmd, targetDir);
|
|
4231
|
-
|
|
4232
|
-
|
|
4378
|
+
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
4379
|
+
reporter.groupEnd();
|
|
4380
|
+
if (exitCode === 0) reporter.passed(check.name, elapsed);
|
|
4233
4381
|
else {
|
|
4234
|
-
|
|
4235
|
-
p.log.error(`${check.name} failed`);
|
|
4382
|
+
reporter.failed(check.name, elapsed);
|
|
4236
4383
|
failures.push(check.name);
|
|
4237
4384
|
if (failFast) return 1;
|
|
4238
4385
|
}
|
|
4239
4386
|
}
|
|
4240
|
-
if (notDefined.length > 0)
|
|
4387
|
+
if (notDefined.length > 0) reporter.skippedNotDefined(notDefined);
|
|
4241
4388
|
if (failures.length > 0) {
|
|
4242
|
-
|
|
4389
|
+
reporter.anyFailed(failures);
|
|
4243
4390
|
return 1;
|
|
4244
4391
|
}
|
|
4245
|
-
|
|
4392
|
+
reporter.allPassed();
|
|
4246
4393
|
return 0;
|
|
4247
4394
|
}
|
|
4248
4395
|
const runChecksCommand = defineCommand({
|
|
@@ -4276,7 +4423,7 @@ const runChecksCommand = defineCommand({
|
|
|
4276
4423
|
const exitCode = runRunChecks(path.resolve(args.dir ?? "."), {
|
|
4277
4424
|
skip: args.skip ? new Set(args.skip.split(",").map((s) => s.trim())) : void 0,
|
|
4278
4425
|
add: args.add ? args.add.split(",").map((s) => s.trim()) : void 0,
|
|
4279
|
-
failFast: args["fail-fast"]
|
|
4426
|
+
failFast: args["fail-fast"] ? true : void 0
|
|
4280
4427
|
});
|
|
4281
4428
|
process.exitCode = exitCode;
|
|
4282
4429
|
}
|
|
@@ -4656,8 +4803,8 @@ const dockerCheckCommand = defineCommand({
|
|
|
4656
4803
|
//#region src/bin.ts
|
|
4657
4804
|
const main = defineCommand({
|
|
4658
4805
|
meta: {
|
|
4659
|
-
name: "
|
|
4660
|
-
version: "0.
|
|
4806
|
+
name: "bst",
|
|
4807
|
+
version: "0.27.1",
|
|
4661
4808
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
4662
4809
|
},
|
|
4663
4810
|
subCommands: {
|
|
@@ -4673,7 +4820,7 @@ const main = defineCommand({
|
|
|
4673
4820
|
"docker:check": dockerCheckCommand
|
|
4674
4821
|
}
|
|
4675
4822
|
});
|
|
4676
|
-
console.log(`@bensandee/tooling v0.
|
|
4823
|
+
console.log(`@bensandee/tooling v0.27.1`);
|
|
4677
4824
|
async function run() {
|
|
4678
4825
|
await runMain(main);
|
|
4679
4826
|
process.exit(process.exitCode ?? 0);
|