@git.zone/cli 2.14.3 → 2.15.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.
Files changed (46) hide show
  1. package/.smartconfig.json +36 -11
  2. package/assets/templates/service/npmextra.json +8 -2
  3. package/assets/templates/smartconfig/_smartconfig.json +5 -1
  4. package/assets/templates/website/npmextra.json +8 -2
  5. package/dist_ts/00_commitinfo_data.js +1 -1
  6. package/dist_ts/gitzone.cli.js +8 -1
  7. package/dist_ts/helpers.changelog.d.ts +16 -0
  8. package/dist_ts/helpers.changelog.js +114 -0
  9. package/dist_ts/helpers.smartconfigmigrations.d.ts +7 -0
  10. package/dist_ts/helpers.smartconfigmigrations.js +161 -0
  11. package/dist_ts/helpers.workflow.d.ts +97 -0
  12. package/dist_ts/helpers.workflow.js +258 -0
  13. package/dist_ts/mod_commit/index.js +190 -303
  14. package/dist_ts/mod_commit/mod.helpers.d.ts +23 -0
  15. package/dist_ts/mod_commit/mod.helpers.js +22 -15
  16. package/dist_ts/mod_commit/mod.ui.d.ts +1 -1
  17. package/dist_ts/mod_commit/mod.ui.js +7 -2
  18. package/dist_ts/mod_config/classes.commitconfig.d.ts +6 -0
  19. package/dist_ts/mod_config/classes.commitconfig.js +28 -4
  20. package/dist_ts/mod_config/classes.releaseconfig.js +13 -7
  21. package/dist_ts/mod_config/index.js +77 -29
  22. package/dist_ts/mod_format/formatters/smartconfig.formatter.js +3 -57
  23. package/dist_ts/mod_release/index.d.ts +3 -0
  24. package/dist_ts/mod_release/index.js +299 -0
  25. package/dist_ts/mod_release/mod.plugins.d.ts +3 -0
  26. package/dist_ts/mod_release/mod.plugins.js +4 -0
  27. package/dist_ts/mod_standard/index.js +16 -3
  28. package/license +2 -2
  29. package/package.json +20 -30
  30. package/readme.hints.md +15 -17
  31. package/readme.md +239 -421
  32. package/ts/00_commitinfo_data.ts +1 -1
  33. package/ts/gitzone.cli.ts +8 -0
  34. package/ts/helpers.changelog.ts +165 -0
  35. package/ts/helpers.smartconfigmigrations.ts +192 -0
  36. package/ts/helpers.workflow.ts +387 -0
  37. package/ts/mod_commit/index.ts +233 -435
  38. package/ts/mod_commit/mod.helpers.ts +28 -16
  39. package/ts/mod_commit/mod.ui.ts +7 -2
  40. package/ts/mod_config/classes.commitconfig.ts +33 -3
  41. package/ts/mod_config/classes.releaseconfig.ts +14 -7
  42. package/ts/mod_config/index.ts +89 -28
  43. package/ts/mod_format/formatters/smartconfig.formatter.ts +2 -62
  44. package/ts/mod_release/index.ts +393 -0
  45. package/ts/mod_release/mod.plugins.ts +5 -0
  46. package/ts/mod_standard/index.ts +15 -2
@@ -0,0 +1,387 @@
1
+ import { getCliConfigValue } from "./helpers.smartconfig.js";
2
+
3
+ export type TConfirmationMode = "prompt" | "auto" | "plan";
4
+
5
+ export type TCommitStep =
6
+ | "format"
7
+ | "analyze"
8
+ | "test"
9
+ | "build"
10
+ | "changelog"
11
+ | "commit"
12
+ | "push";
13
+
14
+ export type TReleaseTarget = "git" | "npm" | "docker";
15
+
16
+ export interface ICommitWorkflowConfig {
17
+ confirmation?: TConfirmationMode;
18
+ staging?: "all";
19
+ steps?: TCommitStep[];
20
+ alwaysTest?: boolean;
21
+ alwaysBuild?: boolean;
22
+ analyze?: {
23
+ provider?: "ai";
24
+ requireConfirmationFor?: string[];
25
+ };
26
+ test?: {
27
+ command?: string;
28
+ };
29
+ build?: {
30
+ command?: string;
31
+ verifyCleanTree?: boolean;
32
+ };
33
+ push?: {
34
+ remote?: string;
35
+ followTags?: boolean;
36
+ };
37
+ }
38
+
39
+ export interface IReleaseGitTargetConfig {
40
+ enabled?: boolean;
41
+ remote?: string;
42
+ pushBranch?: boolean;
43
+ pushTags?: boolean;
44
+ }
45
+
46
+ export interface IReleaseNpmTargetConfig {
47
+ enabled?: boolean;
48
+ registries?: string[];
49
+ accessLevel?: "public" | "private";
50
+ alreadyPublished?: "success" | "error";
51
+ }
52
+
53
+ export interface IReleaseDockerTargetConfig {
54
+ enabled?: boolean;
55
+ images?: string[];
56
+ }
57
+
58
+ export interface IReleaseWorkflowConfig {
59
+ confirmation?: TConfirmationMode;
60
+ version?: {
61
+ strategy?: "semver";
62
+ source?: "pendingChangelog" | "manual";
63
+ };
64
+ preflight?: {
65
+ requireCleanTree?: boolean;
66
+ test?: boolean;
67
+ build?: boolean;
68
+ testCommand?: string;
69
+ buildCommand?: string;
70
+ };
71
+ targets?: {
72
+ git?: IReleaseGitTargetConfig;
73
+ npm?: IReleaseNpmTargetConfig;
74
+ docker?: IReleaseDockerTargetConfig;
75
+ };
76
+ }
77
+
78
+ export interface IResolvedCommitWorkflow {
79
+ confirmation: TConfirmationMode;
80
+ steps: TCommitStep[];
81
+ staging: "all";
82
+ testCommand: string;
83
+ buildCommand: string;
84
+ changelogFile: "changelog.md";
85
+ changelogSection: "Pending";
86
+ pushRemote: string;
87
+ pushFollowTags: boolean;
88
+ releaseFlagRequested: boolean;
89
+ }
90
+
91
+ export interface IResolvedReleaseWorkflow {
92
+ confirmation: TConfirmationMode;
93
+ plan: string[];
94
+ targets: TReleaseTarget[];
95
+ requireCleanTree: boolean;
96
+ runTests: boolean;
97
+ runBuild: boolean;
98
+ testCommand: string;
99
+ buildCommand: string;
100
+ changelogFile: "changelog.md";
101
+ changelogPendingSection: "Pending";
102
+ changelogVersionHeading: "## {{date}} - {{version}}";
103
+ gitEnabled: boolean;
104
+ gitRemote: string;
105
+ pushBranch: boolean;
106
+ pushTags: boolean;
107
+ npmEnabled: boolean;
108
+ npmRegistries: string[];
109
+ npmAccessLevel: "public" | "private";
110
+ npmAlreadyPublished: "success" | "error";
111
+ dockerEnabled: boolean;
112
+ dockerImages: string[];
113
+ }
114
+
115
+ interface ICliWorkflowConfig {
116
+ commit?: ICommitWorkflowConfig;
117
+ release?: IReleaseWorkflowConfig;
118
+ }
119
+
120
+ const commitFlagToStep: Record<string, TCommitStep | undefined> = {
121
+ f: "format",
122
+ t: "test",
123
+ b: "build",
124
+ p: "push",
125
+ };
126
+
127
+ const unique = <T>(items: T[]): T[] => {
128
+ const result: T[] = [];
129
+ for (const item of items) {
130
+ if (!result.includes(item)) {
131
+ result.push(item);
132
+ }
133
+ }
134
+ return result;
135
+ };
136
+
137
+ const normalizeConfirmation = (
138
+ value: unknown,
139
+ fallback: TConfirmationMode,
140
+ ): TConfirmationMode => {
141
+ if (value === "prompt" || value === "auto" || value === "plan") {
142
+ return value;
143
+ }
144
+ return fallback;
145
+ };
146
+
147
+ const normalizeRegistryUrl = (url: string): string => {
148
+ let normalizedUrl = url.trim();
149
+ if (!normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://")) {
150
+ normalizedUrl = `https://${normalizedUrl}`;
151
+ }
152
+ return normalizedUrl.endsWith("/") ? normalizedUrl.slice(0, -1) : normalizedUrl;
153
+ };
154
+
155
+ const isDisabled = (argvArg: any, ...keys: string[]): boolean => {
156
+ return keys.some((key) => argvArg[key] === false || argvArg[`no-${key}`] || argvArg[`no${key[0].toUpperCase()}${key.slice(1)}`]);
157
+ };
158
+
159
+ const readCliWorkflowConfig = async (): Promise<ICliWorkflowConfig> => {
160
+ return await getCliConfigValue<ICliWorkflowConfig>("", {});
161
+ };
162
+
163
+ const getOrderedArgsAfterCommand = (commandName: string): string[] => {
164
+ const rawArgs = process.argv.slice(2);
165
+ const commandIndex = rawArgs.indexOf(commandName);
166
+ if (commandIndex === -1) {
167
+ return rawArgs;
168
+ }
169
+ return rawArgs.slice(commandIndex + 1);
170
+ };
171
+
172
+ const getOrderedShortFlags = (commandName: string): string[] => {
173
+ const orderedFlags: string[] = [];
174
+ for (const arg of getOrderedArgsAfterCommand(commandName)) {
175
+ if (arg === "--") {
176
+ break;
177
+ }
178
+ if (arg.startsWith("--")) {
179
+ continue;
180
+ }
181
+ if (arg.startsWith("-") && arg.length > 1) {
182
+ orderedFlags.push(...arg.slice(1).split(""));
183
+ }
184
+ }
185
+ return orderedFlags;
186
+ };
187
+
188
+ const hasExplicitCommitWorkflowFlags = (argvArg: any): boolean => {
189
+ return Boolean(
190
+ argvArg.f ||
191
+ argvArg.format ||
192
+ argvArg.t ||
193
+ argvArg.test ||
194
+ argvArg.b ||
195
+ argvArg.build ||
196
+ argvArg.p ||
197
+ argvArg.push,
198
+ );
199
+ };
200
+
201
+ const normalizeCommitSteps = (rawSteps: TCommitStep[]): TCommitStep[] => {
202
+ const steps = unique(rawSteps.filter(Boolean));
203
+ const pushRequested = steps.includes("push");
204
+ const prePushSteps = steps.filter((step) => step !== "push");
205
+
206
+ if (!prePushSteps.includes("analyze")) {
207
+ prePushSteps.unshift("analyze");
208
+ }
209
+
210
+ if (!prePushSteps.includes("changelog")) {
211
+ const commitIndex = prePushSteps.indexOf("commit");
212
+ if (commitIndex === -1) {
213
+ prePushSteps.push("changelog");
214
+ } else {
215
+ prePushSteps.splice(commitIndex, 0, "changelog");
216
+ }
217
+ }
218
+
219
+ if (!prePushSteps.includes("commit")) {
220
+ prePushSteps.push("commit");
221
+ }
222
+
223
+ const analyzeIndex = prePushSteps.indexOf("analyze");
224
+ const commitIndex = prePushSteps.indexOf("commit");
225
+ if (analyzeIndex > commitIndex) {
226
+ throw new Error("Commit workflow requires analyze before commit.");
227
+ }
228
+
229
+ const changelogIndex = prePushSteps.indexOf("changelog");
230
+ if (changelogIndex === -1 || changelogIndex > commitIndex) {
231
+ throw new Error("Commit workflow requires changelog before commit.");
232
+ }
233
+
234
+ return pushRequested ? [...prePushSteps, "push"] : prePushSteps;
235
+ };
236
+
237
+ const getTargetOverride = (argvArg: any): TReleaseTarget[] | undefined => {
238
+ const validTargets: TReleaseTarget[] = ["git", "npm", "docker"];
239
+ const rawTargets = argvArg.target || argvArg.targets;
240
+ if (typeof rawTargets === "string") {
241
+ return rawTargets
242
+ .split(",")
243
+ .map((target) => target.trim())
244
+ .filter((target): target is TReleaseTarget => validTargets.includes(target as TReleaseTarget));
245
+ }
246
+
247
+ const targets: TReleaseTarget[] = [];
248
+ if (argvArg.git || argvArg.p || argvArg.push) targets.push("git");
249
+ if (argvArg.npm) targets.push("npm");
250
+ if (argvArg.docker) targets.push("docker");
251
+ return targets.length > 0 ? targets : undefined;
252
+ };
253
+
254
+ const buildReleasePlan = (options: {
255
+ requireCleanTree: boolean;
256
+ runTests: boolean;
257
+ runBuild: boolean;
258
+ targets: TReleaseTarget[];
259
+ }): string[] => {
260
+ const plan: string[] = [];
261
+ if (options.requireCleanTree) plan.push("preflight.cleanTree");
262
+ if (options.runTests) plan.push("preflight.test");
263
+ plan.push("core.version", "core.changelog", "core.commit", "core.tag");
264
+ if (options.runBuild) plan.push("core.build");
265
+ for (const target of options.targets) {
266
+ plan.push(`target.${target}`);
267
+ }
268
+ return plan;
269
+ };
270
+
271
+ export const resolveCommitWorkflow = async (argvArg: any): Promise<IResolvedCommitWorkflow> => {
272
+ const cliConfig = await readCliWorkflowConfig();
273
+ const commitConfig = cliConfig.commit || {};
274
+ const releaseFlagRequested = Boolean(argvArg.r || argvArg.release);
275
+
276
+ let confirmation = normalizeConfirmation(commitConfig.confirmation, "prompt");
277
+ if (argvArg.plan) {
278
+ confirmation = "plan";
279
+ } else if (argvArg.y || argvArg.yes) {
280
+ confirmation = "auto";
281
+ }
282
+
283
+ let rawSteps: TCommitStep[];
284
+ if (hasExplicitCommitWorkflowFlags(argvArg)) {
285
+ const orderedFlags = getOrderedShortFlags("commit");
286
+ rawSteps = ["analyze"];
287
+ for (const shortFlag of orderedFlags) {
288
+ const step = commitFlagToStep[shortFlag];
289
+ if (step) {
290
+ rawSteps.push(step);
291
+ }
292
+ }
293
+ if (argvArg.format && !rawSteps.includes("format")) rawSteps.push("format");
294
+ if (argvArg.test && !rawSteps.includes("test")) rawSteps.push("test");
295
+ if (argvArg.build && !rawSteps.includes("build")) rawSteps.push("build");
296
+ if (argvArg.push && !rawSteps.includes("push")) rawSteps.push("push");
297
+ rawSteps.push("changelog");
298
+ rawSteps.push("commit");
299
+ } else if (Array.isArray(commitConfig.steps) && commitConfig.steps.length > 0) {
300
+ rawSteps = commitConfig.steps;
301
+ } else {
302
+ rawSteps = ["analyze"];
303
+ if (commitConfig.alwaysTest) rawSteps.push("test");
304
+ if (commitConfig.alwaysBuild) rawSteps.push("build");
305
+ rawSteps.push("changelog");
306
+ rawSteps.push("commit");
307
+ }
308
+
309
+ return {
310
+ confirmation,
311
+ steps: normalizeCommitSteps(rawSteps),
312
+ staging: commitConfig.staging || "all",
313
+ testCommand: commitConfig.test?.command || "pnpm test",
314
+ buildCommand: commitConfig.build?.command || "pnpm build",
315
+ changelogFile: "changelog.md",
316
+ changelogSection: "Pending",
317
+ pushRemote: commitConfig.push?.remote || "origin",
318
+ pushFollowTags: commitConfig.push?.followTags || false,
319
+ releaseFlagRequested,
320
+ };
321
+ };
322
+
323
+ export const resolveReleaseWorkflow = async (argvArg: any): Promise<IResolvedReleaseWorkflow> => {
324
+ const cliConfig = await readCliWorkflowConfig();
325
+ const releaseConfig = cliConfig.release || {};
326
+ const targetConfig = releaseConfig.targets || {};
327
+ const gitConfig = targetConfig.git || {};
328
+ const npmConfig = targetConfig.npm || {};
329
+ const dockerConfig = targetConfig.docker || {};
330
+ const npmRegistries = (npmConfig.registries || []).map(normalizeRegistryUrl);
331
+ const npmEnabled = npmConfig.enabled ?? npmRegistries.length > 0;
332
+ const gitEnabled = gitConfig.enabled ?? true;
333
+ const dockerEnabled = dockerConfig.enabled ?? false;
334
+
335
+ let confirmation = normalizeConfirmation(releaseConfig.confirmation, "prompt");
336
+ if (argvArg.plan) {
337
+ confirmation = "plan";
338
+ } else if (argvArg.y || argvArg.yes) {
339
+ confirmation = "auto";
340
+ }
341
+
342
+ let requireCleanTree = releaseConfig.preflight?.requireCleanTree ?? true;
343
+ let runTests = releaseConfig.preflight?.test ?? false;
344
+ let runBuild = releaseConfig.preflight?.build ?? true;
345
+ if (argvArg.t || argvArg.test) runTests = true;
346
+ if (argvArg.b || argvArg.build) runBuild = true;
347
+ if (isDisabled(argvArg, "test")) runTests = false;
348
+ if (isDisabled(argvArg, "build")) runBuild = false;
349
+ if (isDisabled(argvArg, "preflight")) requireCleanTree = false;
350
+
351
+ const configuredTargets: TReleaseTarget[] = [];
352
+ if (gitEnabled) configuredTargets.push("git");
353
+ if (npmEnabled) configuredTargets.push("npm");
354
+ if (dockerEnabled) configuredTargets.push("docker");
355
+ let targets = getTargetOverride(argvArg) || configuredTargets;
356
+ if (isDisabled(argvArg, "git", "push")) {
357
+ targets = targets.filter((target) => target !== "git");
358
+ }
359
+ if (isDisabled(argvArg, "publish")) {
360
+ targets = targets.filter((target) => target === "git");
361
+ }
362
+ targets = unique(targets);
363
+
364
+ return {
365
+ confirmation,
366
+ plan: buildReleasePlan({ requireCleanTree, runTests, runBuild, targets }),
367
+ targets,
368
+ requireCleanTree,
369
+ runTests,
370
+ runBuild,
371
+ testCommand: releaseConfig.preflight?.testCommand || "pnpm test",
372
+ buildCommand: releaseConfig.preflight?.buildCommand || "pnpm build",
373
+ changelogFile: "changelog.md",
374
+ changelogPendingSection: "Pending",
375
+ changelogVersionHeading: "## {{date}} - {{version}}",
376
+ gitEnabled,
377
+ gitRemote: gitConfig.remote || "origin",
378
+ pushBranch: gitConfig.pushBranch ?? true,
379
+ pushTags: gitConfig.pushTags ?? true,
380
+ npmEnabled,
381
+ npmRegistries,
382
+ npmAccessLevel: npmConfig.accessLevel || "public",
383
+ npmAlreadyPublished: npmConfig.alreadyPublished || "success",
384
+ dockerEnabled,
385
+ dockerImages: dockerConfig.images || [],
386
+ };
387
+ };