@bensandee/tooling 0.25.3 → 0.27.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 +304 -209
- package/dist/index.d.mts +4 -0
- package/package.json +10 -7
- 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";
|
|
@@ -10,7 +11,24 @@ import { z } from "zod";
|
|
|
10
11
|
import { FatalError, TransientError, UnexpectedError } from "@bensandee/common";
|
|
11
12
|
import { isMap, isScalar, isSeq, parse as parse$1, parseDocument, stringify } from "yaml";
|
|
12
13
|
import { execSync } from "node:child_process";
|
|
14
|
+
import picomatch from "picomatch";
|
|
13
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
|
|
14
32
|
//#region src/types.ts
|
|
15
33
|
const LEGACY_TOOLS = [
|
|
16
34
|
"eslint",
|
|
@@ -204,6 +222,8 @@ function computeDefaults(targetDir) {
|
|
|
204
222
|
ci: detectCiPlatform(targetDir),
|
|
205
223
|
setupRenovate: true,
|
|
206
224
|
releaseStrategy: "none",
|
|
225
|
+
publishNpm: false,
|
|
226
|
+
publishDocker: false,
|
|
207
227
|
projectType: isMonorepo ? "default" : detectProjectType(targetDir),
|
|
208
228
|
detectPackageTypes: true
|
|
209
229
|
};
|
|
@@ -256,10 +276,10 @@ function getMonorepoPackages(targetDir) {
|
|
|
256
276
|
//#endregion
|
|
257
277
|
//#region src/prompts/init-prompts.ts
|
|
258
278
|
function isCancelled(value) {
|
|
259
|
-
return
|
|
279
|
+
return clack.isCancel(value);
|
|
260
280
|
}
|
|
261
281
|
async function runInitPrompts(targetDir, saved) {
|
|
262
|
-
|
|
282
|
+
clack.intro("@bensandee/tooling repo:sync");
|
|
263
283
|
const existingPkg = readPackageJson(targetDir);
|
|
264
284
|
const detected = detectProject(targetDir);
|
|
265
285
|
const defaults = computeDefaults(targetDir);
|
|
@@ -276,7 +296,7 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
276
296
|
const projectType = saved?.projectType ?? defaults.projectType;
|
|
277
297
|
const detectPackageTypes = saved?.detectPackageTypes ?? defaults.detectPackageTypes;
|
|
278
298
|
if (detected.legacyConfigs.some((l) => l.tool === "prettier") && isFirstInit) {
|
|
279
|
-
const formatterAnswer = await
|
|
299
|
+
const formatterAnswer = await clack.select({
|
|
280
300
|
message: "Existing Prettier config found. Keep Prettier or migrate to oxfmt?",
|
|
281
301
|
initialValue: "prettier",
|
|
282
302
|
options: [{
|
|
@@ -289,14 +309,14 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
289
309
|
}]
|
|
290
310
|
});
|
|
291
311
|
if (isCancelled(formatterAnswer)) {
|
|
292
|
-
|
|
312
|
+
clack.cancel("Cancelled.");
|
|
293
313
|
process.exit(0);
|
|
294
314
|
}
|
|
295
315
|
formatter = formatterAnswer;
|
|
296
316
|
}
|
|
297
317
|
const detectedCi = detectCiPlatform(targetDir);
|
|
298
318
|
if (isFirstInit && detectedCi === "none") {
|
|
299
|
-
const ciAnswer = await
|
|
319
|
+
const ciAnswer = await clack.select({
|
|
300
320
|
message: "CI workflow",
|
|
301
321
|
initialValue: "forgejo",
|
|
302
322
|
options: [
|
|
@@ -315,14 +335,14 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
315
335
|
]
|
|
316
336
|
});
|
|
317
337
|
if (isCancelled(ciAnswer)) {
|
|
318
|
-
|
|
338
|
+
clack.cancel("Cancelled.");
|
|
319
339
|
process.exit(0);
|
|
320
340
|
}
|
|
321
341
|
ci = ciAnswer;
|
|
322
342
|
}
|
|
323
343
|
const hasExistingRelease = detected.hasReleaseItConfig || detected.hasSimpleReleaseConfig || detected.hasChangesetsConfig;
|
|
324
344
|
if (isFirstInit && !hasExistingRelease) {
|
|
325
|
-
const releaseAnswer = await
|
|
345
|
+
const releaseAnswer = await clack.select({
|
|
326
346
|
message: "Release management",
|
|
327
347
|
initialValue: defaults.releaseStrategy,
|
|
328
348
|
options: [
|
|
@@ -348,12 +368,40 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
348
368
|
]
|
|
349
369
|
});
|
|
350
370
|
if (isCancelled(releaseAnswer)) {
|
|
351
|
-
|
|
371
|
+
clack.cancel("Cancelled.");
|
|
352
372
|
process.exit(0);
|
|
353
373
|
}
|
|
354
374
|
releaseStrategy = releaseAnswer;
|
|
355
375
|
}
|
|
356
|
-
|
|
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!");
|
|
357
405
|
return {
|
|
358
406
|
name,
|
|
359
407
|
isNew: !isExisting,
|
|
@@ -364,6 +412,8 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
364
412
|
ci,
|
|
365
413
|
setupRenovate,
|
|
366
414
|
releaseStrategy,
|
|
415
|
+
publishNpm,
|
|
416
|
+
publishDocker,
|
|
367
417
|
projectType,
|
|
368
418
|
detectPackageTypes,
|
|
369
419
|
targetDir
|
|
@@ -481,51 +531,53 @@ function createDryRunContext(config) {
|
|
|
481
531
|
//#region src/utils/tooling-config.ts
|
|
482
532
|
const CONFIG_FILE = ".tooling.json";
|
|
483
533
|
const DeclarativeHealthCheckSchema = z.object({
|
|
484
|
-
name: z.string(),
|
|
485
|
-
url: z.string(),
|
|
486
|
-
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" })
|
|
487
537
|
});
|
|
488
538
|
const DockerCheckConfigSchema = z.object({
|
|
489
|
-
composeFiles: z.array(z.string()).optional(),
|
|
490
|
-
envFile: z.string().optional(),
|
|
491
|
-
services: z.array(z.string()).optional(),
|
|
492
|
-
healthChecks: z.array(DeclarativeHealthCheckSchema).optional(),
|
|
493
|
-
buildCommand: z.string().optional(),
|
|
494
|
-
buildCwd: z.string().optional(),
|
|
495
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
496
|
-
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" })
|
|
497
547
|
});
|
|
498
548
|
const ToolingConfigSchema = z.strictObject({
|
|
499
|
-
$schema: z.string().optional(),
|
|
500
|
-
structure: z.enum(["single", "monorepo"]).optional(),
|
|
501
|
-
useEslintPlugin: z.boolean().optional(),
|
|
502
|
-
formatter: z.enum(["oxfmt", "prettier"]).optional(),
|
|
503
|
-
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" }),
|
|
504
554
|
ci: z.enum([
|
|
505
555
|
"github",
|
|
506
556
|
"forgejo",
|
|
507
557
|
"none"
|
|
508
|
-
]).optional(),
|
|
509
|
-
setupRenovate: z.boolean().optional(),
|
|
558
|
+
]).optional().meta({ description: "CI platform" }),
|
|
559
|
+
setupRenovate: z.boolean().optional().meta({ description: "Generate Renovate config" }),
|
|
510
560
|
releaseStrategy: z.enum([
|
|
511
561
|
"release-it",
|
|
512
562
|
"simple",
|
|
513
563
|
"changesets",
|
|
514
564
|
"none"
|
|
515
|
-
]).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)" }),
|
|
516
568
|
projectType: z.enum([
|
|
517
569
|
"default",
|
|
518
570
|
"node",
|
|
519
571
|
"react",
|
|
520
572
|
"library"
|
|
521
|
-
]).optional(),
|
|
522
|
-
detectPackageTypes: z.boolean().optional(),
|
|
523
|
-
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" }),
|
|
524
576
|
docker: z.record(z.string(), z.object({
|
|
525
|
-
dockerfile: z.string(),
|
|
526
|
-
context: z.string().default(".")
|
|
527
|
-
})).optional(),
|
|
528
|
-
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" })
|
|
529
581
|
});
|
|
530
582
|
/** Load saved tooling config from the target directory. Returns undefined if missing, throws on invalid. */
|
|
531
583
|
function loadToolingConfig(targetDir) {
|
|
@@ -545,6 +597,8 @@ const OVERRIDE_KEYS = [
|
|
|
545
597
|
"ci",
|
|
546
598
|
"setupRenovate",
|
|
547
599
|
"releaseStrategy",
|
|
600
|
+
"publishNpm",
|
|
601
|
+
"publishDocker",
|
|
548
602
|
"projectType",
|
|
549
603
|
"detectPackageTypes"
|
|
550
604
|
];
|
|
@@ -590,6 +644,8 @@ function mergeWithSavedConfig(detected, saved) {
|
|
|
590
644
|
ci: saved.ci ?? detected.ci,
|
|
591
645
|
setupRenovate: saved.setupRenovate ?? detected.setupRenovate,
|
|
592
646
|
releaseStrategy: saved.releaseStrategy ?? detected.releaseStrategy,
|
|
647
|
+
publishNpm: saved.publishNpm ?? detected.publishNpm,
|
|
648
|
+
publishDocker: saved.publishDocker ?? detected.publishDocker,
|
|
593
649
|
projectType: saved.projectType ?? detected.projectType,
|
|
594
650
|
detectPackageTypes: saved.detectPackageTypes ?? detected.detectPackageTypes
|
|
595
651
|
};
|
|
@@ -772,7 +828,7 @@ jobs:
|
|
|
772
828
|
DOCKER_REGISTRY_NAMESPACE: ${actionsExpr$2("vars.DOCKER_REGISTRY_NAMESPACE")}
|
|
773
829
|
DOCKER_USERNAME: ${actionsExpr$2("secrets.DOCKER_USERNAME")}
|
|
774
830
|
DOCKER_PASSWORD: ${actionsExpr$2("secrets.DOCKER_PASSWORD")}
|
|
775
|
-
run: pnpm exec
|
|
831
|
+
run: pnpm exec bst docker:publish
|
|
776
832
|
`;
|
|
777
833
|
}
|
|
778
834
|
function requiredDeploySteps() {
|
|
@@ -795,7 +851,7 @@ function requiredDeploySteps() {
|
|
|
795
851
|
},
|
|
796
852
|
{
|
|
797
853
|
match: { run: "docker:publish" },
|
|
798
|
-
step: { run: "pnpm exec
|
|
854
|
+
step: { run: "pnpm exec bst docker:publish" }
|
|
799
855
|
}
|
|
800
856
|
];
|
|
801
857
|
}
|
|
@@ -832,7 +888,7 @@ function hasDockerPackages(ctx) {
|
|
|
832
888
|
}
|
|
833
889
|
async function generateDeployCi(ctx) {
|
|
834
890
|
const filePath = "deploy-ci";
|
|
835
|
-
if (!
|
|
891
|
+
if (!ctx.config.publishDocker || ctx.config.ci === "none") return {
|
|
836
892
|
filePath,
|
|
837
893
|
action: "skipped",
|
|
838
894
|
description: "Deploy CI workflow not applicable"
|
|
@@ -851,21 +907,13 @@ async function generateDeployCi(ctx) {
|
|
|
851
907
|
};
|
|
852
908
|
const merged = mergeWorkflowSteps(existing, "deploy", requiredDeploySteps());
|
|
853
909
|
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
854
|
-
if (
|
|
855
|
-
|
|
856
|
-
return {
|
|
857
|
-
filePath: workflowPath,
|
|
858
|
-
action: "updated",
|
|
859
|
-
description: "Added missing steps to deploy workflow"
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
if (await ctx.confirmOverwrite(workflowPath) === "skip") {
|
|
863
|
-
if (merged.changed || withComment !== merged.content) {
|
|
910
|
+
if (!merged.changed) {
|
|
911
|
+
if (withComment !== existing) {
|
|
864
912
|
ctx.write(workflowPath, withComment);
|
|
865
913
|
return {
|
|
866
914
|
filePath: workflowPath,
|
|
867
915
|
action: "updated",
|
|
868
|
-
description: "Added
|
|
916
|
+
description: "Added schema comment to deploy workflow"
|
|
869
917
|
};
|
|
870
918
|
}
|
|
871
919
|
return {
|
|
@@ -874,11 +922,11 @@ async function generateDeployCi(ctx) {
|
|
|
874
922
|
description: "Existing deploy workflow preserved"
|
|
875
923
|
};
|
|
876
924
|
}
|
|
877
|
-
ctx.write(workflowPath,
|
|
925
|
+
ctx.write(workflowPath, withComment);
|
|
878
926
|
return {
|
|
879
927
|
filePath: workflowPath,
|
|
880
928
|
action: "updated",
|
|
881
|
-
description: "
|
|
929
|
+
description: "Added missing steps to deploy workflow"
|
|
882
930
|
};
|
|
883
931
|
}
|
|
884
932
|
return {
|
|
@@ -903,10 +951,10 @@ const STANDARD_SCRIPTS_SINGLE = {
|
|
|
903
951
|
test: "vitest run",
|
|
904
952
|
lint: "oxlint",
|
|
905
953
|
knip: "knip",
|
|
906
|
-
check: "
|
|
907
|
-
"ci:check": "pnpm check",
|
|
908
|
-
"tooling:check": "
|
|
909
|
-
"tooling:sync": "
|
|
954
|
+
check: "bst checks:run",
|
|
955
|
+
"ci:check": "pnpm check --skip 'docker:*'",
|
|
956
|
+
"tooling:check": "bst repo:sync --check",
|
|
957
|
+
"tooling:sync": "bst repo:sync"
|
|
910
958
|
};
|
|
911
959
|
const STANDARD_SCRIPTS_MONOREPO = {
|
|
912
960
|
build: "pnpm -r build",
|
|
@@ -914,10 +962,10 @@ const STANDARD_SCRIPTS_MONOREPO = {
|
|
|
914
962
|
typecheck: "pnpm -r --parallel run typecheck",
|
|
915
963
|
lint: "oxlint",
|
|
916
964
|
knip: "knip",
|
|
917
|
-
check: "
|
|
918
|
-
"ci:check": "pnpm check",
|
|
919
|
-
"tooling:check": "
|
|
920
|
-
"tooling:sync": "
|
|
965
|
+
check: "bst checks:run",
|
|
966
|
+
"ci:check": "pnpm check --skip 'docker:*'",
|
|
967
|
+
"tooling:check": "bst repo:sync --check",
|
|
968
|
+
"tooling:sync": "bst repo:sync"
|
|
921
969
|
};
|
|
922
970
|
/** Scripts that tooling owns — map from script name to keyword that must appear in the value. */
|
|
923
971
|
const MANAGED_SCRIPTS = {
|
|
@@ -982,8 +1030,8 @@ function addReleaseDeps(deps, config) {
|
|
|
982
1030
|
function getAddedDevDepNames(config) {
|
|
983
1031
|
const deps = { ...ROOT_DEV_DEPS };
|
|
984
1032
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
985
|
-
deps["@bensandee/config"] = "0.9.
|
|
986
|
-
deps["@bensandee/tooling"] = "0.
|
|
1033
|
+
deps["@bensandee/config"] = "0.9.1";
|
|
1034
|
+
deps["@bensandee/tooling"] = "0.27.0";
|
|
987
1035
|
if (config.formatter === "oxfmt") deps["oxfmt"] = "0.35.0";
|
|
988
1036
|
if (config.formatter === "prettier") deps["prettier"] = "3.8.1";
|
|
989
1037
|
addReleaseDeps(deps, config);
|
|
@@ -1000,15 +1048,15 @@ async function generatePackageJson(ctx) {
|
|
|
1000
1048
|
format: formatScript
|
|
1001
1049
|
};
|
|
1002
1050
|
if (ctx.config.releaseStrategy === "changesets") allScripts["changeset"] = "changeset";
|
|
1003
|
-
if (ctx.config.releaseStrategy !== "none" && ctx.config.releaseStrategy !== "changesets") allScripts["trigger-release"] = "
|
|
1051
|
+
if (ctx.config.releaseStrategy !== "none" && ctx.config.releaseStrategy !== "changesets") allScripts["trigger-release"] = "bst release:trigger";
|
|
1004
1052
|
if (hasDockerPackages(ctx)) {
|
|
1005
|
-
allScripts["docker:build"] = "
|
|
1006
|
-
allScripts["docker:check"] = "
|
|
1053
|
+
allScripts["docker:build"] = "bst docker:build";
|
|
1054
|
+
allScripts["docker:check"] = "bst docker:check";
|
|
1007
1055
|
}
|
|
1008
1056
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1009
1057
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1010
|
-
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.
|
|
1011
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1058
|
+
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.1";
|
|
1059
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.27.0";
|
|
1012
1060
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.2";
|
|
1013
1061
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
1014
1062
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
@@ -1769,8 +1817,14 @@ const ClaudeSettingsSchema = z.object({
|
|
|
1769
1817
|
});
|
|
1770
1818
|
function parseClaudeSettings(raw) {
|
|
1771
1819
|
try {
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1820
|
+
const json = JSON.parse(raw);
|
|
1821
|
+
const rawResult = z.record(z.string(), z.unknown()).safeParse(json);
|
|
1822
|
+
const settingsResult = ClaudeSettingsSchema.safeParse(json);
|
|
1823
|
+
if (!rawResult.success || !settingsResult.success) return void 0;
|
|
1824
|
+
return {
|
|
1825
|
+
settings: settingsResult.data,
|
|
1826
|
+
rawJson: rawResult.data
|
|
1827
|
+
};
|
|
1774
1828
|
} catch {
|
|
1775
1829
|
return;
|
|
1776
1830
|
}
|
|
@@ -1829,7 +1883,6 @@ function buildSettings(ctx) {
|
|
|
1829
1883
|
"Bash(wc *)",
|
|
1830
1884
|
"Bash(test *)",
|
|
1831
1885
|
"Bash([ *)",
|
|
1832
|
-
"Bash(find *)",
|
|
1833
1886
|
"Bash(grep *)",
|
|
1834
1887
|
"Bash(which *)",
|
|
1835
1888
|
"Bash(node -e *)",
|
|
@@ -1896,7 +1949,7 @@ function buildSettings(ctx) {
|
|
|
1896
1949
|
instructions: [
|
|
1897
1950
|
"Use pnpm, not npm/yarn/npx. Run binaries with `pnpm exec`.",
|
|
1898
1951
|
"No typecasts (as/any). Use zod schemas, type guards, or narrowing instead.",
|
|
1899
|
-
"Fix lint violations instead of suppressing them. Only add disable comments when suppression is genuinely the best option.",
|
|
1952
|
+
"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.",
|
|
1900
1953
|
"Prefer extensionless imports; if an extension is required, use .ts over .js."
|
|
1901
1954
|
],
|
|
1902
1955
|
enabledPlugins,
|
|
@@ -1920,27 +1973,44 @@ function writeOrMergeSettings(ctx, filePath, generated) {
|
|
|
1920
1973
|
action: "skipped",
|
|
1921
1974
|
description: "Could not parse existing settings"
|
|
1922
1975
|
};
|
|
1923
|
-
const
|
|
1924
|
-
const
|
|
1925
|
-
const
|
|
1926
|
-
const
|
|
1927
|
-
|
|
1928
|
-
const
|
|
1929
|
-
|
|
1976
|
+
const { settings, rawJson } = parsed;
|
|
1977
|
+
const missingAllow = generated.permissions.allow.filter((rule) => !settings.permissions.allow.includes(rule));
|
|
1978
|
+
const missingDeny = generated.permissions.deny.filter((rule) => !settings.permissions.deny.includes(rule));
|
|
1979
|
+
const mergedInstructions = [...settings.instructions];
|
|
1980
|
+
let instructionChanges = 0;
|
|
1981
|
+
for (const inst of generated.instructions) {
|
|
1982
|
+
if (mergedInstructions.includes(inst)) continue;
|
|
1983
|
+
const prefixIdx = mergedInstructions.findIndex((e) => inst.startsWith(e) || e.startsWith(inst));
|
|
1984
|
+
if (prefixIdx !== -1) mergedInstructions[prefixIdx] = inst;
|
|
1985
|
+
else mergedInstructions.push(inst);
|
|
1986
|
+
instructionChanges++;
|
|
1987
|
+
}
|
|
1988
|
+
const missingPlugins = Object.entries(generated.enabledPlugins).filter(([key]) => !(key in settings.enabledPlugins));
|
|
1989
|
+
const missingMarketplaces = Object.entries(generated.extraKnownMarketplaces).filter(([key]) => !(key in settings.extraKnownMarketplaces));
|
|
1990
|
+
const changed = missingAllow.length + missingDeny.length + instructionChanges + missingPlugins.length + missingMarketplaces.length;
|
|
1991
|
+
if (changed === 0) return {
|
|
1930
1992
|
filePath,
|
|
1931
1993
|
action: "skipped",
|
|
1932
1994
|
description: "Already has all rules and instructions"
|
|
1933
1995
|
};
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1996
|
+
rawJson["permissions"] = {
|
|
1997
|
+
allow: [...settings.permissions.allow, ...missingAllow],
|
|
1998
|
+
deny: [...settings.permissions.deny, ...missingDeny]
|
|
1999
|
+
};
|
|
2000
|
+
rawJson["instructions"] = mergedInstructions;
|
|
2001
|
+
const updatedPlugins = { ...settings.enabledPlugins };
|
|
2002
|
+
for (const [key, value] of missingPlugins) updatedPlugins[key] = value;
|
|
2003
|
+
const updatedMarketplaces = { ...settings.extraKnownMarketplaces };
|
|
2004
|
+
for (const [key, value] of missingMarketplaces) updatedMarketplaces[key] = value;
|
|
2005
|
+
if (Object.keys(updatedPlugins).length > 0) rawJson["enabledPlugins"] = updatedPlugins;
|
|
2006
|
+
else delete rawJson["enabledPlugins"];
|
|
2007
|
+
if (Object.keys(updatedMarketplaces).length > 0) rawJson["extraKnownMarketplaces"] = updatedMarketplaces;
|
|
2008
|
+
else delete rawJson["extraKnownMarketplaces"];
|
|
2009
|
+
ctx.write(filePath, JSON.stringify(rawJson, null, 2) + "\n");
|
|
1940
2010
|
return {
|
|
1941
2011
|
filePath,
|
|
1942
2012
|
action: "updated",
|
|
1943
|
-
description: `
|
|
2013
|
+
description: `Updated ${String(changed)} rules/instructions`
|
|
1944
2014
|
};
|
|
1945
2015
|
}
|
|
1946
2016
|
ctx.write(filePath, serializeSettings$1(generated));
|
|
@@ -2114,13 +2184,13 @@ permissions:
|
|
|
2114
2184
|
- name: Release
|
|
2115
2185
|
env:
|
|
2116
2186
|
GITHUB_TOKEN: \${{ github.token }}
|
|
2117
|
-
run: pnpm exec
|
|
2187
|
+
run: pnpm exec bst release:simple` : `
|
|
2118
2188
|
- name: Release
|
|
2119
2189
|
env:
|
|
2120
2190
|
FORGEJO_SERVER_URL: \${{ github.server_url }}
|
|
2121
2191
|
FORGEJO_REPOSITORY: \${{ github.repository }}
|
|
2122
2192
|
FORGEJO_TOKEN: \${{ secrets.FORGEJO_TOKEN }}
|
|
2123
|
-
run: pnpm exec
|
|
2193
|
+
run: pnpm exec bst release:simple`;
|
|
2124
2194
|
return `${workflowSchemaComment(ci)}name: Release
|
|
2125
2195
|
on:
|
|
2126
2196
|
workflow_dispatch:
|
|
@@ -2160,7 +2230,7 @@ function changesetsReleaseStep(ci, publishesNpm) {
|
|
|
2160
2230
|
FORGEJO_TOKEN: actionsExpr("secrets.FORGEJO_TOKEN"),
|
|
2161
2231
|
...publishesNpm && { NODE_AUTH_TOKEN: actionsExpr("secrets.NPM_TOKEN") }
|
|
2162
2232
|
},
|
|
2163
|
-
run: "pnpm exec
|
|
2233
|
+
run: "pnpm exec bst release:changesets"
|
|
2164
2234
|
}
|
|
2165
2235
|
};
|
|
2166
2236
|
}
|
|
@@ -2208,13 +2278,13 @@ function requiredReleaseSteps(strategy, nodeVersionYaml, publishesNpm) {
|
|
|
2208
2278
|
case "simple":
|
|
2209
2279
|
steps.push({
|
|
2210
2280
|
match: { run: "release:simple" },
|
|
2211
|
-
step: { run: "pnpm exec
|
|
2281
|
+
step: { run: "pnpm exec bst release:simple" }
|
|
2212
2282
|
});
|
|
2213
2283
|
break;
|
|
2214
2284
|
case "changesets":
|
|
2215
2285
|
steps.push({
|
|
2216
2286
|
match: { run: "changeset" },
|
|
2217
|
-
step: { run: "pnpm exec
|
|
2287
|
+
step: { run: "pnpm exec bst release:changesets" }
|
|
2218
2288
|
});
|
|
2219
2289
|
break;
|
|
2220
2290
|
}
|
|
@@ -2256,7 +2326,7 @@ async function generateReleaseCi(ctx) {
|
|
|
2256
2326
|
action: "skipped",
|
|
2257
2327
|
description: "Release CI workflow not applicable"
|
|
2258
2328
|
};
|
|
2259
|
-
const publishesNpm =
|
|
2329
|
+
const publishesNpm = ctx.config.publishNpm === true;
|
|
2260
2330
|
if (ctx.config.releaseStrategy === "changesets") return generateChangesetsReleaseCi(ctx, publishesNpm);
|
|
2261
2331
|
const isGitHub = ctx.config.ci === "github";
|
|
2262
2332
|
const workflowPath = isGitHub ? ".github/workflows/release.yml" : ".forgejo/workflows/release.yml";
|
|
@@ -2277,21 +2347,13 @@ async function generateReleaseCi(ctx) {
|
|
|
2277
2347
|
};
|
|
2278
2348
|
const merged = mergeWorkflowSteps(existing, "release", requiredReleaseSteps(ctx.config.releaseStrategy, nodeVersionYaml, publishesNpm));
|
|
2279
2349
|
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
2280
|
-
if (
|
|
2281
|
-
|
|
2282
|
-
return {
|
|
2283
|
-
filePath: workflowPath,
|
|
2284
|
-
action: "updated",
|
|
2285
|
-
description: "Added missing steps to release workflow"
|
|
2286
|
-
};
|
|
2287
|
-
}
|
|
2288
|
-
if (await ctx.confirmOverwrite(workflowPath) === "skip") {
|
|
2289
|
-
if (merged.changed || withComment !== merged.content) {
|
|
2350
|
+
if (!merged.changed) {
|
|
2351
|
+
if (withComment !== existing) {
|
|
2290
2352
|
ctx.write(workflowPath, withComment);
|
|
2291
2353
|
return {
|
|
2292
2354
|
filePath: workflowPath,
|
|
2293
2355
|
action: "updated",
|
|
2294
|
-
description: "Added
|
|
2356
|
+
description: "Added schema comment to release workflow"
|
|
2295
2357
|
};
|
|
2296
2358
|
}
|
|
2297
2359
|
return {
|
|
@@ -2300,11 +2362,11 @@ async function generateReleaseCi(ctx) {
|
|
|
2300
2362
|
description: "Existing release workflow preserved"
|
|
2301
2363
|
};
|
|
2302
2364
|
}
|
|
2303
|
-
ctx.write(workflowPath,
|
|
2365
|
+
ctx.write(workflowPath, withComment);
|
|
2304
2366
|
return {
|
|
2305
2367
|
filePath: workflowPath,
|
|
2306
2368
|
action: "updated",
|
|
2307
|
-
description: "
|
|
2369
|
+
description: "Added missing steps to release workflow"
|
|
2308
2370
|
};
|
|
2309
2371
|
}
|
|
2310
2372
|
return {
|
|
@@ -2779,9 +2841,6 @@ function imageRef(namespace, imageName, tag) {
|
|
|
2779
2841
|
function log$1(message) {
|
|
2780
2842
|
console.log(message);
|
|
2781
2843
|
}
|
|
2782
|
-
function debug$1(verbose, message) {
|
|
2783
|
-
if (verbose) console.log(`[debug] ${message}`);
|
|
2784
|
-
}
|
|
2785
2844
|
/** Read the repo name from root package.json. */
|
|
2786
2845
|
function readRepoName(executor, cwd) {
|
|
2787
2846
|
const rootPkgRaw = executor.readFile(path.join(cwd, "package.json"));
|
|
@@ -2791,7 +2850,7 @@ function readRepoName(executor, cwd) {
|
|
|
2791
2850
|
return repoName;
|
|
2792
2851
|
}
|
|
2793
2852
|
/** Build a single docker image from its config. Paths are resolved relative to cwd. */
|
|
2794
|
-
function buildImage(executor, pkg, cwd,
|
|
2853
|
+
function buildImage(executor, pkg, cwd, extraArgs) {
|
|
2795
2854
|
const dockerfilePath = path.resolve(cwd, pkg.docker.dockerfile);
|
|
2796
2855
|
const contextPath = path.resolve(cwd, pkg.docker.context);
|
|
2797
2856
|
const command = [
|
|
@@ -2801,10 +2860,7 @@ function buildImage(executor, pkg, cwd, verbose, extraArgs) {
|
|
|
2801
2860
|
...extraArgs,
|
|
2802
2861
|
contextPath
|
|
2803
2862
|
].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}`);
|
|
2863
|
+
executor.execInherit(command);
|
|
2808
2864
|
}
|
|
2809
2865
|
/**
|
|
2810
2866
|
* Detect packages with docker config in .tooling.json and build each one.
|
|
@@ -2818,7 +2874,7 @@ function runDockerBuild(executor, config) {
|
|
|
2818
2874
|
if (config.packageDir) {
|
|
2819
2875
|
const pkg = readSinglePackageDocker(executor, config.cwd, config.packageDir, repoName);
|
|
2820
2876
|
log$1(`Building image for ${pkg.dir} (${pkg.imageName}:latest)...`);
|
|
2821
|
-
buildImage(executor, pkg, config.cwd, config.
|
|
2877
|
+
buildImage(executor, pkg, config.cwd, config.extraArgs);
|
|
2822
2878
|
log$1(`Built ${pkg.imageName}:latest`);
|
|
2823
2879
|
return { packages: [pkg] };
|
|
2824
2880
|
}
|
|
@@ -2830,7 +2886,7 @@ function runDockerBuild(executor, config) {
|
|
|
2830
2886
|
log$1(`Found ${packages.length} Docker package(s): ${packages.map((p) => p.dir).join(", ")}`);
|
|
2831
2887
|
for (const pkg of packages) {
|
|
2832
2888
|
log$1(`Building image for ${pkg.dir} (${pkg.imageName}:latest)...`);
|
|
2833
|
-
buildImage(executor, pkg, config.cwd, config.
|
|
2889
|
+
buildImage(executor, pkg, config.cwd, config.extraArgs);
|
|
2834
2890
|
}
|
|
2835
2891
|
log$1(`Built ${packages.length} image(s)`);
|
|
2836
2892
|
return { packages };
|
|
@@ -2847,7 +2903,6 @@ function runDockerPublish(executor, config) {
|
|
|
2847
2903
|
const { packages } = runDockerBuild(executor, {
|
|
2848
2904
|
cwd: config.cwd,
|
|
2849
2905
|
packageDir: void 0,
|
|
2850
|
-
verbose: config.verbose,
|
|
2851
2906
|
extraArgs: []
|
|
2852
2907
|
});
|
|
2853
2908
|
if (packages.length === 0) return {
|
|
@@ -3091,15 +3146,19 @@ function contextAsDockerReader(ctx) {
|
|
|
3091
3146
|
}
|
|
3092
3147
|
/** Log what was detected so the user understands generator decisions. */
|
|
3093
3148
|
function logDetectionSummary(ctx) {
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3149
|
+
if (ctx.config.publishDocker) {
|
|
3150
|
+
const dockerPackages = detectDockerPackages(contextAsDockerReader(ctx), ctx.targetDir, ctx.config.name);
|
|
3151
|
+
if (dockerPackages.length > 0) log$2.info(`Docker images: ${dockerPackages.map((pkg) => pkg.imageName).join(", ")}`);
|
|
3152
|
+
}
|
|
3153
|
+
if (ctx.config.publishNpm) {
|
|
3154
|
+
const publishable = getPublishablePackages(ctx.targetDir, ctx.config.structure, ctx.packageJson);
|
|
3155
|
+
if (publishable.length > 0) log$2.info(`npm packages: ${publishable.map((pkg) => pkg.name).join(", ")}`);
|
|
3156
|
+
}
|
|
3098
3157
|
}
|
|
3099
3158
|
async function runInit(config, options = {}) {
|
|
3100
3159
|
const detected = detectProject(config.targetDir);
|
|
3101
3160
|
const { ctx, archivedFiles } = createContext(config, options.confirmOverwrite ?? (async (relativePath) => {
|
|
3102
|
-
const result = await
|
|
3161
|
+
const result = await select({
|
|
3103
3162
|
message: `${relativePath} already exists. What do you want to do?`,
|
|
3104
3163
|
options: [{
|
|
3105
3164
|
value: "overwrite",
|
|
@@ -3109,10 +3168,9 @@ async function runInit(config, options = {}) {
|
|
|
3109
3168
|
label: "Skip"
|
|
3110
3169
|
}]
|
|
3111
3170
|
});
|
|
3112
|
-
if (
|
|
3171
|
+
if (isCancel(result)) return "skip";
|
|
3113
3172
|
return result;
|
|
3114
3173
|
}));
|
|
3115
|
-
if (config.releaseStrategy !== "none" && !ctx.packageJson?.repository) p.log.warn(`package.json is missing a "repository" field — required for release strategy "${config.releaseStrategy}"`);
|
|
3116
3174
|
logDetectionSummary(ctx);
|
|
3117
3175
|
const results = await runGenerators(ctx);
|
|
3118
3176
|
const alreadyArchived = new Set(results.filter((r) => r.action === "archived").map((r) => r.filePath));
|
|
@@ -3123,8 +3181,12 @@ async function runInit(config, options = {}) {
|
|
|
3123
3181
|
});
|
|
3124
3182
|
const created = results.filter((r) => r.action === "created");
|
|
3125
3183
|
const updated = results.filter((r) => r.action === "updated");
|
|
3126
|
-
|
|
3127
|
-
|
|
3184
|
+
const hasChanges = created.length > 0 || updated.length > 0 || archivedFiles.length > 0;
|
|
3185
|
+
const prompt = generateMigratePrompt(results, config, detected);
|
|
3186
|
+
const promptPath = ".tooling-migrate.md";
|
|
3187
|
+
ctx.write(promptPath, prompt);
|
|
3188
|
+
if (!hasChanges && options.noPrompt) {
|
|
3189
|
+
log$2.success("Repository is up to date.");
|
|
3128
3190
|
return results;
|
|
3129
3191
|
}
|
|
3130
3192
|
if (results.some((r) => r.action === "archived" && r.filePath.startsWith(".husky/"))) try {
|
|
@@ -3137,18 +3199,15 @@ async function runInit(config, options = {}) {
|
|
|
3137
3199
|
const summaryLines = [];
|
|
3138
3200
|
if (created.length > 0) summaryLines.push(`Created: ${created.map((r) => r.filePath).join(", ")}`);
|
|
3139
3201
|
if (updated.length > 0) summaryLines.push(`Updated: ${updated.map((r) => r.filePath).join(", ")}`);
|
|
3140
|
-
|
|
3202
|
+
note(summaryLines.join("\n"), "Summary");
|
|
3141
3203
|
if (!options.noPrompt) {
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
ctx.write(promptPath, prompt);
|
|
3145
|
-
p.log.info(`Migration prompt written to ${promptPath}`);
|
|
3146
|
-
p.log.info("In Claude Code, run: \"Execute the steps in .tooling-migrate.md\"");
|
|
3204
|
+
log$2.info(`Migration prompt written to ${promptPath}`);
|
|
3205
|
+
log$2.info("In Claude Code, run: \"Execute the steps in .tooling-migrate.md\"");
|
|
3147
3206
|
}
|
|
3148
3207
|
const bensandeeDeps = getAddedDevDepNames(config).filter((name) => name.startsWith("@bensandee/"));
|
|
3149
3208
|
const hasLockfile = ctx.exists("pnpm-lock.yaml");
|
|
3150
3209
|
if (bensandeeDeps.length > 0 && hasLockfile) {
|
|
3151
|
-
|
|
3210
|
+
log$2.info("Updating @bensandee/* packages...");
|
|
3152
3211
|
try {
|
|
3153
3212
|
execSync(`pnpm update --latest ${bensandeeDeps.join(" ")}`, {
|
|
3154
3213
|
cwd: config.targetDir,
|
|
@@ -3156,10 +3215,17 @@ async function runInit(config, options = {}) {
|
|
|
3156
3215
|
timeout: 6e4
|
|
3157
3216
|
});
|
|
3158
3217
|
} catch (_error) {
|
|
3159
|
-
|
|
3218
|
+
log$2.warn("Could not update @bensandee/* packages — run pnpm install manually");
|
|
3160
3219
|
}
|
|
3161
3220
|
}
|
|
3162
|
-
|
|
3221
|
+
if (hasChanges && ctx.exists("package.json")) try {
|
|
3222
|
+
execSync("pnpm format", {
|
|
3223
|
+
cwd: config.targetDir,
|
|
3224
|
+
stdio: "ignore",
|
|
3225
|
+
timeout: 3e4
|
|
3226
|
+
});
|
|
3227
|
+
} catch (_error) {}
|
|
3228
|
+
note([
|
|
3163
3229
|
"1. Run: pnpm install",
|
|
3164
3230
|
"2. Run: pnpm check",
|
|
3165
3231
|
...options.noPrompt ? [] : ["3. In Claude Code, run: \"Execute the steps in .tooling-migrate.md\""]
|
|
@@ -3241,22 +3307,22 @@ async function runCheck(targetDir) {
|
|
|
3241
3307
|
return true;
|
|
3242
3308
|
});
|
|
3243
3309
|
if (actionable.length === 0) {
|
|
3244
|
-
|
|
3310
|
+
log$2.success("Repository is up to date.");
|
|
3245
3311
|
return 0;
|
|
3246
3312
|
}
|
|
3247
|
-
|
|
3313
|
+
log$2.warn(`${actionable.length} file(s) would be changed by repo:sync`);
|
|
3248
3314
|
for (const r of actionable) {
|
|
3249
|
-
|
|
3315
|
+
log$2.info(` ${r.action}: ${r.filePath} — ${r.description}`);
|
|
3250
3316
|
const newContent = pendingWrites.get(r.filePath);
|
|
3251
3317
|
if (!newContent) continue;
|
|
3252
3318
|
const existingPath = path.join(targetDir, r.filePath);
|
|
3253
3319
|
const existing = existsSync(existingPath) ? readFileSync(existingPath, "utf-8") : void 0;
|
|
3254
3320
|
if (!existing) {
|
|
3255
3321
|
const lineCount = newContent.split("\n").length - 1;
|
|
3256
|
-
|
|
3322
|
+
log$2.info(` + ${lineCount} new lines`);
|
|
3257
3323
|
} else {
|
|
3258
3324
|
const diff = lineDiff(existing, newContent);
|
|
3259
|
-
for (const line of diff)
|
|
3325
|
+
for (const line of diff) log$2.info(` ${line}`);
|
|
3260
3326
|
}
|
|
3261
3327
|
}
|
|
3262
3328
|
return 1;
|
|
@@ -3311,6 +3377,16 @@ function createRealExecutor() {
|
|
|
3311
3377
|
};
|
|
3312
3378
|
}
|
|
3313
3379
|
},
|
|
3380
|
+
execInherit(command, options) {
|
|
3381
|
+
execSync(command, {
|
|
3382
|
+
cwd: options?.cwd,
|
|
3383
|
+
env: options?.env ? {
|
|
3384
|
+
...process.env,
|
|
3385
|
+
...options.env
|
|
3386
|
+
} : void 0,
|
|
3387
|
+
stdio: "inherit"
|
|
3388
|
+
});
|
|
3389
|
+
},
|
|
3314
3390
|
fetch: globalThis.fetch,
|
|
3315
3391
|
listChangesetFiles(cwd) {
|
|
3316
3392
|
const dir = path.join(cwd, ".changeset");
|
|
@@ -3493,7 +3569,7 @@ async function createRelease(executor, conn, tag) {
|
|
|
3493
3569
|
//#region src/release/log.ts
|
|
3494
3570
|
/** Log a debug message when verbose mode is enabled. */
|
|
3495
3571
|
function debug(config, message) {
|
|
3496
|
-
if (config.verbose)
|
|
3572
|
+
if (config.verbose) log$2.info(`[debug] ${message}`);
|
|
3497
3573
|
}
|
|
3498
3574
|
/** Log the result of an exec call when verbose mode is enabled. */
|
|
3499
3575
|
function debugExec(config, label, result) {
|
|
@@ -3501,7 +3577,7 @@ function debugExec(config, label, result) {
|
|
|
3501
3577
|
const lines = [`[debug] ${label} (exit code ${String(result.exitCode)})`];
|
|
3502
3578
|
if (result.stdout.trim()) lines.push(` stdout: ${result.stdout.trim()}`);
|
|
3503
3579
|
if (result.stderr.trim()) lines.push(` stderr: ${result.stderr.trim()}`);
|
|
3504
|
-
|
|
3580
|
+
log$2.info(lines.join("\n"));
|
|
3505
3581
|
}
|
|
3506
3582
|
//#endregion
|
|
3507
3583
|
//#region src/release/version.ts
|
|
@@ -3573,7 +3649,7 @@ function buildPrContent(executor, cwd, packagesBefore) {
|
|
|
3573
3649
|
}
|
|
3574
3650
|
/** Mode 1: version packages and create/update a PR. */
|
|
3575
3651
|
async function runVersionMode(executor, config) {
|
|
3576
|
-
|
|
3652
|
+
log$2.info("Changesets detected — versioning packages");
|
|
3577
3653
|
const packagesBefore = executor.listWorkspacePackages(config.cwd);
|
|
3578
3654
|
debug(config, `Packages before versioning: ${packagesBefore.map((pkg) => `${pkg.name}@${pkg.version}`).join(", ") || "(none)"}`);
|
|
3579
3655
|
const changesetConfigPath = path.join(config.cwd, ".changeset", "config.json");
|
|
@@ -3599,19 +3675,19 @@ async function runVersionMode(executor, config) {
|
|
|
3599
3675
|
const addResult = executor.exec("git add -A", { cwd: config.cwd });
|
|
3600
3676
|
if (addResult.exitCode !== 0) throw new FatalError(`git add failed: ${addResult.stderr || addResult.stdout}`);
|
|
3601
3677
|
const remainingChangesets = executor.listChangesetFiles(config.cwd);
|
|
3602
|
-
if (remainingChangesets.length > 0)
|
|
3678
|
+
if (remainingChangesets.length > 0) log$2.warn(`Changeset files still present after versioning: ${remainingChangesets.join(", ")}`);
|
|
3603
3679
|
debug(config, `Changeset files after versioning: ${remainingChangesets.length > 0 ? remainingChangesets.join(", ") : "(none — all consumed)"}`);
|
|
3604
3680
|
const commitResult = executor.exec("git commit -m \"chore: version packages\"", { cwd: config.cwd });
|
|
3605
3681
|
debugExec(config, "git commit", commitResult);
|
|
3606
3682
|
if (commitResult.exitCode !== 0) {
|
|
3607
|
-
|
|
3683
|
+
log$2.info("Nothing to commit after versioning");
|
|
3608
3684
|
return {
|
|
3609
3685
|
mode: "version",
|
|
3610
3686
|
pr: "none"
|
|
3611
3687
|
};
|
|
3612
3688
|
}
|
|
3613
3689
|
if (config.dryRun) {
|
|
3614
|
-
|
|
3690
|
+
log$2.info("[dry-run] Would push and create/update PR");
|
|
3615
3691
|
return {
|
|
3616
3692
|
mode: "version",
|
|
3617
3693
|
pr: "none"
|
|
@@ -3634,7 +3710,7 @@ async function runVersionMode(executor, config) {
|
|
|
3634
3710
|
base: "main",
|
|
3635
3711
|
body
|
|
3636
3712
|
});
|
|
3637
|
-
|
|
3713
|
+
log$2.info("Created version PR");
|
|
3638
3714
|
return {
|
|
3639
3715
|
mode: "version",
|
|
3640
3716
|
pr: "created"
|
|
@@ -3644,7 +3720,7 @@ async function runVersionMode(executor, config) {
|
|
|
3644
3720
|
title,
|
|
3645
3721
|
body
|
|
3646
3722
|
});
|
|
3647
|
-
|
|
3723
|
+
log$2.info(`Updated version PR #${String(existingPr)}`);
|
|
3648
3724
|
return {
|
|
3649
3725
|
mode: "version",
|
|
3650
3726
|
pr: "updated"
|
|
@@ -3669,7 +3745,7 @@ async function retryAsync(fn) {
|
|
|
3669
3745
|
}
|
|
3670
3746
|
/** Mode 2: publish to npm, push tags, and create Forgejo releases. */
|
|
3671
3747
|
async function runPublishMode(executor, config) {
|
|
3672
|
-
|
|
3748
|
+
log$2.info("No changesets — publishing packages");
|
|
3673
3749
|
const publishResult = executor.exec("pnpm changeset publish", { cwd: config.cwd });
|
|
3674
3750
|
debugExec(config, "pnpm changeset publish", publishResult);
|
|
3675
3751
|
if (publishResult.exitCode !== 0) throw new FatalError(`pnpm changeset publish failed (exit code ${String(publishResult.exitCode)}):\n${publishResult.stderr}`);
|
|
@@ -3684,11 +3760,11 @@ async function runPublishMode(executor, config) {
|
|
|
3684
3760
|
debug(config, `Reconciled tags to push: ${tagsToPush.length > 0 ? tagsToPush.join(", ") : "(none)"}`);
|
|
3685
3761
|
if (config.dryRun) {
|
|
3686
3762
|
if (tagsToPush.length === 0) {
|
|
3687
|
-
|
|
3763
|
+
log$2.info("No packages were published");
|
|
3688
3764
|
return { mode: "none" };
|
|
3689
3765
|
}
|
|
3690
|
-
|
|
3691
|
-
|
|
3766
|
+
log$2.info(`Tags to process: ${tagsToPush.join(", ")}`);
|
|
3767
|
+
log$2.info("[dry-run] Would push tags and create releases");
|
|
3692
3768
|
return {
|
|
3693
3769
|
mode: "publish",
|
|
3694
3770
|
tags: tagsToPush
|
|
@@ -3704,10 +3780,10 @@ async function runPublishMode(executor, config) {
|
|
|
3704
3780
|
for (const tag of remoteExpectedTags) if (!await findRelease(executor, conn, tag)) tagsWithMissingReleases.push(tag);
|
|
3705
3781
|
const allTags = [...tagsToPush, ...tagsWithMissingReleases];
|
|
3706
3782
|
if (allTags.length === 0) {
|
|
3707
|
-
|
|
3783
|
+
log$2.info("No packages were published");
|
|
3708
3784
|
return { mode: "none" };
|
|
3709
3785
|
}
|
|
3710
|
-
|
|
3786
|
+
log$2.info(`Tags to process: ${allTags.join(", ")}`);
|
|
3711
3787
|
const errors = [];
|
|
3712
3788
|
for (const tag of allTags) try {
|
|
3713
3789
|
if (!remoteSet.has(tag)) {
|
|
@@ -3718,7 +3794,7 @@ async function runPublishMode(executor, config) {
|
|
|
3718
3794
|
const pushTagResult = executor.exec(`git push origin refs/tags/${tag}`, { cwd: config.cwd });
|
|
3719
3795
|
if (pushTagResult.exitCode !== 0) throw new FatalError(`Failed to push tag ${tag}: ${pushTagResult.stderr || pushTagResult.stdout}`);
|
|
3720
3796
|
}
|
|
3721
|
-
if (await findRelease(executor, conn, tag))
|
|
3797
|
+
if (await findRelease(executor, conn, tag)) log$2.warn(`Release for ${tag} already exists — skipping`);
|
|
3722
3798
|
else {
|
|
3723
3799
|
await retryAsync(async () => {
|
|
3724
3800
|
try {
|
|
@@ -3728,14 +3804,14 @@ async function runPublishMode(executor, config) {
|
|
|
3728
3804
|
throw error;
|
|
3729
3805
|
}
|
|
3730
3806
|
});
|
|
3731
|
-
|
|
3807
|
+
log$2.info(`Created release for ${tag}`);
|
|
3732
3808
|
}
|
|
3733
3809
|
} catch (error) {
|
|
3734
3810
|
errors.push({
|
|
3735
3811
|
tag,
|
|
3736
3812
|
error
|
|
3737
3813
|
});
|
|
3738
|
-
|
|
3814
|
+
log$2.warn(`Failed to process ${tag}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3739
3815
|
}
|
|
3740
3816
|
if (errors.length > 0) throw new TransientError(`Failed to create releases for: ${errors.map((e) => e.tag).join(", ")}`);
|
|
3741
3817
|
return {
|
|
@@ -3905,12 +3981,12 @@ async function triggerForgejo(conn, ref) {
|
|
|
3905
3981
|
body: JSON.stringify({ ref })
|
|
3906
3982
|
});
|
|
3907
3983
|
if (!res.ok) throw new FatalError(`Failed to trigger Forgejo workflow: ${res.status} ${res.statusText}`);
|
|
3908
|
-
|
|
3984
|
+
log$2.info(`Triggered release workflow on Forgejo (ref: ${ref})`);
|
|
3909
3985
|
}
|
|
3910
3986
|
function triggerGitHub(ref) {
|
|
3911
3987
|
const result = createRealExecutor().exec(`gh workflow run release.yml --ref ${ref}`, { cwd: process.cwd() });
|
|
3912
3988
|
if (result.exitCode !== 0) throw new FatalError(`Failed to trigger GitHub workflow: ${result.stderr || result.stdout || "unknown error"}`);
|
|
3913
|
-
|
|
3989
|
+
log$2.info(`Triggered release workflow on GitHub (ref: ${ref})`);
|
|
3914
3990
|
}
|
|
3915
3991
|
//#endregion
|
|
3916
3992
|
//#region src/commands/forgejo-create-release.ts
|
|
@@ -3930,11 +4006,11 @@ const createForgejoReleaseCommand = defineCommand({
|
|
|
3930
4006
|
const executor = createRealExecutor();
|
|
3931
4007
|
const conn = resolved.conn;
|
|
3932
4008
|
if (await findRelease(executor, conn, args.tag)) {
|
|
3933
|
-
|
|
4009
|
+
log$2.info(`Release for ${args.tag} already exists — skipping`);
|
|
3934
4010
|
return;
|
|
3935
4011
|
}
|
|
3936
4012
|
await createRelease(executor, conn, args.tag);
|
|
3937
|
-
|
|
4013
|
+
log$2.info(`Created Forgejo release for ${args.tag}`);
|
|
3938
4014
|
}
|
|
3939
4015
|
});
|
|
3940
4016
|
//#endregion
|
|
@@ -3961,26 +4037,26 @@ async function mergeForgejo(conn, dryRun) {
|
|
|
3961
4037
|
const prNumber = await findOpenPr(executor, conn, HEAD_BRANCH);
|
|
3962
4038
|
if (prNumber === null) throw new FatalError(`No open PR found for branch ${HEAD_BRANCH}`);
|
|
3963
4039
|
if (dryRun) {
|
|
3964
|
-
|
|
4040
|
+
log$2.info(`[dry-run] Would merge PR #${String(prNumber)} and delete branch ${HEAD_BRANCH}`);
|
|
3965
4041
|
return;
|
|
3966
4042
|
}
|
|
3967
4043
|
await mergePr(executor, conn, prNumber, {
|
|
3968
4044
|
method: "merge",
|
|
3969
4045
|
deleteBranch: true
|
|
3970
4046
|
});
|
|
3971
|
-
|
|
4047
|
+
log$2.info(`Merged PR #${String(prNumber)} and deleted branch ${HEAD_BRANCH}`);
|
|
3972
4048
|
}
|
|
3973
4049
|
function mergeGitHub(dryRun) {
|
|
3974
4050
|
const executor = createRealExecutor();
|
|
3975
4051
|
if (dryRun) {
|
|
3976
4052
|
const prNum = executor.exec(`gh pr view ${HEAD_BRANCH} --json number --jq .number`, { cwd: process.cwd() }).stdout.trim();
|
|
3977
4053
|
if (!prNum) throw new FatalError(`No open PR found for branch ${HEAD_BRANCH}`);
|
|
3978
|
-
|
|
4054
|
+
log$2.info(`[dry-run] Would merge PR #${prNum} and delete branch ${HEAD_BRANCH}`);
|
|
3979
4055
|
return;
|
|
3980
4056
|
}
|
|
3981
4057
|
const result = executor.exec(`gh pr merge ${HEAD_BRANCH} --merge --delete-branch`, { cwd: process.cwd() });
|
|
3982
4058
|
if (result.exitCode !== 0) throw new FatalError(`Failed to merge PR: ${result.stderr || result.stdout || "unknown error"}`);
|
|
3983
|
-
|
|
4059
|
+
log$2.info(`Merged changesets PR and deleted branch ${HEAD_BRANCH}`);
|
|
3984
4060
|
}
|
|
3985
4061
|
//#endregion
|
|
3986
4062
|
//#region src/release/simple.ts
|
|
@@ -4013,7 +4089,7 @@ function readVersion(executor, cwd) {
|
|
|
4013
4089
|
/** Run the full commit-and-tag-version release flow. */
|
|
4014
4090
|
async function runSimpleRelease(executor, config) {
|
|
4015
4091
|
const command = buildCommand(config);
|
|
4016
|
-
|
|
4092
|
+
log$2.info(`Running: ${command}`);
|
|
4017
4093
|
const versionResult = executor.exec(command, { cwd: config.cwd });
|
|
4018
4094
|
debugExec(config, "commit-and-tag-version", versionResult);
|
|
4019
4095
|
if (versionResult.exitCode !== 0) throw new FatalError(`commit-and-tag-version failed (exit code ${String(versionResult.exitCode)}):\n${versionResult.stderr || versionResult.stdout}`);
|
|
@@ -4023,12 +4099,12 @@ async function runSimpleRelease(executor, config) {
|
|
|
4023
4099
|
debugExec(config, "git describe", tagResult);
|
|
4024
4100
|
const tag = tagResult.stdout.trim();
|
|
4025
4101
|
if (!tag) throw new FatalError("Could not determine the new tag from git describe");
|
|
4026
|
-
|
|
4102
|
+
log$2.info(`Version ${version} tagged as ${tag}`);
|
|
4027
4103
|
if (config.dryRun) {
|
|
4028
4104
|
const slidingTags = config.noSlidingTags ? [] : computeSlidingTags(version);
|
|
4029
|
-
|
|
4030
|
-
if (slidingTags.length > 0)
|
|
4031
|
-
if (!config.noRelease && config.platform)
|
|
4105
|
+
log$2.info(`[dry-run] Would push to origin with --follow-tags`);
|
|
4106
|
+
if (slidingTags.length > 0) log$2.info(`[dry-run] Would create sliding tags: ${slidingTags.join(", ")}`);
|
|
4107
|
+
if (!config.noRelease && config.platform) log$2.info(`[dry-run] Would create ${config.platform.type} release for ${tag}`);
|
|
4032
4108
|
return {
|
|
4033
4109
|
version,
|
|
4034
4110
|
tag,
|
|
@@ -4045,7 +4121,7 @@ async function runSimpleRelease(executor, config) {
|
|
|
4045
4121
|
debugExec(config, "git push", pushResult);
|
|
4046
4122
|
if (pushResult.exitCode !== 0) throw new FatalError(`git push failed (exit code ${String(pushResult.exitCode)}):\n${pushResult.stderr || pushResult.stdout}`);
|
|
4047
4123
|
pushed = true;
|
|
4048
|
-
|
|
4124
|
+
log$2.info("Pushed to origin");
|
|
4049
4125
|
}
|
|
4050
4126
|
let slidingTags = [];
|
|
4051
4127
|
if (!config.noSlidingTags && pushed) {
|
|
@@ -4056,8 +4132,8 @@ async function runSimpleRelease(executor, config) {
|
|
|
4056
4132
|
}
|
|
4057
4133
|
const forcePushResult = executor.exec(`git push origin ${slidingTags.join(" ")} --force`, { cwd: config.cwd });
|
|
4058
4134
|
debugExec(config, "force-push sliding tags", forcePushResult);
|
|
4059
|
-
if (forcePushResult.exitCode !== 0)
|
|
4060
|
-
else
|
|
4135
|
+
if (forcePushResult.exitCode !== 0) log$2.warn(`Warning: Failed to push sliding tags: ${forcePushResult.stderr || forcePushResult.stdout}`);
|
|
4136
|
+
else log$2.info(`Created sliding tags: ${slidingTags.join(", ")}`);
|
|
4061
4137
|
}
|
|
4062
4138
|
let releaseCreated = false;
|
|
4063
4139
|
if (!config.noRelease && config.platform) releaseCreated = await createPlatformRelease(executor, config, tag);
|
|
@@ -4077,16 +4153,16 @@ async function createPlatformRelease(executor, config, tag) {
|
|
|
4077
4153
|
return false;
|
|
4078
4154
|
}
|
|
4079
4155
|
await createRelease(executor, config.platform.conn, tag);
|
|
4080
|
-
|
|
4156
|
+
log$2.info(`Created Forgejo release for ${tag}`);
|
|
4081
4157
|
return true;
|
|
4082
4158
|
}
|
|
4083
4159
|
const ghResult = executor.exec(`gh release create ${tag} --generate-notes`, { cwd: config.cwd });
|
|
4084
4160
|
debugExec(config, "gh release create", ghResult);
|
|
4085
4161
|
if (ghResult.exitCode !== 0) {
|
|
4086
|
-
|
|
4162
|
+
log$2.warn(`Warning: Failed to create GitHub release: ${ghResult.stderr || ghResult.stdout}`);
|
|
4087
4163
|
return false;
|
|
4088
4164
|
}
|
|
4089
|
-
|
|
4165
|
+
log$2.info(`Created GitHub release for ${tag}`);
|
|
4090
4166
|
return true;
|
|
4091
4167
|
}
|
|
4092
4168
|
//#endregion
|
|
@@ -4165,14 +4241,20 @@ const CHECKS = [
|
|
|
4165
4241
|
{ name: "typecheck" },
|
|
4166
4242
|
{ name: "lint" },
|
|
4167
4243
|
{ name: "test" },
|
|
4244
|
+
{ name: "docker:build" },
|
|
4245
|
+
{ name: "tooling:check" },
|
|
4246
|
+
{ name: "docker:check" },
|
|
4168
4247
|
{
|
|
4169
4248
|
name: "format",
|
|
4170
4249
|
args: "--check"
|
|
4171
4250
|
},
|
|
4172
|
-
{ name: "knip" }
|
|
4173
|
-
{ name: "tooling:check" },
|
|
4174
|
-
{ name: "docker:check" }
|
|
4251
|
+
{ name: "knip" }
|
|
4175
4252
|
];
|
|
4253
|
+
/** Check if a name matches any skip pattern. Supports glob syntax via picomatch. */
|
|
4254
|
+
function shouldSkip(name, patterns) {
|
|
4255
|
+
if (patterns.size === 0) return false;
|
|
4256
|
+
return picomatch.isMatch(name, [...patterns]);
|
|
4257
|
+
}
|
|
4176
4258
|
function defaultGetScripts(targetDir) {
|
|
4177
4259
|
try {
|
|
4178
4260
|
const pkg = parsePackageJson(readFileSync(path.join(targetDir, "package.json"), "utf-8"));
|
|
@@ -4193,7 +4275,30 @@ function defaultExecCommand(cmd, cwd) {
|
|
|
4193
4275
|
return 1;
|
|
4194
4276
|
}
|
|
4195
4277
|
}
|
|
4196
|
-
const
|
|
4278
|
+
const rawLog = (msg) => console.log(msg);
|
|
4279
|
+
const ciReporter = {
|
|
4280
|
+
groupStart: (name) => rawLog(`::group::${name}`),
|
|
4281
|
+
groupEnd: () => rawLog("::endgroup::"),
|
|
4282
|
+
passed: (name, elapsedS) => rawLog(`✓ ${name} (${elapsedS}s)`),
|
|
4283
|
+
failed: (name, elapsedS) => {
|
|
4284
|
+
rawLog(`✗ ${name} (${elapsedS}s)`);
|
|
4285
|
+
rawLog(`::error::${name} failed`);
|
|
4286
|
+
},
|
|
4287
|
+
undefinedCheck: (name) => rawLog(`::error::${name} not defined in package.json`),
|
|
4288
|
+
skippedNotDefined: (names) => rawLog(`Skipped (not defined): ${names.join(", ")}`),
|
|
4289
|
+
allPassed: () => rawLog("✓ All checks passed"),
|
|
4290
|
+
anyFailed: (names) => rawLog(`::error::Failed checks: ${names.join(", ")}`)
|
|
4291
|
+
};
|
|
4292
|
+
const localReporter = {
|
|
4293
|
+
groupStart: (_name) => {},
|
|
4294
|
+
groupEnd: () => {},
|
|
4295
|
+
passed: (name) => log$2.success(name),
|
|
4296
|
+
failed: (name) => log$2.error(`${name} failed`),
|
|
4297
|
+
undefinedCheck: (name) => log$2.error(`${name} not defined in package.json`),
|
|
4298
|
+
skippedNotDefined: (names) => log$2.info(`Skipped (not defined): ${names.join(", ")}`),
|
|
4299
|
+
allPassed: () => log$2.success("All checks passed"),
|
|
4300
|
+
anyFailed: (names) => log$2.error(`Failed checks: ${names.join(", ")}`)
|
|
4301
|
+
};
|
|
4197
4302
|
function runRunChecks(targetDir, options = {}) {
|
|
4198
4303
|
const exec = options.execCommand ?? defaultExecCommand;
|
|
4199
4304
|
const getScripts = options.getScripts ?? defaultGetScripts;
|
|
@@ -4201,38 +4306,40 @@ function runRunChecks(targetDir, options = {}) {
|
|
|
4201
4306
|
const add = options.add ?? [];
|
|
4202
4307
|
const isCI = Boolean(process.env["CI"]);
|
|
4203
4308
|
const failFast = options.failFast ?? !isCI;
|
|
4309
|
+
const reporter = isCI ? ciReporter : localReporter;
|
|
4204
4310
|
const definedScripts = getScripts(targetDir);
|
|
4205
4311
|
const addedNames = new Set(add);
|
|
4206
4312
|
const allChecks = [...CHECKS, ...add.map((name) => ({ name }))];
|
|
4207
4313
|
const failures = [];
|
|
4208
4314
|
const notDefined = [];
|
|
4209
4315
|
for (const check of allChecks) {
|
|
4210
|
-
if (
|
|
4316
|
+
if (shouldSkip(check.name, skip)) continue;
|
|
4211
4317
|
if (!definedScripts.has(check.name)) {
|
|
4212
4318
|
if (addedNames.has(check.name)) {
|
|
4213
|
-
|
|
4319
|
+
reporter.undefinedCheck(check.name);
|
|
4214
4320
|
failures.push(check.name);
|
|
4215
4321
|
} else notDefined.push(check.name);
|
|
4216
4322
|
continue;
|
|
4217
4323
|
}
|
|
4218
4324
|
const cmd = check.args ? `pnpm run ${check.name} ${check.args}` : `pnpm run ${check.name}`;
|
|
4219
|
-
|
|
4325
|
+
reporter.groupStart(check.name);
|
|
4326
|
+
const start = Date.now();
|
|
4220
4327
|
const exitCode = exec(cmd, targetDir);
|
|
4221
|
-
|
|
4222
|
-
|
|
4328
|
+
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
4329
|
+
reporter.groupEnd();
|
|
4330
|
+
if (exitCode === 0) reporter.passed(check.name, elapsed);
|
|
4223
4331
|
else {
|
|
4224
|
-
|
|
4225
|
-
p.log.error(`${check.name} failed`);
|
|
4332
|
+
reporter.failed(check.name, elapsed);
|
|
4226
4333
|
failures.push(check.name);
|
|
4227
4334
|
if (failFast) return 1;
|
|
4228
4335
|
}
|
|
4229
4336
|
}
|
|
4230
|
-
if (notDefined.length > 0)
|
|
4337
|
+
if (notDefined.length > 0) reporter.skippedNotDefined(notDefined);
|
|
4231
4338
|
if (failures.length > 0) {
|
|
4232
|
-
|
|
4339
|
+
reporter.anyFailed(failures);
|
|
4233
4340
|
return 1;
|
|
4234
4341
|
}
|
|
4235
|
-
|
|
4342
|
+
reporter.allPassed();
|
|
4236
4343
|
return 0;
|
|
4237
4344
|
}
|
|
4238
4345
|
const runChecksCommand = defineCommand({
|
|
@@ -4266,7 +4373,7 @@ const runChecksCommand = defineCommand({
|
|
|
4266
4373
|
const exitCode = runRunChecks(path.resolve(args.dir ?? "."), {
|
|
4267
4374
|
skip: args.skip ? new Set(args.skip.split(",").map((s) => s.trim())) : void 0,
|
|
4268
4375
|
add: args.add ? args.add.split(",").map((s) => s.trim()) : void 0,
|
|
4269
|
-
failFast: args["fail-fast"]
|
|
4376
|
+
failFast: args["fail-fast"] ? true : void 0
|
|
4270
4377
|
});
|
|
4271
4378
|
process.exitCode = exitCode;
|
|
4272
4379
|
}
|
|
@@ -4283,16 +4390,10 @@ const publishDockerCommand = defineCommand({
|
|
|
4283
4390
|
name: "docker:publish",
|
|
4284
4391
|
description: "Build, tag, and push Docker images for packages with an image:build script"
|
|
4285
4392
|
},
|
|
4286
|
-
args: {
|
|
4287
|
-
"
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
},
|
|
4291
|
-
verbose: {
|
|
4292
|
-
type: "boolean",
|
|
4293
|
-
description: "Enable detailed debug logging"
|
|
4294
|
-
}
|
|
4295
|
-
},
|
|
4393
|
+
args: { "dry-run": {
|
|
4394
|
+
type: "boolean",
|
|
4395
|
+
description: "Build and tag images but skip login, push, and logout"
|
|
4396
|
+
} },
|
|
4296
4397
|
async run({ args }) {
|
|
4297
4398
|
const config = {
|
|
4298
4399
|
cwd: process.cwd(),
|
|
@@ -4300,8 +4401,7 @@ const publishDockerCommand = defineCommand({
|
|
|
4300
4401
|
registryNamespace: requireEnv("DOCKER_REGISTRY_NAMESPACE"),
|
|
4301
4402
|
username: requireEnv("DOCKER_USERNAME"),
|
|
4302
4403
|
password: requireEnv("DOCKER_PASSWORD"),
|
|
4303
|
-
dryRun: args["dry-run"] === true
|
|
4304
|
-
verbose: args.verbose === true
|
|
4404
|
+
dryRun: args["dry-run"] === true
|
|
4305
4405
|
};
|
|
4306
4406
|
runDockerPublish(createRealExecutor(), config);
|
|
4307
4407
|
}
|
|
@@ -4334,10 +4434,6 @@ const dockerBuildCommand = defineCommand({
|
|
|
4334
4434
|
type: "string",
|
|
4335
4435
|
description: "Build a single package by directory path (e.g. packages/server). Useful as an image:build script."
|
|
4336
4436
|
},
|
|
4337
|
-
verbose: {
|
|
4338
|
-
type: "boolean",
|
|
4339
|
-
description: "Enable detailed debug logging"
|
|
4340
|
-
},
|
|
4341
4437
|
_: {
|
|
4342
4438
|
type: "positional",
|
|
4343
4439
|
required: false,
|
|
@@ -4358,7 +4454,6 @@ const dockerBuildCommand = defineCommand({
|
|
|
4358
4454
|
runDockerBuild(executor, {
|
|
4359
4455
|
cwd,
|
|
4360
4456
|
packageDir,
|
|
4361
|
-
verbose: args.verbose === true,
|
|
4362
4457
|
extraArgs: extraArgs.filter((a) => a.length > 0)
|
|
4363
4458
|
});
|
|
4364
4459
|
}
|
|
@@ -4658,8 +4753,8 @@ const dockerCheckCommand = defineCommand({
|
|
|
4658
4753
|
//#region src/bin.ts
|
|
4659
4754
|
const main = defineCommand({
|
|
4660
4755
|
meta: {
|
|
4661
|
-
name: "
|
|
4662
|
-
version: "0.
|
|
4756
|
+
name: "bst",
|
|
4757
|
+
version: "0.27.0",
|
|
4663
4758
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
4664
4759
|
},
|
|
4665
4760
|
subCommands: {
|
|
@@ -4675,7 +4770,7 @@ const main = defineCommand({
|
|
|
4675
4770
|
"docker:check": dockerCheckCommand
|
|
4676
4771
|
}
|
|
4677
4772
|
});
|
|
4678
|
-
console.log(`@bensandee/tooling v0.
|
|
4773
|
+
console.log(`@bensandee/tooling v0.27.0`);
|
|
4679
4774
|
async function run() {
|
|
4680
4775
|
await runMain(main);
|
|
4681
4776
|
process.exit(process.exitCode ?? 0);
|