@git.zone/cli 2.18.1 → 2.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.smartconfig.json +2 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/gitzone.cli.d.ts +1 -1
- package/dist_ts/gitzone.cli.js +100 -126
- package/dist_ts/helpers.climode.d.ts +2 -0
- package/dist_ts/helpers.climode.js +31 -2
- package/dist_ts/helpers.smartconfigmigrations.js +53 -4
- package/dist_ts/helpers.workflow.d.ts +12 -2
- package/dist_ts/helpers.workflow.js +9 -2
- package/dist_ts/mod_config/index.js +254 -29
- package/dist_ts/mod_format/classes.baseformatter.d.ts +3 -1
- package/dist_ts/mod_format/classes.baseformatter.js +7 -1
- package/dist_ts/mod_format/classes.formatplanner.d.ts +2 -0
- package/dist_ts/mod_format/classes.formatplanner.js +48 -4
- package/dist_ts/mod_format/formatters/license.formatter.d.ts +4 -1
- package/dist_ts/mod_format/formatters/license.formatter.js +32 -10
- package/dist_ts/mod_format/formatters/prettier.formatter.d.ts +0 -3
- package/dist_ts/mod_format/formatters/prettier.formatter.js +53 -62
- package/dist_ts/mod_format/index.d.ts +1 -1
- package/dist_ts/mod_format/index.js +237 -8
- package/dist_ts/mod_format/interfaces.format.d.ts +7 -11
- package/dist_ts/mod_format/interfaces.format.js +1 -1
- package/dist_ts/mod_release/index.js +50 -23
- package/dist_ts/mod_standard/index.js +2 -1
- package/package.json +1 -1
- package/readme.hints.md +10 -0
- package/readme.md +31 -4
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/gitzone.cli.ts +104 -144
- package/ts/helpers.climode.ts +36 -1
- package/ts/helpers.smartconfigmigrations.ts +57 -3
- package/ts/helpers.workflow.ts +20 -3
- package/ts/mod_config/index.ts +278 -29
- package/ts/mod_format/classes.baseformatter.ts +13 -1
- package/ts/mod_format/classes.formatplanner.ts +66 -4
- package/ts/mod_format/formatters/license.formatter.ts +43 -15
- package/ts/mod_format/formatters/prettier.formatter.ts +54 -66
- package/ts/mod_format/index.ts +289 -8
- package/ts/mod_format/interfaces.format.ts +8 -11
- package/ts/mod_release/index.ts +46 -23
- package/ts/mod_standard/index.ts +1 -0
package/ts/mod_format/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { TsconfigFormatter } from "./formatters/tsconfig.formatter.js";
|
|
|
22
22
|
import { PrettierFormatter } from "./formatters/prettier.formatter.js";
|
|
23
23
|
import { ReadmeFormatter } from "./formatters/readme.formatter.js";
|
|
24
24
|
import { CopyFormatter } from "./formatters/copy.formatter.js";
|
|
25
|
+
import type { ICheckResult, IFormatPlan } from "./interfaces.format.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Rename npmextra.json or smartconfig.json to .smartconfig.json
|
|
@@ -94,9 +95,39 @@ const getFormatConfig = async () => {
|
|
|
94
95
|
};
|
|
95
96
|
};
|
|
96
97
|
|
|
98
|
+
const normalizeModuleList = (value: unknown): string[] => {
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
return value.flatMap((item) => normalizeModuleList(item));
|
|
101
|
+
}
|
|
102
|
+
if (typeof value !== "string") {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
return value
|
|
106
|
+
.split(",")
|
|
107
|
+
.map((item) => item.trim())
|
|
108
|
+
.filter(Boolean);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const getPlanStatus = (plan: IFormatPlan) => {
|
|
112
|
+
const errorWarnings = plan.warnings.filter(
|
|
113
|
+
(warning) => warning.level === "error",
|
|
114
|
+
);
|
|
115
|
+
const hasChanges = plan.summary.totalFiles > 0;
|
|
116
|
+
const hasErrors = errorWarnings.length > 0;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
ok: !hasChanges && !hasErrors,
|
|
120
|
+
hasChanges,
|
|
121
|
+
hasErrors,
|
|
122
|
+
errorCount: errorWarnings.length,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
97
126
|
const createActiveFormatters = async (options: {
|
|
98
127
|
interactive: boolean;
|
|
99
128
|
jsonOutput: boolean;
|
|
129
|
+
only?: string[];
|
|
130
|
+
skip?: string[];
|
|
100
131
|
}) => {
|
|
101
132
|
const project = await Project.fromCwd({ requireProjectType: false });
|
|
102
133
|
const context = new FormatContext(options);
|
|
@@ -107,11 +138,19 @@ const createActiveFormatters = async (options: {
|
|
|
107
138
|
([, FormatterClass]) => new FormatterClass(context, project),
|
|
108
139
|
);
|
|
109
140
|
|
|
141
|
+
const onlyModules = options.only?.length
|
|
142
|
+
? options.only
|
|
143
|
+
: formatConfig.modules.only;
|
|
144
|
+
const skipModules = [
|
|
145
|
+
...formatConfig.modules.skip,
|
|
146
|
+
...(options.skip || []),
|
|
147
|
+
];
|
|
148
|
+
|
|
110
149
|
const activeFormatters = formatters.filter((formatter) => {
|
|
111
|
-
if (
|
|
112
|
-
return
|
|
150
|
+
if (onlyModules.length > 0) {
|
|
151
|
+
return onlyModules.includes(formatter.name);
|
|
113
152
|
}
|
|
114
|
-
if (
|
|
153
|
+
if (skipModules.includes(formatter.name)) {
|
|
115
154
|
return false;
|
|
116
155
|
}
|
|
117
156
|
return true;
|
|
@@ -129,11 +168,15 @@ const buildFormatPlan = async (options: {
|
|
|
129
168
|
fromPlan?: string;
|
|
130
169
|
interactive: boolean;
|
|
131
170
|
jsonOutput: boolean;
|
|
171
|
+
only?: string[];
|
|
172
|
+
skip?: string[];
|
|
132
173
|
}) => {
|
|
133
174
|
const { context, planner, formatConfig, activeFormatters } =
|
|
134
175
|
await createActiveFormatters({
|
|
135
176
|
interactive: options.interactive,
|
|
136
177
|
jsonOutput: options.jsonOutput,
|
|
178
|
+
only: options.only,
|
|
179
|
+
skip: options.skip,
|
|
137
180
|
});
|
|
138
181
|
|
|
139
182
|
const plan = options.fromPlan
|
|
@@ -167,6 +210,182 @@ const serializePlan = (plan: any) => {
|
|
|
167
210
|
};
|
|
168
211
|
};
|
|
169
212
|
|
|
213
|
+
const buildFormatFixPrompt = (
|
|
214
|
+
plan: IFormatPlan,
|
|
215
|
+
extraInstructions: string,
|
|
216
|
+
): string => {
|
|
217
|
+
const promptParts = [
|
|
218
|
+
"Other /c-* commands can be found at ~/.config/opencode/commands/*",
|
|
219
|
+
"# gitzone format fix",
|
|
220
|
+
"",
|
|
221
|
+
`Working directory: ${process.cwd()}`,
|
|
222
|
+
"",
|
|
223
|
+
"Repair project formatting so `gitzone format check --json` passes.",
|
|
224
|
+
"",
|
|
225
|
+
"Rules:",
|
|
226
|
+
"- Read `.smartconfig.json`, `package.json`, `tsconfig.json`, and the current format plan before editing.",
|
|
227
|
+
"- Prefer deterministic gitzone standards, bundled assets, and existing project conventions.",
|
|
228
|
+
"- Keep changes focused on formatting, metadata normalization, templates, and config consistency.",
|
|
229
|
+
"- Do not commit, release, install dependencies, or modify unrelated files.",
|
|
230
|
+
"- Use pnpm commands only if commands are needed.",
|
|
231
|
+
"- Run `gitzone format --write --yes` after changes.",
|
|
232
|
+
"- Run `gitzone format check --json` after changes and keep fixing until it passes.",
|
|
233
|
+
"- Run `git diff --check` after changes to catch whitespace problems.",
|
|
234
|
+
"",
|
|
235
|
+
"Current format plan:",
|
|
236
|
+
JSON.stringify(serializePlan(plan), null, 2),
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
if (extraInstructions) {
|
|
240
|
+
promptParts.push("", "Additional user instructions:", extraInstructions);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return promptParts.join("\n");
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const handleFormatFix = async (
|
|
247
|
+
options: Record<string, any>,
|
|
248
|
+
mode: ICliMode,
|
|
249
|
+
): Promise<void> => {
|
|
250
|
+
if (mode.json) {
|
|
251
|
+
printJson({
|
|
252
|
+
ok: false,
|
|
253
|
+
error:
|
|
254
|
+
"JSON output is not supported for `gitzone format fix`. Use `gitzone format check --json` for machine-readable diagnostics.",
|
|
255
|
+
});
|
|
256
|
+
process.exitCode = 1;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const extraInstructions = (options._?.slice(2).join(" ") || "").trim();
|
|
261
|
+
const force = Boolean(options.force);
|
|
262
|
+
const autoApprove = Boolean(options.yes || mode.yes);
|
|
263
|
+
const formatConfig = await getFormatConfig();
|
|
264
|
+
const interactive =
|
|
265
|
+
options.interactive ?? (mode.interactive && formatConfig.interactive);
|
|
266
|
+
const only = normalizeModuleList(options.only);
|
|
267
|
+
const skip = normalizeModuleList(options.skip);
|
|
268
|
+
|
|
269
|
+
const buildCurrentPlan = async () => {
|
|
270
|
+
return await buildFormatPlan({
|
|
271
|
+
interactive,
|
|
272
|
+
jsonOutput: false,
|
|
273
|
+
only,
|
|
274
|
+
skip,
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
logger.log("info", "Analyzing project for format fixes...");
|
|
279
|
+
let { plan } = await buildCurrentPlan();
|
|
280
|
+
let status = getPlanStatus(plan);
|
|
281
|
+
|
|
282
|
+
if (status.ok && !extraInstructions && !force) {
|
|
283
|
+
logger.log(
|
|
284
|
+
"success",
|
|
285
|
+
"Format check found no issues. Use `gitzone format fix --force` to run opencode anyway.",
|
|
286
|
+
);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!autoApprove) {
|
|
291
|
+
if (!mode.interactive) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
"Format fix requires an interactive terminal or `-y` to run non-interactively.",
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const confirmed = await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
|
297
|
+
`Run format fixes? (${plan.summary.totalFiles} planned change(s), ${status.errorCount} error warning(s))`,
|
|
298
|
+
true,
|
|
299
|
+
);
|
|
300
|
+
if (!confirmed) {
|
|
301
|
+
logger.log("info", "Format fix cancelled.");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (status.hasChanges) {
|
|
307
|
+
logger.log("info", "Applying deterministic format changes first...");
|
|
308
|
+
await run({
|
|
309
|
+
_: ["format"],
|
|
310
|
+
write: true,
|
|
311
|
+
yes: true,
|
|
312
|
+
interactive: false,
|
|
313
|
+
verbose: options.verbose,
|
|
314
|
+
detailed: options.detailed,
|
|
315
|
+
only: options.only,
|
|
316
|
+
skip: options.skip,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
({ plan } = await buildCurrentPlan());
|
|
320
|
+
status = getPlanStatus(plan);
|
|
321
|
+
if (status.ok && !extraInstructions && !force) {
|
|
322
|
+
logger.log("success", "Format fix completed successfully.");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const opencodeArgs = [
|
|
328
|
+
"run",
|
|
329
|
+
"--title",
|
|
330
|
+
"gitzone format fix",
|
|
331
|
+
"--dir",
|
|
332
|
+
process.cwd(),
|
|
333
|
+
];
|
|
334
|
+
if (autoApprove) {
|
|
335
|
+
opencodeArgs.push("--dangerously-skip-permissions");
|
|
336
|
+
}
|
|
337
|
+
opencodeArgs.push(buildFormatFixPrompt(plan, extraInstructions));
|
|
338
|
+
|
|
339
|
+
logger.log("info", "Starting opencode format fix...");
|
|
340
|
+
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
341
|
+
executor: "bash",
|
|
342
|
+
sourceFilePaths: [],
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
let result: plugins.smartshell.IExecResult;
|
|
346
|
+
try {
|
|
347
|
+
result = await smartshellInstance.execSpawn("opencode", opencodeArgs, {
|
|
348
|
+
stdio: "inherit",
|
|
349
|
+
});
|
|
350
|
+
} catch (error) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
`Failed to run opencode: ${error instanceof Error ? error.message : String(error)}`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (result.exitCode !== 0) {
|
|
357
|
+
logger.log("error", `opencode exited with code ${result.exitCode}`);
|
|
358
|
+
process.exitCode = result.exitCode || 1;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
logger.log("info", "Running deterministic format pass after opencode...");
|
|
363
|
+
await run({
|
|
364
|
+
_: ["format"],
|
|
365
|
+
write: true,
|
|
366
|
+
yes: true,
|
|
367
|
+
interactive: false,
|
|
368
|
+
verbose: options.verbose,
|
|
369
|
+
detailed: options.detailed,
|
|
370
|
+
only: options.only,
|
|
371
|
+
skip: options.skip,
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const { planner: finalPlanner, plan: finalPlan } = await buildCurrentPlan();
|
|
375
|
+
await finalPlanner.displayPlan(finalPlan, options.detailed);
|
|
376
|
+
const finalStatus = getPlanStatus(finalPlan);
|
|
377
|
+
if (finalStatus.ok) {
|
|
378
|
+
logger.log("success", "Format fix completed successfully.");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
logger.log(
|
|
383
|
+
"error",
|
|
384
|
+
`Format fix left ${finalPlan.summary.totalFiles} planned change(s) and ${finalStatus.errorCount} error warning(s).`,
|
|
385
|
+
);
|
|
386
|
+
process.exitCode = 1;
|
|
387
|
+
};
|
|
388
|
+
|
|
170
389
|
export let run = async (
|
|
171
390
|
options: {
|
|
172
391
|
write?: boolean;
|
|
@@ -194,8 +413,25 @@ export let run = async (
|
|
|
194
413
|
setVerboseMode(true);
|
|
195
414
|
}
|
|
196
415
|
|
|
416
|
+
if (subcommand === "fix") {
|
|
417
|
+
await handleFormatFix(options, mode);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
197
421
|
const shouldWrite = options.write ?? options.dryRun === false;
|
|
198
422
|
const treatAsPlan = subcommand === "plan";
|
|
423
|
+
const treatAsCheck = subcommand === "check" || Boolean(options.check);
|
|
424
|
+
|
|
425
|
+
if (treatAsCheck && shouldWrite) {
|
|
426
|
+
const error = "`gitzone format check` is read-only and cannot be combined with --write.";
|
|
427
|
+
if (mode.json) {
|
|
428
|
+
printJson({ ok: false, error });
|
|
429
|
+
} else {
|
|
430
|
+
logger.log("error", error);
|
|
431
|
+
}
|
|
432
|
+
process.exitCode = 1;
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
199
435
|
|
|
200
436
|
if (mode.json && shouldWrite) {
|
|
201
437
|
printJson({
|
|
@@ -212,7 +448,9 @@ export let run = async (
|
|
|
212
448
|
const formatConfig = await getFormatConfig();
|
|
213
449
|
const interactive =
|
|
214
450
|
options.interactive ?? (mode.interactive && formatConfig.interactive);
|
|
215
|
-
const autoApprove = options.yes ?? formatConfig.autoApprove;
|
|
451
|
+
const autoApprove = options.yes ?? (mode.yes || formatConfig.autoApprove);
|
|
452
|
+
const only = normalizeModuleList(options.only);
|
|
453
|
+
const skip = normalizeModuleList(options.skip);
|
|
216
454
|
|
|
217
455
|
try {
|
|
218
456
|
const planBuilder = async () => {
|
|
@@ -220,6 +458,8 @@ export let run = async (
|
|
|
220
458
|
fromPlan: options.fromPlan,
|
|
221
459
|
interactive,
|
|
222
460
|
jsonOutput: mode.json,
|
|
461
|
+
only,
|
|
462
|
+
skip,
|
|
223
463
|
});
|
|
224
464
|
};
|
|
225
465
|
|
|
@@ -231,7 +471,16 @@ export let run = async (
|
|
|
231
471
|
: await planBuilder();
|
|
232
472
|
|
|
233
473
|
if (mode.json) {
|
|
234
|
-
|
|
474
|
+
const serializedPlan = serializePlan(plan);
|
|
475
|
+
if (treatAsCheck) {
|
|
476
|
+
const status = getPlanStatus(plan);
|
|
477
|
+
printJson({ ok: status.ok, ...serializedPlan });
|
|
478
|
+
if (!status.ok) {
|
|
479
|
+
process.exitCode = 1;
|
|
480
|
+
}
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
printJson(serializedPlan);
|
|
235
484
|
return;
|
|
236
485
|
}
|
|
237
486
|
|
|
@@ -251,6 +500,20 @@ export let run = async (
|
|
|
251
500
|
return;
|
|
252
501
|
}
|
|
253
502
|
|
|
503
|
+
if (treatAsCheck) {
|
|
504
|
+
const status = getPlanStatus(plan);
|
|
505
|
+
if (status.ok) {
|
|
506
|
+
logger.log("success", "Format check passed");
|
|
507
|
+
} else {
|
|
508
|
+
logger.log(
|
|
509
|
+
"error",
|
|
510
|
+
`Format check failed: ${plan.summary.totalFiles} planned change(s), ${status.errorCount} error warning(s)`,
|
|
511
|
+
);
|
|
512
|
+
process.exitCode = 1;
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
254
517
|
// Show diffs if explicitly requested or before interactive write confirmation
|
|
255
518
|
const showDiffs =
|
|
256
519
|
options.diff || (shouldWrite && interactive && !autoApprove);
|
|
@@ -314,7 +577,6 @@ export let run = async (
|
|
|
314
577
|
}
|
|
315
578
|
};
|
|
316
579
|
|
|
317
|
-
import type { ICheckResult } from "./interfaces.format.js";
|
|
318
580
|
export type { ICheckResult };
|
|
319
581
|
|
|
320
582
|
/**
|
|
@@ -363,7 +625,7 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
363
625
|
if (mode?.json) {
|
|
364
626
|
printJson({
|
|
365
627
|
command: "format",
|
|
366
|
-
usage: "gitzone format [plan] [options]",
|
|
628
|
+
usage: "gitzone format [plan|check|fix] [options]",
|
|
367
629
|
description:
|
|
368
630
|
"Plans formatting changes by default and applies them only with --write.",
|
|
369
631
|
flags: [
|
|
@@ -393,19 +655,33 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
393
655
|
flag: "--diff",
|
|
394
656
|
description: "Show per-file diffs before applying changes",
|
|
395
657
|
},
|
|
658
|
+
{
|
|
659
|
+
flag: "--only <modules>",
|
|
660
|
+
description: "Run only the comma-separated formatter modules",
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
flag: "--skip <modules>",
|
|
664
|
+
description: "Skip the comma-separated formatter modules",
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
flag: "--force",
|
|
668
|
+
description: "Run `format fix` even when the deterministic plan is clean",
|
|
669
|
+
},
|
|
396
670
|
{ flag: "--json", description: "Emit a read-only format plan as JSON" },
|
|
397
671
|
],
|
|
398
672
|
examples: [
|
|
399
673
|
"gitzone format",
|
|
400
674
|
"gitzone format plan --json",
|
|
675
|
+
"gitzone format check",
|
|
401
676
|
"gitzone format --write --yes",
|
|
677
|
+
"gitzone format fix",
|
|
402
678
|
],
|
|
403
679
|
});
|
|
404
680
|
return;
|
|
405
681
|
}
|
|
406
682
|
|
|
407
683
|
console.log("");
|
|
408
|
-
console.log("Usage: gitzone format [plan] [options]");
|
|
684
|
+
console.log("Usage: gitzone format [plan|check|fix] [options]");
|
|
409
685
|
console.log("");
|
|
410
686
|
console.log(
|
|
411
687
|
"Plans formatting changes by default and applies them only with --write.",
|
|
@@ -424,11 +700,16 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
424
700
|
console.log(
|
|
425
701
|
" --diff Show per-file diffs before applying changes",
|
|
426
702
|
);
|
|
703
|
+
console.log(" --only <modules> Run only comma-separated formatter modules");
|
|
704
|
+
console.log(" --skip <modules> Skip comma-separated formatter modules");
|
|
705
|
+
console.log(" --force Run format fix even when the plan is clean");
|
|
427
706
|
console.log(" --json Emit a read-only format plan as JSON");
|
|
428
707
|
console.log("");
|
|
429
708
|
console.log("Examples:");
|
|
430
709
|
console.log(" gitzone format");
|
|
431
710
|
console.log(" gitzone format plan --json");
|
|
711
|
+
console.log(" gitzone format check");
|
|
432
712
|
console.log(" gitzone format --write --yes");
|
|
713
|
+
console.log(" gitzone format fix");
|
|
433
714
|
console.log("");
|
|
434
715
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export type IFormatWarning = {
|
|
2
|
+
level: 'info' | 'warning' | 'error';
|
|
3
|
+
message: string;
|
|
4
|
+
module: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
export type IFormatPlan = {
|
|
2
8
|
summary: {
|
|
3
9
|
totalFiles: number;
|
|
@@ -5,17 +11,8 @@ export type IFormatPlan = {
|
|
|
5
11
|
filesModified: number;
|
|
6
12
|
filesRemoved: number;
|
|
7
13
|
};
|
|
8
|
-
changes:
|
|
9
|
-
|
|
10
|
-
path: string;
|
|
11
|
-
module: string;
|
|
12
|
-
description: string;
|
|
13
|
-
}>;
|
|
14
|
-
warnings: Array<{
|
|
15
|
-
level: 'info' | 'warning' | 'error';
|
|
16
|
-
message: string;
|
|
17
|
-
module: string;
|
|
18
|
-
}>;
|
|
14
|
+
changes: IPlannedChange[];
|
|
15
|
+
warnings: IFormatWarning[];
|
|
19
16
|
};
|
|
20
17
|
|
|
21
18
|
export type IPlannedChange = {
|
package/ts/mod_release/index.ts
CHANGED
|
@@ -107,7 +107,7 @@ export const run = async (argvArg: any) => {
|
|
|
107
107
|
npmResults.push(...(await runNpmTarget(smartshellInstance, workflow)));
|
|
108
108
|
}
|
|
109
109
|
if (workflow.targets.includes("docker")) {
|
|
110
|
-
dockerResults.push(...(await runDockerTarget(smartshellInstance, workflow
|
|
110
|
+
dockerResults.push(...(await runDockerTarget(smartshellInstance, workflow)));
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
printReleaseSummary(newVersion, gitResults, npmResults, dockerResults);
|
|
@@ -262,31 +262,43 @@ async function runNpmTarget(
|
|
|
262
262
|
async function runDockerTarget(
|
|
263
263
|
smartshellInstance: plugins.smartshell.Smartshell,
|
|
264
264
|
workflow: IResolvedReleaseWorkflow,
|
|
265
|
-
newVersion: string,
|
|
266
265
|
): Promise<ITargetResult[]> {
|
|
267
266
|
if (!workflow.dockerEnabled) {
|
|
268
267
|
return [{ target: "docker", status: "skipped", message: "disabled" }];
|
|
269
268
|
}
|
|
270
|
-
if (workflow.dockerImages.length === 0) {
|
|
271
|
-
return [{ target: "docker", status: "failed", message: "no images configured" }];
|
|
272
|
-
}
|
|
273
269
|
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
270
|
+
const command = buildTsdockerPushCommand(workflow);
|
|
271
|
+
const result = await smartshellInstance.exec(command);
|
|
272
|
+
const output = `${result.stdout || ""}\n${(result as any).stderr || ""}\n${(result as any).combinedOutput || ""}`;
|
|
273
|
+
return [{
|
|
274
|
+
target: workflow.dockerPatterns.length > 0
|
|
275
|
+
? `tsdocker:${workflow.dockerPatterns.join(",")}`
|
|
276
|
+
: "tsdocker",
|
|
277
|
+
status: result.exitCode === 0 ? "success" : "failed",
|
|
278
|
+
message: result.exitCode === 0 ? undefined : firstMeaningfulLine(output),
|
|
279
|
+
}];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function buildTsdockerPushCommand(workflow: IResolvedReleaseWorkflow): string {
|
|
283
|
+
const commandParts = ["tsdocker", "push"];
|
|
284
|
+
if (workflow.dockerNoBuild) {
|
|
285
|
+
commandParts.push("--no-build");
|
|
288
286
|
}
|
|
289
|
-
|
|
287
|
+
if (workflow.dockerCached) {
|
|
288
|
+
commandParts.push("--cached");
|
|
289
|
+
}
|
|
290
|
+
if (workflow.dockerParallel === true) {
|
|
291
|
+
commandParts.push("--parallel");
|
|
292
|
+
} else if (typeof workflow.dockerParallel === "number" && Number.isFinite(workflow.dockerParallel) && workflow.dockerParallel > 0) {
|
|
293
|
+
commandParts.push(`--parallel=${Math.floor(workflow.dockerParallel)}`);
|
|
294
|
+
}
|
|
295
|
+
if (workflow.dockerContext) {
|
|
296
|
+
commandParts.push(`--context=${shellQuote(workflow.dockerContext)}`);
|
|
297
|
+
}
|
|
298
|
+
for (const pattern of workflow.dockerPatterns) {
|
|
299
|
+
commandParts.push(shellQuote(pattern));
|
|
300
|
+
}
|
|
301
|
+
return commandParts.join(" ");
|
|
290
302
|
}
|
|
291
303
|
|
|
292
304
|
function isAlreadyPublishedOutput(output: string): boolean {
|
|
@@ -315,11 +327,22 @@ function printReleasePlan(workflow: IResolvedReleaseWorkflow): void {
|
|
|
315
327
|
console.log(`npm registries: ${workflow.npmRegistries.length > 0 ? workflow.npmRegistries.join(", ") : "none"}`);
|
|
316
328
|
}
|
|
317
329
|
if (workflow.targets.includes("docker")) {
|
|
318
|
-
console.log(`docker
|
|
330
|
+
console.log(`docker engine: ${workflow.dockerEngine}`);
|
|
331
|
+
console.log(`docker patterns: ${workflow.dockerPatterns.length > 0 ? workflow.dockerPatterns.join(", ") : "all Dockerfiles"}`);
|
|
332
|
+
console.log(`docker options: ${formatDockerOptions(workflow)}`);
|
|
319
333
|
}
|
|
320
334
|
console.log("");
|
|
321
335
|
}
|
|
322
336
|
|
|
337
|
+
function formatDockerOptions(workflow: IResolvedReleaseWorkflow): string {
|
|
338
|
+
const options: string[] = [];
|
|
339
|
+
if (workflow.dockerCached) options.push("cached");
|
|
340
|
+
if (workflow.dockerParallel) options.push(`parallel=${workflow.dockerParallel === true ? "true" : workflow.dockerParallel}`);
|
|
341
|
+
if (workflow.dockerNoBuild) options.push("no-build");
|
|
342
|
+
if (workflow.dockerContext) options.push(`context=${workflow.dockerContext}`);
|
|
343
|
+
return options.length > 0 ? options.join(", ") : "default";
|
|
344
|
+
}
|
|
345
|
+
|
|
323
346
|
function printReleaseSummary(
|
|
324
347
|
newVersion: string,
|
|
325
348
|
gitResults: ITargetResult[],
|
|
@@ -365,7 +388,7 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
365
388
|
{ flag: "-p, --push", description: "Enable the git release target" },
|
|
366
389
|
{ flag: "--target <names>", description: "Release only selected targets: git,npm,docker" },
|
|
367
390
|
{ flag: "--npm", description: "Enable the npm release target" },
|
|
368
|
-
{ flag: "--docker", description: "Enable the
|
|
391
|
+
{ flag: "--docker", description: "Enable the tsdocker release target" },
|
|
369
392
|
{ flag: "--no-publish", description: "Run release core and git target only" },
|
|
370
393
|
{ flag: "--plan", description: "Show resolved workflow without mutating files" },
|
|
371
394
|
],
|
|
@@ -385,7 +408,7 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
385
408
|
console.log(" -p, --push Enable the git release target");
|
|
386
409
|
console.log(" --target <names> Release only selected targets: git,npm,docker");
|
|
387
410
|
console.log(" --npm Enable the npm release target");
|
|
388
|
-
console.log(" --docker Enable the
|
|
411
|
+
console.log(" --docker Enable the tsdocker release target");
|
|
389
412
|
console.log(" --no-publish Run release core and git target only");
|
|
390
413
|
console.log(" --major|--minor|--patch Override inferred semver level");
|
|
391
414
|
console.log(" --plan Show resolved workflow without mutating files");
|
package/ts/mod_standard/index.ts
CHANGED
|
@@ -202,6 +202,7 @@ export async function showHelp(
|
|
|
202
202
|
console.log(" gitzone commit recommend --json");
|
|
203
203
|
console.log(" gitzone release --plan");
|
|
204
204
|
console.log(" gitzone format plan --json");
|
|
205
|
+
console.log(" gitzone format check");
|
|
205
206
|
console.log(" gitzone services set mongodb,minio");
|
|
206
207
|
console.log(" gitzone tools update");
|
|
207
208
|
console.log("");
|