@bensandee/tooling 0.32.0 → 0.34.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 +56 -17
- package/dist/bin.mjs +276 -198
- package/dist/{check-DMDdHanG.mjs → check-Ceom_OgJ.mjs} +61 -11
- package/dist/docker-check/index.d.mts +2 -0
- package/dist/docker-check/index.mjs +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { l as createRealExecutor$1, t as runDockerCheck, u as isExecSyncError } from "./check-
|
|
2
|
+
import { d as debug, f as debugExec, h as note, l as createRealExecutor$1, m as log, p as isEnvVerbose, t as runDockerCheck, u as isExecSyncError } from "./check-Ceom_OgJ.mjs";
|
|
3
3
|
import { defineCommand, runMain } from "citty";
|
|
4
|
-
import * as
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
5
|
import { isCancel, select } from "@clack/prompts";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -13,22 +13,6 @@ import { FatalError, TransientError, UnexpectedError } from "@bensandee/common";
|
|
|
13
13
|
import { isMap, isScalar, isSeq, parse as parse$1, parseDocument, stringify } from "yaml";
|
|
14
14
|
import picomatch from "picomatch";
|
|
15
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
|
|
32
16
|
//#region src/types.ts
|
|
33
17
|
const LEGACY_TOOLS = [
|
|
34
18
|
"eslint",
|
|
@@ -312,7 +296,7 @@ function getMonorepoPackages(targetDir) {
|
|
|
312
296
|
//#endregion
|
|
313
297
|
//#region src/prompts/init-prompts.ts
|
|
314
298
|
function isCancelled(value) {
|
|
315
|
-
return
|
|
299
|
+
return p.isCancel(value);
|
|
316
300
|
}
|
|
317
301
|
function detectProjectInfo(targetDir) {
|
|
318
302
|
const existingPkg = readPackageJson(targetDir);
|
|
@@ -323,7 +307,7 @@ function detectProjectInfo(targetDir) {
|
|
|
323
307
|
};
|
|
324
308
|
}
|
|
325
309
|
async function runInitPrompts(targetDir, saved) {
|
|
326
|
-
|
|
310
|
+
p.intro("@bensandee/tooling repo:sync");
|
|
327
311
|
const { detected, defaults, name } = detectProjectInfo(targetDir);
|
|
328
312
|
const isFirstInit = !saved;
|
|
329
313
|
const structure = saved?.structure ?? defaults.structure;
|
|
@@ -336,7 +320,7 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
336
320
|
const projectType = saved?.projectType ?? defaults.projectType;
|
|
337
321
|
const detectPackageTypes = saved?.detectPackageTypes ?? defaults.detectPackageTypes;
|
|
338
322
|
if (detected.legacyConfigs.some((l) => l.tool === "prettier") && isFirstInit) {
|
|
339
|
-
const formatterAnswer = await
|
|
323
|
+
const formatterAnswer = await p.select({
|
|
340
324
|
message: "Existing Prettier config found. Keep Prettier or migrate to oxfmt?",
|
|
341
325
|
initialValue: "prettier",
|
|
342
326
|
options: [{
|
|
@@ -349,14 +333,14 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
349
333
|
}]
|
|
350
334
|
});
|
|
351
335
|
if (isCancelled(formatterAnswer)) {
|
|
352
|
-
|
|
336
|
+
p.cancel("Cancelled.");
|
|
353
337
|
process.exit(0);
|
|
354
338
|
}
|
|
355
339
|
formatter = formatterAnswer;
|
|
356
340
|
}
|
|
357
341
|
const detectedCi = detectCiPlatform(targetDir);
|
|
358
342
|
if (isFirstInit && detectedCi === "none") {
|
|
359
|
-
const ciAnswer = await
|
|
343
|
+
const ciAnswer = await p.select({
|
|
360
344
|
message: "CI workflow",
|
|
361
345
|
initialValue: "forgejo",
|
|
362
346
|
options: [
|
|
@@ -375,14 +359,14 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
375
359
|
]
|
|
376
360
|
});
|
|
377
361
|
if (isCancelled(ciAnswer)) {
|
|
378
|
-
|
|
362
|
+
p.cancel("Cancelled.");
|
|
379
363
|
process.exit(0);
|
|
380
364
|
}
|
|
381
365
|
ci = ciAnswer;
|
|
382
366
|
}
|
|
383
367
|
const hasExistingRelease = detected.hasReleaseItConfig || detected.hasSimpleReleaseConfig || detected.hasChangesetsConfig;
|
|
384
368
|
if (isFirstInit && !hasExistingRelease) {
|
|
385
|
-
const releaseAnswer = await
|
|
369
|
+
const releaseAnswer = await p.select({
|
|
386
370
|
message: "Release management",
|
|
387
371
|
initialValue: defaults.releaseStrategy,
|
|
388
372
|
options: [
|
|
@@ -408,7 +392,7 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
408
392
|
]
|
|
409
393
|
});
|
|
410
394
|
if (isCancelled(releaseAnswer)) {
|
|
411
|
-
|
|
395
|
+
p.cancel("Cancelled.");
|
|
412
396
|
process.exit(0);
|
|
413
397
|
}
|
|
414
398
|
releaseStrategy = releaseAnswer;
|
|
@@ -416,12 +400,12 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
416
400
|
let publishNpm = saved?.publishNpm ?? false;
|
|
417
401
|
if (isFirstInit && releaseStrategy !== "none") {
|
|
418
402
|
if (getPublishablePackages(targetDir, structure).length > 0) {
|
|
419
|
-
const answer = await
|
|
403
|
+
const answer = await p.confirm({
|
|
420
404
|
message: "Publish packages to npm?",
|
|
421
405
|
initialValue: false
|
|
422
406
|
});
|
|
423
407
|
if (isCancelled(answer)) {
|
|
424
|
-
|
|
408
|
+
p.cancel("Cancelled.");
|
|
425
409
|
process.exit(0);
|
|
426
410
|
}
|
|
427
411
|
publishNpm = answer;
|
|
@@ -430,18 +414,18 @@ async function runInitPrompts(targetDir, saved) {
|
|
|
430
414
|
let publishDocker = saved?.publishDocker ?? false;
|
|
431
415
|
if (isFirstInit) {
|
|
432
416
|
if (existsSync(path.join(targetDir, "Dockerfile")) || existsSync(path.join(targetDir, "docker/Dockerfile"))) {
|
|
433
|
-
const answer = await
|
|
417
|
+
const answer = await p.confirm({
|
|
434
418
|
message: "Publish Docker images to a registry?",
|
|
435
419
|
initialValue: false
|
|
436
420
|
});
|
|
437
421
|
if (isCancelled(answer)) {
|
|
438
|
-
|
|
422
|
+
p.cancel("Cancelled.");
|
|
439
423
|
process.exit(0);
|
|
440
424
|
}
|
|
441
425
|
publishDocker = answer;
|
|
442
426
|
}
|
|
443
427
|
}
|
|
444
|
-
|
|
428
|
+
p.outro("Configuration complete!");
|
|
445
429
|
return {
|
|
446
430
|
name,
|
|
447
431
|
structure,
|
|
@@ -859,9 +843,6 @@ function generateTags(version) {
|
|
|
859
843
|
function imageRef(namespace, imageName, tag) {
|
|
860
844
|
return `${namespace}/${imageName}:${tag}`;
|
|
861
845
|
}
|
|
862
|
-
function log$1(message) {
|
|
863
|
-
console.log(message);
|
|
864
|
-
}
|
|
865
846
|
/** Read the repo name from root package.json. */
|
|
866
847
|
function readRepoName(executor, cwd) {
|
|
867
848
|
const rootPkgRaw = executor.readFile(path.join(cwd, "package.json"));
|
|
@@ -894,22 +875,24 @@ function runDockerBuild(executor, config) {
|
|
|
894
875
|
const repoName = readRepoName(executor, config.cwd);
|
|
895
876
|
if (config.packageDir) {
|
|
896
877
|
const pkg = readSinglePackageDocker(executor, config.cwd, config.packageDir, repoName);
|
|
897
|
-
log
|
|
878
|
+
log.info(`Building image for ${pkg.dir} (${pkg.imageName}:latest)...`);
|
|
879
|
+
debug(config, `Dockerfile: ${pkg.docker.dockerfile}, context: ${pkg.docker.context}`);
|
|
898
880
|
buildImage(executor, pkg, config.cwd, config.extraArgs);
|
|
899
|
-
log
|
|
881
|
+
log.info(`Built ${pkg.imageName}:latest`);
|
|
900
882
|
return { packages: [pkg] };
|
|
901
883
|
}
|
|
902
884
|
const packages = detectDockerPackages(executor, config.cwd, repoName);
|
|
903
885
|
if (packages.length === 0) {
|
|
904
|
-
log
|
|
886
|
+
log.info("No packages with docker config found");
|
|
905
887
|
return { packages: [] };
|
|
906
888
|
}
|
|
907
|
-
log
|
|
889
|
+
log.info(`Found ${String(packages.length)} Docker package(s): ${packages.map((p) => p.dir).join(", ")}`);
|
|
908
890
|
for (const pkg of packages) {
|
|
909
|
-
log
|
|
891
|
+
log.info(`Building image for ${pkg.dir} (${pkg.imageName}:latest)...`);
|
|
892
|
+
debug(config, `Dockerfile: ${pkg.docker.dockerfile}, context: ${pkg.docker.context}`);
|
|
910
893
|
buildImage(executor, pkg, config.cwd, config.extraArgs);
|
|
911
894
|
}
|
|
912
|
-
log
|
|
895
|
+
log.info(`Built ${String(packages.length)} image(s)`);
|
|
913
896
|
return { packages };
|
|
914
897
|
}
|
|
915
898
|
/**
|
|
@@ -924,7 +907,8 @@ function runDockerPublish(executor, config) {
|
|
|
924
907
|
const { packages } = runDockerBuild(executor, {
|
|
925
908
|
cwd: config.cwd,
|
|
926
909
|
packageDir: void 0,
|
|
927
|
-
extraArgs: []
|
|
910
|
+
extraArgs: [],
|
|
911
|
+
verbose: config.verbose
|
|
928
912
|
});
|
|
929
913
|
if (packages.length === 0) return {
|
|
930
914
|
packages: [],
|
|
@@ -932,35 +916,38 @@ function runDockerPublish(executor, config) {
|
|
|
932
916
|
};
|
|
933
917
|
for (const pkg of packages) if (!pkg.version) throw new FatalError(`Package ${pkg.dir} has docker config but no version in package.json`);
|
|
934
918
|
if (!config.dryRun) {
|
|
935
|
-
log
|
|
919
|
+
log.info(`Logging in to ${config.registryHost}...`);
|
|
936
920
|
const loginResult = executor.exec(`echo "${config.password}" | docker login ${config.registryHost} -u ${config.username} --password-stdin`);
|
|
921
|
+
debugExec(config, "docker login", loginResult);
|
|
937
922
|
if (loginResult.exitCode !== 0) throw new FatalError(`Docker login failed: ${loginResult.stderr}`);
|
|
938
|
-
} else log
|
|
923
|
+
} else log.info("[dry-run] Skipping docker login");
|
|
939
924
|
const allTags = [];
|
|
940
925
|
try {
|
|
941
926
|
for (const pkg of packages) {
|
|
942
927
|
const tags = generateTags(pkg.version ?? "");
|
|
943
|
-
log
|
|
928
|
+
log.info(`${pkg.dir} v${pkg.version} → tags: ${tags.join(", ")}`);
|
|
944
929
|
for (const tag of tags) {
|
|
945
930
|
const ref = imageRef(config.registryNamespace, pkg.imageName, tag);
|
|
946
931
|
allTags.push(ref);
|
|
947
|
-
log
|
|
932
|
+
log.info(`Tagging ${pkg.imageName} → ${ref}`);
|
|
948
933
|
const tagResult = executor.exec(`docker tag ${pkg.imageName} ${ref}`);
|
|
934
|
+
debugExec(config, `docker tag ${pkg.imageName} ${ref}`, tagResult);
|
|
949
935
|
if (tagResult.exitCode !== 0) throw new FatalError(`docker tag failed: ${tagResult.stderr}`);
|
|
950
936
|
if (!config.dryRun) {
|
|
951
|
-
log
|
|
937
|
+
log.info(`Pushing ${ref}...`);
|
|
952
938
|
const pushResult = executor.exec(`docker push ${ref}`);
|
|
939
|
+
debugExec(config, `docker push ${ref}`, pushResult);
|
|
953
940
|
if (pushResult.exitCode !== 0) throw new FatalError(`docker push failed: ${pushResult.stderr}`);
|
|
954
|
-
} else log
|
|
941
|
+
} else log.info(`[dry-run] Skipping push for ${ref}`);
|
|
955
942
|
}
|
|
956
943
|
}
|
|
957
944
|
} finally {
|
|
958
945
|
if (!config.dryRun) {
|
|
959
|
-
log
|
|
946
|
+
log.info(`Logging out from ${config.registryHost}...`);
|
|
960
947
|
executor.exec(`docker logout ${config.registryHost}`);
|
|
961
948
|
}
|
|
962
949
|
}
|
|
963
|
-
log
|
|
950
|
+
log.info(`Published ${String(allTags.length)} image tag(s)`);
|
|
964
951
|
return {
|
|
965
952
|
packages,
|
|
966
953
|
tags: allTags
|
|
@@ -1590,7 +1577,7 @@ function getAddedDevDepNames(config) {
|
|
|
1590
1577
|
const deps = { ...ROOT_DEV_DEPS };
|
|
1591
1578
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
1592
1579
|
deps["@bensandee/config"] = "0.9.1";
|
|
1593
|
-
deps["@bensandee/tooling"] = "0.
|
|
1580
|
+
deps["@bensandee/tooling"] = "0.34.0";
|
|
1594
1581
|
if (config.formatter === "oxfmt") deps["oxfmt"] = {
|
|
1595
1582
|
"@changesets/cli": "2.30.0",
|
|
1596
1583
|
"@release-it/bumper": "7.0.5",
|
|
@@ -1645,7 +1632,7 @@ async function generatePackageJson(ctx) {
|
|
|
1645
1632
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1646
1633
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1647
1634
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.1";
|
|
1648
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1635
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.34.0";
|
|
1649
1636
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.2";
|
|
1650
1637
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
|
|
1651
1638
|
"@changesets/cli": "2.30.0",
|
|
@@ -2804,6 +2791,41 @@ function releaseItSteps(ci, nodeVersionYaml, publishesNpm) {
|
|
|
2804
2791
|
}
|
|
2805
2792
|
}];
|
|
2806
2793
|
}
|
|
2794
|
+
/** Build the workflow_dispatch trigger with optional inputs for the simple strategy. */
|
|
2795
|
+
function simpleWorkflowDispatchTrigger() {
|
|
2796
|
+
return { workflow_dispatch: { inputs: {
|
|
2797
|
+
bump: {
|
|
2798
|
+
description: "Version bump type (default: conventional-commits auto-detect)",
|
|
2799
|
+
required: false,
|
|
2800
|
+
type: "choice",
|
|
2801
|
+
default: "auto",
|
|
2802
|
+
options: [
|
|
2803
|
+
"auto",
|
|
2804
|
+
"major",
|
|
2805
|
+
"minor",
|
|
2806
|
+
"patch",
|
|
2807
|
+
"first-release"
|
|
2808
|
+
]
|
|
2809
|
+
},
|
|
2810
|
+
prerelease: {
|
|
2811
|
+
description: "Create a prerelease with the given tag (e.g., beta, alpha)",
|
|
2812
|
+
required: false,
|
|
2813
|
+
type: "string"
|
|
2814
|
+
}
|
|
2815
|
+
} } };
|
|
2816
|
+
}
|
|
2817
|
+
/** Build the release:simple run command with conditional flags from workflow inputs. */
|
|
2818
|
+
function simpleReleaseCommand() {
|
|
2819
|
+
return [
|
|
2820
|
+
"FLAGS=",
|
|
2821
|
+
`case "${actionsExpr("inputs.bump")}" in`,
|
|
2822
|
+
" major|minor|patch) FLAGS=\"$FLAGS --release-as " + actionsExpr("inputs.bump") + "\" ;;",
|
|
2823
|
+
" first-release) FLAGS=\"$FLAGS --first-release\" ;;",
|
|
2824
|
+
"esac",
|
|
2825
|
+
`if [ -n "${actionsExpr("inputs.prerelease")}" ]; then FLAGS="$FLAGS --prerelease ${actionsExpr("inputs.prerelease")}"; fi`,
|
|
2826
|
+
"pnpm exec bst release:simple $FLAGS"
|
|
2827
|
+
].join("\n");
|
|
2828
|
+
}
|
|
2807
2829
|
function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker) {
|
|
2808
2830
|
const releaseStep = {
|
|
2809
2831
|
match: { run: "release:simple" },
|
|
@@ -2814,7 +2836,7 @@ function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker) {
|
|
|
2814
2836
|
FORGEJO_REPOSITORY: actionsExpr("github.repository"),
|
|
2815
2837
|
RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN")
|
|
2816
2838
|
},
|
|
2817
|
-
run:
|
|
2839
|
+
run: simpleReleaseCommand()
|
|
2818
2840
|
}
|
|
2819
2841
|
};
|
|
2820
2842
|
const dockerStep = {
|
|
@@ -2929,10 +2951,11 @@ async function generateReleaseCi(ctx) {
|
|
|
2929
2951
|
action: "skipped",
|
|
2930
2952
|
description: "Release CI workflow not applicable"
|
|
2931
2953
|
};
|
|
2954
|
+
const on = ctx.config.releaseStrategy === "simple" ? simpleWorkflowDispatchTrigger() : { workflow_dispatch: null };
|
|
2932
2955
|
const content = buildWorkflowYaml({
|
|
2933
2956
|
ci: ctx.config.ci,
|
|
2934
2957
|
name: "Release",
|
|
2935
|
-
on
|
|
2958
|
+
on,
|
|
2936
2959
|
...isGitHub && { permissions: { contents: "write" } },
|
|
2937
2960
|
jobName: "release",
|
|
2938
2961
|
steps
|
|
@@ -3285,7 +3308,7 @@ function generateMigratePrompt(results, config, detected) {
|
|
|
3285
3308
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3286
3309
|
sections.push("# Migration Prompt");
|
|
3287
3310
|
sections.push("");
|
|
3288
|
-
sections.push(`_Generated by \`@bensandee/tooling@0.
|
|
3311
|
+
sections.push(`_Generated by \`@bensandee/tooling@0.34.0 repo:sync\` on ${timestamp}_`);
|
|
3289
3312
|
sections.push("");
|
|
3290
3313
|
sections.push("The following prompt was generated by `@bensandee/tooling repo:sync`. Paste it into Claude Code or another AI assistant to finish migrating this repository.");
|
|
3291
3314
|
sections.push("");
|
|
@@ -3469,11 +3492,11 @@ function contextAsDockerReader(ctx) {
|
|
|
3469
3492
|
function logDetectionSummary(ctx) {
|
|
3470
3493
|
if (ctx.config.publishDocker) {
|
|
3471
3494
|
const dockerPackages = detectDockerPackages(contextAsDockerReader(ctx), ctx.targetDir, ctx.config.name);
|
|
3472
|
-
if (dockerPackages.length > 0) log
|
|
3495
|
+
if (dockerPackages.length > 0) log.info(`Docker images: ${dockerPackages.map((pkg) => pkg.imageName).join(", ")}`);
|
|
3473
3496
|
}
|
|
3474
3497
|
if (ctx.config.publishNpm) {
|
|
3475
3498
|
const publishable = getPublishablePackages(ctx.targetDir, ctx.config.structure, ctx.packageJson);
|
|
3476
|
-
if (publishable.length > 0) log
|
|
3499
|
+
if (publishable.length > 0) log.info(`npm packages: ${publishable.map((pkg) => pkg.name).join(", ")}`);
|
|
3477
3500
|
}
|
|
3478
3501
|
}
|
|
3479
3502
|
async function runInit(config, options = {}) {
|
|
@@ -3507,7 +3530,7 @@ async function runInit(config, options = {}) {
|
|
|
3507
3530
|
const promptPath = ".tooling-migrate.md";
|
|
3508
3531
|
ctx.write(promptPath, prompt);
|
|
3509
3532
|
if (!hasChanges && options.noPrompt) {
|
|
3510
|
-
log
|
|
3533
|
+
log.success("Repository is up to date.");
|
|
3511
3534
|
return results;
|
|
3512
3535
|
}
|
|
3513
3536
|
if (results.some((r) => r.action === "archived" && r.filePath.startsWith(".husky/"))) try {
|
|
@@ -3522,13 +3545,13 @@ async function runInit(config, options = {}) {
|
|
|
3522
3545
|
if (updated.length > 0) summaryLines.push(`Updated: ${updated.map((r) => r.filePath).join(", ")}`);
|
|
3523
3546
|
note(summaryLines.join("\n"), "Summary");
|
|
3524
3547
|
if (!options.noPrompt) {
|
|
3525
|
-
log
|
|
3526
|
-
log
|
|
3548
|
+
log.info(`Migration prompt written to ${promptPath}`);
|
|
3549
|
+
log.info("In Claude Code, run: \"Execute the steps in .tooling-migrate.md\"");
|
|
3527
3550
|
}
|
|
3528
3551
|
const bensandeeDeps = getAddedDevDepNames(config).filter((name) => name.startsWith("@bensandee/"));
|
|
3529
3552
|
const hasLockfile = ctx.exists("pnpm-lock.yaml");
|
|
3530
3553
|
if (bensandeeDeps.length > 0 && hasLockfile) {
|
|
3531
|
-
log
|
|
3554
|
+
log.info("Updating @bensandee/* packages...");
|
|
3532
3555
|
try {
|
|
3533
3556
|
execSync(`pnpm update --latest ${bensandeeDeps.join(" ")}`, {
|
|
3534
3557
|
cwd: config.targetDir,
|
|
@@ -3536,7 +3559,7 @@ async function runInit(config, options = {}) {
|
|
|
3536
3559
|
timeout: 6e4
|
|
3537
3560
|
});
|
|
3538
3561
|
} catch (_error) {
|
|
3539
|
-
log
|
|
3562
|
+
log.warn("Could not update @bensandee/* packages — run pnpm install manually");
|
|
3540
3563
|
}
|
|
3541
3564
|
}
|
|
3542
3565
|
if (hasChanges && ctx.exists("package.json")) try {
|
|
@@ -3628,22 +3651,22 @@ async function runCheck(targetDir) {
|
|
|
3628
3651
|
return true;
|
|
3629
3652
|
});
|
|
3630
3653
|
if (actionable.length === 0) {
|
|
3631
|
-
log
|
|
3654
|
+
log.success("Repository is up to date.");
|
|
3632
3655
|
return 0;
|
|
3633
3656
|
}
|
|
3634
|
-
log
|
|
3657
|
+
log.warn(`${actionable.length} file(s) would be changed by repo:sync`);
|
|
3635
3658
|
for (const r of actionable) {
|
|
3636
|
-
log
|
|
3659
|
+
log.info(` ${r.action}: ${r.filePath} — ${r.description}`);
|
|
3637
3660
|
const newContent = pendingWrites.get(r.filePath);
|
|
3638
3661
|
if (!newContent) continue;
|
|
3639
3662
|
const existingPath = path.join(targetDir, r.filePath);
|
|
3640
3663
|
const existing = existsSync(existingPath) ? readFileSync(existingPath, "utf-8") : void 0;
|
|
3641
3664
|
if (!existing) {
|
|
3642
3665
|
const lineCount = newContent.split("\n").length - 1;
|
|
3643
|
-
log
|
|
3666
|
+
log.info(` + ${lineCount} new lines`);
|
|
3644
3667
|
} else {
|
|
3645
3668
|
const diff = lineDiff(existing, newContent);
|
|
3646
|
-
for (const line of diff) log
|
|
3669
|
+
for (const line of diff) log.info(` ${line}`);
|
|
3647
3670
|
}
|
|
3648
3671
|
}
|
|
3649
3672
|
return 1;
|
|
@@ -3791,6 +3814,17 @@ function reconcileTags(expectedTags, remoteTags, stdoutTags) {
|
|
|
3791
3814
|
}
|
|
3792
3815
|
//#endregion
|
|
3793
3816
|
//#region src/release/forgejo.ts
|
|
3817
|
+
const RETRY_ATTEMPTS = 3;
|
|
3818
|
+
const RETRY_BASE_DELAY_MS = 1e3;
|
|
3819
|
+
/** Safely read response body text for inclusion in error messages. */
|
|
3820
|
+
async function responseBodyText(res) {
|
|
3821
|
+
try {
|
|
3822
|
+
const text = await res.text();
|
|
3823
|
+
return text.length > 500 ? text.slice(0, 500) + "…" : text;
|
|
3824
|
+
} catch {
|
|
3825
|
+
return "(could not read response body)";
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3794
3828
|
const PullRequestSchema = z.array(z.object({
|
|
3795
3829
|
number: z.number(),
|
|
3796
3830
|
head: z.object({ ref: z.string() })
|
|
@@ -3804,7 +3838,10 @@ const PullRequestSchema = z.array(z.object({
|
|
|
3804
3838
|
async function findOpenPr(executor, conn, head) {
|
|
3805
3839
|
const url = `${conn.serverUrl}/api/v1/repos/${conn.repository}/pulls?state=open`;
|
|
3806
3840
|
const res = await executor.fetch(url, { headers: { Authorization: `token ${conn.token}` } });
|
|
3807
|
-
if (!res.ok)
|
|
3841
|
+
if (!res.ok) {
|
|
3842
|
+
const body = await responseBodyText(res);
|
|
3843
|
+
throw new TransientError(`Failed to list PRs: ${res.status} ${res.statusText}\n${body}`);
|
|
3844
|
+
}
|
|
3808
3845
|
const parsed = PullRequestSchema.safeParse(await res.json());
|
|
3809
3846
|
if (!parsed.success) throw new UnexpectedError(`Unexpected PR list response: ${parsed.error.message}`);
|
|
3810
3847
|
return parsed.data.find((pr) => pr.head.ref === head)?.number ?? null;
|
|
@@ -3826,7 +3863,10 @@ async function createPr(executor, conn, options) {
|
|
|
3826
3863
|
},
|
|
3827
3864
|
body: JSON.stringify(payload)
|
|
3828
3865
|
});
|
|
3829
|
-
if (!res.ok)
|
|
3866
|
+
if (!res.ok) {
|
|
3867
|
+
const body = await responseBodyText(res);
|
|
3868
|
+
throw new TransientError(`Failed to create PR: ${res.status} ${res.statusText}\n${body}`);
|
|
3869
|
+
}
|
|
3830
3870
|
}
|
|
3831
3871
|
/** Update an existing pull request's title and body. */
|
|
3832
3872
|
async function updatePr(executor, conn, prNumber, options) {
|
|
@@ -3842,7 +3882,10 @@ async function updatePr(executor, conn, prNumber, options) {
|
|
|
3842
3882
|
body: options.body
|
|
3843
3883
|
})
|
|
3844
3884
|
});
|
|
3845
|
-
if (!res.ok)
|
|
3885
|
+
if (!res.ok) {
|
|
3886
|
+
const body = await responseBodyText(res);
|
|
3887
|
+
throw new TransientError(`Failed to update PR #${String(prNumber)}: ${res.status} ${res.statusText}\n${body}`);
|
|
3888
|
+
}
|
|
3846
3889
|
}
|
|
3847
3890
|
/** Merge a pull request by number. */
|
|
3848
3891
|
async function mergePr(executor, conn, prNumber, options) {
|
|
@@ -3858,7 +3901,10 @@ async function mergePr(executor, conn, prNumber, options) {
|
|
|
3858
3901
|
delete_branch_after_merge: options?.deleteBranch ?? true
|
|
3859
3902
|
})
|
|
3860
3903
|
});
|
|
3861
|
-
if (!res.ok)
|
|
3904
|
+
if (!res.ok) {
|
|
3905
|
+
const body = await responseBodyText(res);
|
|
3906
|
+
throw new TransientError(`Failed to merge PR #${String(prNumber)}: ${res.status} ${res.statusText}\n${body}`);
|
|
3907
|
+
}
|
|
3862
3908
|
}
|
|
3863
3909
|
/** Check whether a Forgejo release already exists for a given tag. */
|
|
3864
3910
|
async function findRelease(executor, conn, tag) {
|
|
@@ -3867,7 +3913,8 @@ async function findRelease(executor, conn, tag) {
|
|
|
3867
3913
|
const res = await executor.fetch(url, { headers: { Authorization: `token ${conn.token}` } });
|
|
3868
3914
|
if (res.status === 200) return true;
|
|
3869
3915
|
if (res.status === 404) return false;
|
|
3870
|
-
|
|
3916
|
+
const body = await responseBodyText(res);
|
|
3917
|
+
throw new TransientError(`Failed to check release for ${tag}: ${res.status} ${res.statusText}\n${body}`);
|
|
3871
3918
|
}
|
|
3872
3919
|
/** Create a Forgejo release for a given tag. */
|
|
3873
3920
|
async function createRelease(executor, conn, tag) {
|
|
@@ -3884,21 +3931,35 @@ async function createRelease(executor, conn, tag) {
|
|
|
3884
3931
|
body: `Published ${tag}`
|
|
3885
3932
|
})
|
|
3886
3933
|
});
|
|
3887
|
-
if (!res.ok)
|
|
3934
|
+
if (!res.ok) {
|
|
3935
|
+
const body = await responseBodyText(res);
|
|
3936
|
+
throw new TransientError(`Failed to create release for ${tag}: ${res.status} ${res.statusText}\n${body}`);
|
|
3937
|
+
}
|
|
3888
3938
|
}
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3939
|
+
/**
|
|
3940
|
+
* Ensure a Forgejo release exists for a tag, creating it if necessary.
|
|
3941
|
+
*
|
|
3942
|
+
* Handles two edge cases:
|
|
3943
|
+
* - The release already exists before we try (skips creation)
|
|
3944
|
+
* - Forgejo auto-creates a release when a tag is pushed, causing a 500 race
|
|
3945
|
+
* condition (detected by re-checking after failure)
|
|
3946
|
+
*
|
|
3947
|
+
* Retries on transient errors with exponential backoff.
|
|
3948
|
+
* Returns "created" | "exists" | "race" indicating what happened.
|
|
3949
|
+
*/
|
|
3950
|
+
async function ensureRelease(executor, conn, tag) {
|
|
3951
|
+
if (await findRelease(executor, conn, tag)) return "exists";
|
|
3952
|
+
for (let attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) try {
|
|
3953
|
+
await createRelease(executor, conn, tag);
|
|
3954
|
+
return "created";
|
|
3955
|
+
} catch (error) {
|
|
3956
|
+
if (await findRelease(executor, conn, tag)) return "race";
|
|
3957
|
+
if (attempt >= RETRY_ATTEMPTS) throw error;
|
|
3958
|
+
log.warn(`Release creation attempt ${String(attempt)}/${String(RETRY_ATTEMPTS)} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3959
|
+
const delay = RETRY_BASE_DELAY_MS * 2 ** (attempt - 1);
|
|
3960
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
3961
|
+
}
|
|
3962
|
+
throw new TransientError(`Failed to create release for ${tag} after ${String(RETRY_ATTEMPTS)} attempts`);
|
|
3902
3963
|
}
|
|
3903
3964
|
//#endregion
|
|
3904
3965
|
//#region src/release/version.ts
|
|
@@ -3970,7 +4031,7 @@ function buildPrContent(executor, cwd, packagesBefore) {
|
|
|
3970
4031
|
}
|
|
3971
4032
|
/** Mode 1: version packages and create/update a PR. */
|
|
3972
4033
|
async function runVersionMode(executor, config) {
|
|
3973
|
-
log
|
|
4034
|
+
log.info("Changesets detected — versioning packages");
|
|
3974
4035
|
const packagesBefore = executor.listWorkspacePackages(config.cwd);
|
|
3975
4036
|
debug(config, `Packages before versioning: ${packagesBefore.map((pkg) => `${pkg.name}@${pkg.version}`).join(", ") || "(none)"}`);
|
|
3976
4037
|
const changesetConfigPath = path.join(config.cwd, ".changeset", "config.json");
|
|
@@ -3996,19 +4057,19 @@ async function runVersionMode(executor, config) {
|
|
|
3996
4057
|
const addResult = executor.exec("git add -A", { cwd: config.cwd });
|
|
3997
4058
|
if (addResult.exitCode !== 0) throw new FatalError(`git add failed: ${addResult.stderr || addResult.stdout}`);
|
|
3998
4059
|
const remainingChangesets = executor.listChangesetFiles(config.cwd);
|
|
3999
|
-
if (remainingChangesets.length > 0) log
|
|
4060
|
+
if (remainingChangesets.length > 0) log.warn(`Changeset files still present after versioning: ${remainingChangesets.join(", ")}`);
|
|
4000
4061
|
debug(config, `Changeset files after versioning: ${remainingChangesets.length > 0 ? remainingChangesets.join(", ") : "(none — all consumed)"}`);
|
|
4001
4062
|
const commitResult = executor.exec("git commit -m \"chore: version packages\"", { cwd: config.cwd });
|
|
4002
4063
|
debugExec(config, "git commit", commitResult);
|
|
4003
4064
|
if (commitResult.exitCode !== 0) {
|
|
4004
|
-
log
|
|
4065
|
+
log.info("Nothing to commit after versioning");
|
|
4005
4066
|
return {
|
|
4006
4067
|
mode: "version",
|
|
4007
4068
|
pr: "none"
|
|
4008
4069
|
};
|
|
4009
4070
|
}
|
|
4010
4071
|
if (config.dryRun) {
|
|
4011
|
-
log
|
|
4072
|
+
log.info("[dry-run] Would push and create/update PR");
|
|
4012
4073
|
return {
|
|
4013
4074
|
mode: "version",
|
|
4014
4075
|
pr: "none"
|
|
@@ -4031,7 +4092,7 @@ async function runVersionMode(executor, config) {
|
|
|
4031
4092
|
base: "main",
|
|
4032
4093
|
body
|
|
4033
4094
|
});
|
|
4034
|
-
log
|
|
4095
|
+
log.info("Created version PR");
|
|
4035
4096
|
return {
|
|
4036
4097
|
mode: "version",
|
|
4037
4098
|
pr: "created"
|
|
@@ -4041,7 +4102,7 @@ async function runVersionMode(executor, config) {
|
|
|
4041
4102
|
title,
|
|
4042
4103
|
body
|
|
4043
4104
|
});
|
|
4044
|
-
log
|
|
4105
|
+
log.info(`Updated version PR #${String(existingPr)}`);
|
|
4045
4106
|
return {
|
|
4046
4107
|
mode: "version",
|
|
4047
4108
|
pr: "updated"
|
|
@@ -4049,24 +4110,9 @@ async function runVersionMode(executor, config) {
|
|
|
4049
4110
|
}
|
|
4050
4111
|
//#endregion
|
|
4051
4112
|
//#region src/release/publish.ts
|
|
4052
|
-
const RETRY_ATTEMPTS = 3;
|
|
4053
|
-
const RETRY_BASE_DELAY_MS = 1e3;
|
|
4054
|
-
async function retryAsync(fn) {
|
|
4055
|
-
let lastError;
|
|
4056
|
-
for (let attempt = 0; attempt <= RETRY_ATTEMPTS; attempt++) try {
|
|
4057
|
-
return await fn();
|
|
4058
|
-
} catch (error) {
|
|
4059
|
-
lastError = error;
|
|
4060
|
-
if (attempt < RETRY_ATTEMPTS) {
|
|
4061
|
-
const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
|
|
4062
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
4063
|
-
}
|
|
4064
|
-
}
|
|
4065
|
-
throw lastError;
|
|
4066
|
-
}
|
|
4067
4113
|
/** Mode 2: publish to npm, push tags, and create Forgejo releases. */
|
|
4068
4114
|
async function runPublishMode(executor, config) {
|
|
4069
|
-
log
|
|
4115
|
+
log.info("No changesets — publishing packages");
|
|
4070
4116
|
const publishResult = executor.exec("pnpm changeset publish", { cwd: config.cwd });
|
|
4071
4117
|
debugExec(config, "pnpm changeset publish", publishResult);
|
|
4072
4118
|
if (publishResult.exitCode !== 0) throw new FatalError(`pnpm changeset publish failed (exit code ${String(publishResult.exitCode)}):\n${publishResult.stderr}`);
|
|
@@ -4081,11 +4127,11 @@ async function runPublishMode(executor, config) {
|
|
|
4081
4127
|
debug(config, `Reconciled tags to push: ${tagsToPush.length > 0 ? tagsToPush.join(", ") : "(none)"}`);
|
|
4082
4128
|
if (config.dryRun) {
|
|
4083
4129
|
if (tagsToPush.length === 0) {
|
|
4084
|
-
log
|
|
4130
|
+
log.info("No packages were published");
|
|
4085
4131
|
return { mode: "none" };
|
|
4086
4132
|
}
|
|
4087
|
-
log
|
|
4088
|
-
log
|
|
4133
|
+
log.info(`Tags to process: ${tagsToPush.join(", ")}`);
|
|
4134
|
+
log.info("[dry-run] Would push tags and create releases");
|
|
4089
4135
|
return {
|
|
4090
4136
|
mode: "publish",
|
|
4091
4137
|
tags: tagsToPush
|
|
@@ -4101,10 +4147,10 @@ async function runPublishMode(executor, config) {
|
|
|
4101
4147
|
for (const tag of remoteExpectedTags) if (!await findRelease(executor, conn, tag)) tagsWithMissingReleases.push(tag);
|
|
4102
4148
|
const allTags = [...tagsToPush, ...tagsWithMissingReleases];
|
|
4103
4149
|
if (allTags.length === 0) {
|
|
4104
|
-
log
|
|
4150
|
+
log.info("No packages were published");
|
|
4105
4151
|
return { mode: "none" };
|
|
4106
4152
|
}
|
|
4107
|
-
log
|
|
4153
|
+
log.info(`Tags to process: ${allTags.join(", ")}`);
|
|
4108
4154
|
const errors = [];
|
|
4109
4155
|
for (const tag of allTags) try {
|
|
4110
4156
|
if (!remoteSet.has(tag)) {
|
|
@@ -4115,24 +4161,14 @@ async function runPublishMode(executor, config) {
|
|
|
4115
4161
|
const pushTagResult = executor.exec(`git push origin refs/tags/${tag}`, { cwd: config.cwd });
|
|
4116
4162
|
if (pushTagResult.exitCode !== 0) throw new FatalError(`Failed to push tag ${tag}: ${pushTagResult.stderr || pushTagResult.stdout}`);
|
|
4117
4163
|
}
|
|
4118
|
-
if (await
|
|
4119
|
-
else {
|
|
4120
|
-
await retryAsync(async () => {
|
|
4121
|
-
try {
|
|
4122
|
-
await createRelease(executor, conn, tag);
|
|
4123
|
-
} catch (error) {
|
|
4124
|
-
if (await findRelease(executor, conn, tag)) return;
|
|
4125
|
-
throw error;
|
|
4126
|
-
}
|
|
4127
|
-
});
|
|
4128
|
-
log$2.info(`Created release for ${tag}`);
|
|
4129
|
-
}
|
|
4164
|
+
if (await ensureRelease(executor, conn, tag) === "exists") log.warn(`Release for ${tag} already exists — skipping`);
|
|
4165
|
+
else log.info(`Created release for ${tag}`);
|
|
4130
4166
|
} catch (error) {
|
|
4131
4167
|
errors.push({
|
|
4132
4168
|
tag,
|
|
4133
4169
|
error
|
|
4134
4170
|
});
|
|
4135
|
-
log
|
|
4171
|
+
log.warn(`Failed to process ${tag}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4136
4172
|
}
|
|
4137
4173
|
if (errors.length > 0) throw new TransientError(`Failed to create releases for: ${errors.map((e) => e.tag).join(", ")}`);
|
|
4138
4174
|
return {
|
|
@@ -4226,6 +4262,14 @@ function configureGitAuth(executor, conn, cwd) {
|
|
|
4226
4262
|
const authUrl = `https://x-access-token:${conn.token}@${host}/${conn.repository}`;
|
|
4227
4263
|
executor.exec(`git remote set-url origin ${authUrl}`, { cwd });
|
|
4228
4264
|
}
|
|
4265
|
+
/** Configure git user.name and user.email for CI bot commits. */
|
|
4266
|
+
function configureGitIdentity(executor, platform, cwd) {
|
|
4267
|
+
const isGitHub = platform === "github";
|
|
4268
|
+
const name = isGitHub ? "github-actions[bot]" : "forgejo-actions[bot]";
|
|
4269
|
+
const email = isGitHub ? "github-actions[bot]@users.noreply.github.com" : "forgejo-actions[bot]@noreply.localhost";
|
|
4270
|
+
executor.exec(`git config user.name "${name}"`, { cwd });
|
|
4271
|
+
executor.exec(`git config user.email "${email}"`, { cwd });
|
|
4272
|
+
}
|
|
4229
4273
|
//#endregion
|
|
4230
4274
|
//#region src/commands/release-changesets.ts
|
|
4231
4275
|
const releaseForgejoCommand = defineCommand({
|
|
@@ -4240,13 +4284,13 @@ const releaseForgejoCommand = defineCommand({
|
|
|
4240
4284
|
},
|
|
4241
4285
|
verbose: {
|
|
4242
4286
|
type: "boolean",
|
|
4243
|
-
description: "Enable detailed debug logging (also enabled by
|
|
4287
|
+
description: "Enable detailed debug logging (also enabled by TOOLING_DEBUG env var)"
|
|
4244
4288
|
}
|
|
4245
4289
|
},
|
|
4246
4290
|
async run({ args }) {
|
|
4247
4291
|
if ((await runRelease(buildReleaseConfig({
|
|
4248
4292
|
dryRun: args["dry-run"] === true,
|
|
4249
|
-
verbose: args.verbose === true ||
|
|
4293
|
+
verbose: args.verbose === true || isEnvVerbose()
|
|
4250
4294
|
}), createRealExecutor())).mode === "none") process.exitCode = 0;
|
|
4251
4295
|
}
|
|
4252
4296
|
});
|
|
@@ -4274,8 +4318,7 @@ async function runRelease(config, executor) {
|
|
|
4274
4318
|
debug(config, `Skipping release on non-main branch: ${branch}`);
|
|
4275
4319
|
return { mode: "none" };
|
|
4276
4320
|
}
|
|
4277
|
-
executor
|
|
4278
|
-
executor.exec("git config user.email \"forgejo-actions[bot]@noreply.localhost\"", { cwd: config.cwd });
|
|
4321
|
+
configureGitIdentity(executor, "forgejo", config.cwd);
|
|
4279
4322
|
configureGitAuth(executor, config, config.cwd);
|
|
4280
4323
|
const changesetFiles = executor.listChangesetFiles(config.cwd);
|
|
4281
4324
|
debug(config, `Changeset files found: ${changesetFiles.length > 0 ? changesetFiles.join(", ") : "(none)"}`);
|
|
@@ -4293,35 +4336,55 @@ const releaseTriggerCommand = defineCommand({
|
|
|
4293
4336
|
name: "release:trigger",
|
|
4294
4337
|
description: "Trigger the release CI workflow"
|
|
4295
4338
|
},
|
|
4296
|
-
args: {
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4339
|
+
args: {
|
|
4340
|
+
ref: {
|
|
4341
|
+
type: "string",
|
|
4342
|
+
description: "Git ref to trigger on (default: main)",
|
|
4343
|
+
required: false
|
|
4344
|
+
},
|
|
4345
|
+
bump: {
|
|
4346
|
+
type: "string",
|
|
4347
|
+
description: "Version bump type: auto, major, minor, patch, or first-release",
|
|
4348
|
+
required: false
|
|
4349
|
+
},
|
|
4350
|
+
prerelease: {
|
|
4351
|
+
type: "string",
|
|
4352
|
+
description: "Create a prerelease with the given tag (e.g., beta, alpha)",
|
|
4353
|
+
required: false
|
|
4354
|
+
}
|
|
4355
|
+
},
|
|
4301
4356
|
async run({ args }) {
|
|
4302
4357
|
const ref = args.ref ?? "main";
|
|
4358
|
+
const inputs = {};
|
|
4359
|
+
if (args.bump) inputs["bump"] = args.bump;
|
|
4360
|
+
if (args.prerelease) inputs["prerelease"] = args.prerelease;
|
|
4303
4361
|
const resolved = resolveConnection(process.cwd());
|
|
4304
|
-
if (resolved.type === "forgejo") await triggerForgejo(resolved.conn, ref);
|
|
4305
|
-
else triggerGitHub(ref);
|
|
4362
|
+
if (resolved.type === "forgejo") await triggerForgejo(resolved.conn, ref, inputs);
|
|
4363
|
+
else triggerGitHub(ref, inputs);
|
|
4306
4364
|
}
|
|
4307
4365
|
});
|
|
4308
|
-
async function triggerForgejo(conn, ref) {
|
|
4366
|
+
async function triggerForgejo(conn, ref, inputs) {
|
|
4309
4367
|
const url = `${conn.serverUrl}/api/v1/repos/${conn.repository}/actions/workflows/release.yml/dispatches`;
|
|
4368
|
+
const body = { ref };
|
|
4369
|
+
if (Object.keys(inputs).length > 0) body["inputs"] = inputs;
|
|
4310
4370
|
const res = await fetch(url, {
|
|
4311
4371
|
method: "POST",
|
|
4312
4372
|
headers: {
|
|
4313
4373
|
Authorization: `token ${conn.token}`,
|
|
4314
4374
|
"Content-Type": "application/json"
|
|
4315
4375
|
},
|
|
4316
|
-
body: JSON.stringify(
|
|
4376
|
+
body: JSON.stringify(body)
|
|
4317
4377
|
});
|
|
4318
4378
|
if (!res.ok) throw new FatalError(`Failed to trigger Forgejo workflow: ${res.status} ${res.statusText}`);
|
|
4319
|
-
log
|
|
4379
|
+
log.info(`Triggered release workflow on Forgejo (ref: ${ref})`);
|
|
4320
4380
|
}
|
|
4321
|
-
function triggerGitHub(ref) {
|
|
4322
|
-
const
|
|
4381
|
+
function triggerGitHub(ref, inputs) {
|
|
4382
|
+
const executor = createRealExecutor();
|
|
4383
|
+
const inputFlags = Object.entries(inputs).map(([k, v]) => `-f ${k}=${v}`).join(" ");
|
|
4384
|
+
const cmd = `gh workflow run release.yml --ref ${ref}${inputFlags ? ` ${inputFlags}` : ""}`;
|
|
4385
|
+
const result = executor.exec(cmd, { cwd: process.cwd() });
|
|
4323
4386
|
if (result.exitCode !== 0) throw new FatalError(`Failed to trigger GitHub workflow: ${result.stderr || result.stdout || "unknown error"}`);
|
|
4324
|
-
log
|
|
4387
|
+
log.info(`Triggered release workflow on GitHub (ref: ${ref})`);
|
|
4325
4388
|
}
|
|
4326
4389
|
//#endregion
|
|
4327
4390
|
//#region src/commands/forgejo-create-release.ts
|
|
@@ -4341,11 +4404,11 @@ const createForgejoReleaseCommand = defineCommand({
|
|
|
4341
4404
|
const executor = createRealExecutor();
|
|
4342
4405
|
const conn = resolved.conn;
|
|
4343
4406
|
if (await findRelease(executor, conn, args.tag)) {
|
|
4344
|
-
log
|
|
4407
|
+
log.info(`Release for ${args.tag} already exists — skipping`);
|
|
4345
4408
|
return;
|
|
4346
4409
|
}
|
|
4347
4410
|
await createRelease(executor, conn, args.tag);
|
|
4348
|
-
log
|
|
4411
|
+
log.info(`Created Forgejo release for ${args.tag}`);
|
|
4349
4412
|
}
|
|
4350
4413
|
});
|
|
4351
4414
|
//#endregion
|
|
@@ -4372,26 +4435,26 @@ async function mergeForgejo(conn, dryRun) {
|
|
|
4372
4435
|
const prNumber = await findOpenPr(executor, conn, HEAD_BRANCH);
|
|
4373
4436
|
if (prNumber === null) throw new FatalError(`No open PR found for branch ${HEAD_BRANCH}`);
|
|
4374
4437
|
if (dryRun) {
|
|
4375
|
-
log
|
|
4438
|
+
log.info(`[dry-run] Would merge PR #${String(prNumber)} and delete branch ${HEAD_BRANCH}`);
|
|
4376
4439
|
return;
|
|
4377
4440
|
}
|
|
4378
4441
|
await mergePr(executor, conn, prNumber, {
|
|
4379
4442
|
method: "merge",
|
|
4380
4443
|
deleteBranch: true
|
|
4381
4444
|
});
|
|
4382
|
-
log
|
|
4445
|
+
log.info(`Merged PR #${String(prNumber)} and deleted branch ${HEAD_BRANCH}`);
|
|
4383
4446
|
}
|
|
4384
4447
|
function mergeGitHub(dryRun) {
|
|
4385
4448
|
const executor = createRealExecutor();
|
|
4386
4449
|
if (dryRun) {
|
|
4387
4450
|
const prNum = executor.exec(`gh pr view ${HEAD_BRANCH} --json number --jq .number`, { cwd: process.cwd() }).stdout.trim();
|
|
4388
4451
|
if (!prNum) throw new FatalError(`No open PR found for branch ${HEAD_BRANCH}`);
|
|
4389
|
-
log
|
|
4452
|
+
log.info(`[dry-run] Would merge PR #${prNum} and delete branch ${HEAD_BRANCH}`);
|
|
4390
4453
|
return;
|
|
4391
4454
|
}
|
|
4392
4455
|
const result = executor.exec(`gh pr merge ${HEAD_BRANCH} --merge --delete-branch`, { cwd: process.cwd() });
|
|
4393
4456
|
if (result.exitCode !== 0) throw new FatalError(`Failed to merge PR: ${result.stderr || result.stdout || "unknown error"}`);
|
|
4394
|
-
log
|
|
4457
|
+
log.info(`Merged changesets PR and deleted branch ${HEAD_BRANCH}`);
|
|
4395
4458
|
}
|
|
4396
4459
|
//#endregion
|
|
4397
4460
|
//#region src/release/simple.ts
|
|
@@ -4421,20 +4484,15 @@ function readVersion(executor, cwd) {
|
|
|
4421
4484
|
if (!pkg?.version) throw new FatalError("No version field found in package.json");
|
|
4422
4485
|
return pkg.version;
|
|
4423
4486
|
}
|
|
4424
|
-
/**
|
|
4425
|
-
function
|
|
4426
|
-
|
|
4427
|
-
const name = isGitHub ? "github-actions[bot]" : "forgejo-actions[bot]";
|
|
4428
|
-
const email = isGitHub ? "github-actions[bot]@users.noreply.github.com" : "forgejo-actions[bot]@noreply.localhost";
|
|
4429
|
-
executor.exec(`git config user.name "${name}"`, { cwd: config.cwd });
|
|
4430
|
-
executor.exec(`git config user.email "${email}"`, { cwd: config.cwd });
|
|
4431
|
-
debug(config, `Configured git identity: ${name} <${email}>`);
|
|
4487
|
+
/** Resolve the platform type string for git identity configuration. */
|
|
4488
|
+
function platformType(config) {
|
|
4489
|
+
return config.platform?.type === "github" ? "github" : "forgejo";
|
|
4432
4490
|
}
|
|
4433
4491
|
/** Run the full commit-and-tag-version release flow. */
|
|
4434
4492
|
async function runSimpleRelease(executor, config) {
|
|
4435
|
-
configureGitIdentity(executor, config);
|
|
4493
|
+
configureGitIdentity(executor, platformType(config), config.cwd);
|
|
4436
4494
|
const command = buildCommand(config);
|
|
4437
|
-
log
|
|
4495
|
+
log.info(`Running: ${command}`);
|
|
4438
4496
|
const versionResult = executor.exec(command, { cwd: config.cwd });
|
|
4439
4497
|
debugExec(config, "commit-and-tag-version", versionResult);
|
|
4440
4498
|
if (versionResult.exitCode !== 0) throw new FatalError(`commit-and-tag-version failed (exit code ${String(versionResult.exitCode)}):\n${versionResult.stderr || versionResult.stdout}`);
|
|
@@ -4444,12 +4502,12 @@ async function runSimpleRelease(executor, config) {
|
|
|
4444
4502
|
debugExec(config, "git describe", tagResult);
|
|
4445
4503
|
const tag = tagResult.stdout.trim();
|
|
4446
4504
|
if (!tag) throw new FatalError("Could not determine the new tag from git describe");
|
|
4447
|
-
log
|
|
4505
|
+
log.info(`Version ${version} tagged as ${tag}`);
|
|
4448
4506
|
if (config.dryRun) {
|
|
4449
4507
|
const slidingTags = config.noSlidingTags ? [] : computeSlidingTags(version);
|
|
4450
|
-
log
|
|
4451
|
-
if (slidingTags.length > 0) log
|
|
4452
|
-
if (!config.noRelease && config.platform) log
|
|
4508
|
+
log.info(`[dry-run] Would push to origin with --follow-tags`);
|
|
4509
|
+
if (slidingTags.length > 0) log.info(`[dry-run] Would create sliding tags: ${slidingTags.join(", ")}`);
|
|
4510
|
+
if (!config.noRelease && config.platform) log.info(`[dry-run] Would create ${config.platform.type} release for ${tag}`);
|
|
4453
4511
|
return {
|
|
4454
4512
|
version,
|
|
4455
4513
|
tag,
|
|
@@ -4470,7 +4528,7 @@ async function runSimpleRelease(executor, config) {
|
|
|
4470
4528
|
debugExec(config, "git push", pushResult);
|
|
4471
4529
|
if (pushResult.exitCode !== 0) throw new FatalError(`git push failed (exit code ${String(pushResult.exitCode)}):\n${pushResult.stderr || pushResult.stdout}`);
|
|
4472
4530
|
pushed = true;
|
|
4473
|
-
log
|
|
4531
|
+
log.info("Pushed to origin");
|
|
4474
4532
|
}
|
|
4475
4533
|
let slidingTags = [];
|
|
4476
4534
|
if (!config.noSlidingTags && pushed) {
|
|
@@ -4481,8 +4539,8 @@ async function runSimpleRelease(executor, config) {
|
|
|
4481
4539
|
}
|
|
4482
4540
|
const forcePushResult = executor.exec(`git push origin ${slidingTags.join(" ")} --force`, { cwd: config.cwd });
|
|
4483
4541
|
debugExec(config, "force-push sliding tags", forcePushResult);
|
|
4484
|
-
if (forcePushResult.exitCode !== 0) log
|
|
4485
|
-
else log
|
|
4542
|
+
if (forcePushResult.exitCode !== 0) log.warn(`Warning: Failed to push sliding tags: ${forcePushResult.stderr || forcePushResult.stdout}`);
|
|
4543
|
+
else log.info(`Created sliding tags: ${slidingTags.join(", ")}`);
|
|
4486
4544
|
}
|
|
4487
4545
|
let releaseCreated = false;
|
|
4488
4546
|
if (!config.noRelease && config.platform) releaseCreated = await createPlatformRelease(executor, config, tag);
|
|
@@ -4497,21 +4555,20 @@ async function runSimpleRelease(executor, config) {
|
|
|
4497
4555
|
async function createPlatformRelease(executor, config, tag) {
|
|
4498
4556
|
if (!config.platform) return false;
|
|
4499
4557
|
if (config.platform.type === "forgejo") {
|
|
4500
|
-
if (await
|
|
4558
|
+
if (await ensureRelease(executor, config.platform.conn, tag) === "exists") {
|
|
4501
4559
|
debug(config, `Release for ${tag} already exists, skipping`);
|
|
4502
4560
|
return false;
|
|
4503
4561
|
}
|
|
4504
|
-
|
|
4505
|
-
log$2.info(`Created Forgejo release for ${tag}`);
|
|
4562
|
+
log.info(`Created Forgejo release for ${tag}`);
|
|
4506
4563
|
return true;
|
|
4507
4564
|
}
|
|
4508
4565
|
const ghResult = executor.exec(`gh release create ${tag} --generate-notes`, { cwd: config.cwd });
|
|
4509
4566
|
debugExec(config, "gh release create", ghResult);
|
|
4510
4567
|
if (ghResult.exitCode !== 0) {
|
|
4511
|
-
log
|
|
4568
|
+
log.warn(`Warning: Failed to create GitHub release: ${ghResult.stderr || ghResult.stdout}`);
|
|
4512
4569
|
return false;
|
|
4513
4570
|
}
|
|
4514
|
-
log
|
|
4571
|
+
log.info(`Created GitHub release for ${tag}`);
|
|
4515
4572
|
return true;
|
|
4516
4573
|
}
|
|
4517
4574
|
//#endregion
|
|
@@ -4528,7 +4585,7 @@ const releaseSimpleCommand = defineCommand({
|
|
|
4528
4585
|
},
|
|
4529
4586
|
verbose: {
|
|
4530
4587
|
type: "boolean",
|
|
4531
|
-
description: "Enable detailed debug logging (also enabled by
|
|
4588
|
+
description: "Enable detailed debug logging (also enabled by TOOLING_DEBUG env var)"
|
|
4532
4589
|
},
|
|
4533
4590
|
"no-push": {
|
|
4534
4591
|
type: "boolean",
|
|
@@ -4557,7 +4614,7 @@ const releaseSimpleCommand = defineCommand({
|
|
|
4557
4614
|
},
|
|
4558
4615
|
async run({ args }) {
|
|
4559
4616
|
const cwd = process.cwd();
|
|
4560
|
-
const verbose = args.verbose === true ||
|
|
4617
|
+
const verbose = args.verbose === true || isEnvVerbose();
|
|
4561
4618
|
const noRelease = args["no-release"] === true;
|
|
4562
4619
|
let platform;
|
|
4563
4620
|
if (!noRelease) {
|
|
@@ -4641,12 +4698,12 @@ const ciReporter = {
|
|
|
4641
4698
|
const localReporter = {
|
|
4642
4699
|
groupStart: (_name) => {},
|
|
4643
4700
|
groupEnd: () => {},
|
|
4644
|
-
passed: (name) => log
|
|
4645
|
-
failed: (name) => log
|
|
4646
|
-
undefinedCheck: (name) => log
|
|
4647
|
-
skippedNotDefined: (names) => log
|
|
4648
|
-
allPassed: () => log
|
|
4649
|
-
anyFailed: (names) => log
|
|
4701
|
+
passed: (name) => log.success(name),
|
|
4702
|
+
failed: (name) => log.error(`${name} failed`),
|
|
4703
|
+
undefinedCheck: (name) => log.error(`${name} not defined in package.json`),
|
|
4704
|
+
skippedNotDefined: (names) => log.info(`Skipped (not defined): ${names.join(", ")}`),
|
|
4705
|
+
allPassed: () => log.success("All checks passed"),
|
|
4706
|
+
anyFailed: (names) => log.error(`Failed checks: ${names.join(", ")}`)
|
|
4650
4707
|
};
|
|
4651
4708
|
function runRunChecks(targetDir, options = {}) {
|
|
4652
4709
|
const exec = options.execCommand ?? defaultExecCommand;
|
|
@@ -4656,6 +4713,7 @@ function runRunChecks(targetDir, options = {}) {
|
|
|
4656
4713
|
const isCI = Boolean(process.env["CI"]);
|
|
4657
4714
|
const failFast = options.failFast ?? !isCI;
|
|
4658
4715
|
const reporter = isCI ? ciReporter : localReporter;
|
|
4716
|
+
const vc = { verbose: options.verbose ?? false };
|
|
4659
4717
|
const definedScripts = getScripts(targetDir);
|
|
4660
4718
|
const addedNames = new Set(add);
|
|
4661
4719
|
const allChecks = [...CHECKS, ...add.map((name) => ({ name }))];
|
|
@@ -4671,11 +4729,13 @@ function runRunChecks(targetDir, options = {}) {
|
|
|
4671
4729
|
continue;
|
|
4672
4730
|
}
|
|
4673
4731
|
const cmd = check.args ? `pnpm run ${check.name} ${check.args}` : `pnpm run ${check.name}`;
|
|
4732
|
+
debug(vc, `Running: ${cmd} (in ${targetDir})`);
|
|
4674
4733
|
reporter.groupStart(check.name);
|
|
4675
4734
|
const start = Date.now();
|
|
4676
4735
|
const exitCode = exec(cmd, targetDir);
|
|
4677
4736
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
4678
4737
|
reporter.groupEnd();
|
|
4738
|
+
debug(vc, `${check.name}: exit code ${String(exitCode)}, ${elapsed}s`);
|
|
4679
4739
|
if (exitCode === 0) reporter.passed(check.name, elapsed);
|
|
4680
4740
|
else {
|
|
4681
4741
|
reporter.failed(check.name, elapsed);
|
|
@@ -4716,13 +4776,19 @@ const runChecksCommand = defineCommand({
|
|
|
4716
4776
|
type: "boolean",
|
|
4717
4777
|
description: "Stop on first failure (default: true in dev, false in CI)",
|
|
4718
4778
|
required: false
|
|
4779
|
+
},
|
|
4780
|
+
verbose: {
|
|
4781
|
+
type: "boolean",
|
|
4782
|
+
description: "Emit detailed debug logging",
|
|
4783
|
+
required: false
|
|
4719
4784
|
}
|
|
4720
4785
|
},
|
|
4721
4786
|
run({ args }) {
|
|
4722
4787
|
const exitCode = runRunChecks(path.resolve(args.dir ?? "."), {
|
|
4723
4788
|
skip: args.skip ? new Set(args.skip.split(",").map((s) => s.trim())) : void 0,
|
|
4724
4789
|
add: args.add ? args.add.split(",").map((s) => s.trim()) : void 0,
|
|
4725
|
-
failFast: args["fail-fast"] ? true : void 0
|
|
4790
|
+
failFast: args["fail-fast"] ? true : void 0,
|
|
4791
|
+
verbose: args.verbose === true || isEnvVerbose()
|
|
4726
4792
|
});
|
|
4727
4793
|
process.exitCode = exitCode;
|
|
4728
4794
|
}
|
|
@@ -4739,10 +4805,16 @@ const publishDockerCommand = defineCommand({
|
|
|
4739
4805
|
name: "docker:publish",
|
|
4740
4806
|
description: "Build, tag, and push Docker images for packages with an image:build script"
|
|
4741
4807
|
},
|
|
4742
|
-
args: {
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4808
|
+
args: {
|
|
4809
|
+
"dry-run": {
|
|
4810
|
+
type: "boolean",
|
|
4811
|
+
description: "Build and tag images but skip login, push, and logout"
|
|
4812
|
+
},
|
|
4813
|
+
verbose: {
|
|
4814
|
+
type: "boolean",
|
|
4815
|
+
description: "Emit detailed debug logging"
|
|
4816
|
+
}
|
|
4817
|
+
},
|
|
4746
4818
|
async run({ args }) {
|
|
4747
4819
|
const config = {
|
|
4748
4820
|
cwd: process.cwd(),
|
|
@@ -4750,7 +4822,8 @@ const publishDockerCommand = defineCommand({
|
|
|
4750
4822
|
registryNamespace: requireEnv("DOCKER_REGISTRY_NAMESPACE"),
|
|
4751
4823
|
username: requireEnv("DOCKER_USERNAME"),
|
|
4752
4824
|
password: requireEnv("DOCKER_PASSWORD"),
|
|
4753
|
-
dryRun: args["dry-run"] === true
|
|
4825
|
+
dryRun: args["dry-run"] === true,
|
|
4826
|
+
verbose: args.verbose === true || isEnvVerbose()
|
|
4754
4827
|
};
|
|
4755
4828
|
runDockerPublish(createRealExecutor(), config);
|
|
4756
4829
|
}
|
|
@@ -4783,6 +4856,10 @@ const dockerBuildCommand = defineCommand({
|
|
|
4783
4856
|
type: "string",
|
|
4784
4857
|
description: "Build a single package by directory path (e.g. packages/server). Useful as an image:build script."
|
|
4785
4858
|
},
|
|
4859
|
+
verbose: {
|
|
4860
|
+
type: "boolean",
|
|
4861
|
+
description: "Emit detailed debug logging"
|
|
4862
|
+
},
|
|
4786
4863
|
_: {
|
|
4787
4864
|
type: "positional",
|
|
4788
4865
|
required: false,
|
|
@@ -4793,6 +4870,7 @@ const dockerBuildCommand = defineCommand({
|
|
|
4793
4870
|
const executor = createRealExecutor();
|
|
4794
4871
|
const rawExtra = args._ ?? [];
|
|
4795
4872
|
const extraArgs = Array.isArray(rawExtra) ? rawExtra.map(String) : [String(rawExtra)];
|
|
4873
|
+
const verbose = args.verbose === true || isEnvVerbose();
|
|
4796
4874
|
let cwd = process.cwd();
|
|
4797
4875
|
let packageDir = args.package;
|
|
4798
4876
|
if (!packageDir) {
|
|
@@ -4803,7 +4881,8 @@ const dockerBuildCommand = defineCommand({
|
|
|
4803
4881
|
runDockerBuild(executor, {
|
|
4804
4882
|
cwd,
|
|
4805
4883
|
packageDir,
|
|
4806
|
-
extraArgs: extraArgs.filter((a) => a.length > 0)
|
|
4884
|
+
extraArgs: extraArgs.filter((a) => a.length > 0),
|
|
4885
|
+
verbose
|
|
4807
4886
|
});
|
|
4808
4887
|
}
|
|
4809
4888
|
});
|
|
@@ -5020,12 +5099,6 @@ function writeTempOverlay(content) {
|
|
|
5020
5099
|
writeFileSync(filePath, content, "utf-8");
|
|
5021
5100
|
return filePath;
|
|
5022
5101
|
}
|
|
5023
|
-
function log(message) {
|
|
5024
|
-
console.log(message);
|
|
5025
|
-
}
|
|
5026
|
-
function warn(message) {
|
|
5027
|
-
console.warn(message);
|
|
5028
|
-
}
|
|
5029
5102
|
const dockerCheckCommand = defineCommand({
|
|
5030
5103
|
meta: {
|
|
5031
5104
|
name: "docker:check",
|
|
@@ -5039,12 +5112,16 @@ const dockerCheckCommand = defineCommand({
|
|
|
5039
5112
|
"poll-interval": {
|
|
5040
5113
|
type: "string",
|
|
5041
5114
|
description: "Interval between polling attempts, in ms (default: 5000)"
|
|
5115
|
+
},
|
|
5116
|
+
verbose: {
|
|
5117
|
+
type: "boolean",
|
|
5118
|
+
description: "Emit detailed debug logging"
|
|
5042
5119
|
}
|
|
5043
5120
|
},
|
|
5044
5121
|
async run({ args }) {
|
|
5045
5122
|
const cwd = process.cwd();
|
|
5046
5123
|
if (loadToolingConfig(cwd)?.dockerCheck === false) {
|
|
5047
|
-
log("Docker check is disabled in .tooling.json");
|
|
5124
|
+
log.info("Docker check is disabled in .tooling.json");
|
|
5048
5125
|
return;
|
|
5049
5126
|
}
|
|
5050
5127
|
const defaults = computeCheckDefaults(cwd);
|
|
@@ -5052,8 +5129,8 @@ const dockerCheckCommand = defineCommand({
|
|
|
5052
5129
|
if (!defaults.checkOverlay) {
|
|
5053
5130
|
const composeCwd = defaults.composeCwd ?? cwd;
|
|
5054
5131
|
const expectedOverlay = (defaults.composeFiles[0] ?? "docker-compose.yaml").replace(/\.(yaml|yml)$/, ".check.$1");
|
|
5055
|
-
warn(`Compose files found but no check overlay. Create ${path.relative(cwd, path.join(composeCwd, expectedOverlay))} to enable docker:check.`);
|
|
5056
|
-
warn("To suppress this warning, set \"dockerCheck\": false in .tooling.json.");
|
|
5132
|
+
log.warn(`Compose files found but no check overlay. Create ${path.relative(cwd, path.join(composeCwd, expectedOverlay))} to enable docker:check.`);
|
|
5133
|
+
log.warn("To suppress this warning, set \"dockerCheck\": false in .tooling.json.");
|
|
5057
5134
|
return;
|
|
5058
5135
|
}
|
|
5059
5136
|
if (!defaults.services || defaults.services.length === 0) throw new FatalError("No services found in compose files.");
|
|
@@ -5066,7 +5143,7 @@ const dockerCheckCommand = defineCommand({
|
|
|
5066
5143
|
if (rootPkg?.name) {
|
|
5067
5144
|
const dockerPackages = detectDockerPackages(fileReader, cwd, rootPkg.name);
|
|
5068
5145
|
const composeImages = extractComposeImageNames(services);
|
|
5069
|
-
for (const pkg of dockerPackages) if (!composeImages.some((img) => img === pkg.imageName || img.endsWith(`/${pkg.imageName}`))) warn(`Docker package "${pkg.dir}" (image: ${pkg.imageName}) is not referenced in any compose service.`);
|
|
5146
|
+
for (const pkg of dockerPackages) if (!composeImages.some((img) => img === pkg.imageName || img.endsWith(`/${pkg.imageName}`))) log.warn(`Docker package "${pkg.dir}" (image: ${pkg.imageName}) is not referenced in any compose service.`);
|
|
5070
5147
|
}
|
|
5071
5148
|
}
|
|
5072
5149
|
const tempOverlayPath = writeTempOverlay(generateCheckOverlay(services));
|
|
@@ -5087,7 +5164,8 @@ const dockerCheckCommand = defineCommand({
|
|
|
5087
5164
|
buildCwd: defaults.buildCwd,
|
|
5088
5165
|
healthChecks: defaults.healthChecks ? toHttpHealthChecks(defaults.healthChecks) : [],
|
|
5089
5166
|
timeoutMs: args.timeout ? Number.parseInt(args.timeout, 10) : defaults.timeoutMs,
|
|
5090
|
-
pollIntervalMs: args["poll-interval"] ? Number.parseInt(args["poll-interval"], 10) : defaults.pollIntervalMs
|
|
5167
|
+
pollIntervalMs: args["poll-interval"] ? Number.parseInt(args["poll-interval"], 10) : defaults.pollIntervalMs,
|
|
5168
|
+
verbose: args.verbose === true || isEnvVerbose()
|
|
5091
5169
|
};
|
|
5092
5170
|
const result = await runDockerCheck(createRealExecutor$1(), config);
|
|
5093
5171
|
if (!result.success) throw new FatalError(`Check failed (${result.reason}): ${result.message}`);
|
|
@@ -5103,7 +5181,7 @@ const dockerCheckCommand = defineCommand({
|
|
|
5103
5181
|
const main = defineCommand({
|
|
5104
5182
|
meta: {
|
|
5105
5183
|
name: "bst",
|
|
5106
|
-
version: "0.
|
|
5184
|
+
version: "0.34.0",
|
|
5107
5185
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
5108
5186
|
},
|
|
5109
5187
|
subCommands: {
|
|
@@ -5119,7 +5197,7 @@ const main = defineCommand({
|
|
|
5119
5197
|
"docker:check": dockerCheckCommand
|
|
5120
5198
|
}
|
|
5121
5199
|
});
|
|
5122
|
-
console.log(`@bensandee/tooling v0.
|
|
5200
|
+
console.log(`@bensandee/tooling v0.34.0`);
|
|
5123
5201
|
async function run() {
|
|
5124
5202
|
await runMain(main);
|
|
5125
5203
|
process.exit(process.exitCode ?? 0);
|