@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.
- package/.smartconfig.json +36 -11
- package/assets/templates/service/npmextra.json +8 -2
- package/assets/templates/smartconfig/_smartconfig.json +5 -1
- package/assets/templates/website/npmextra.json +8 -2
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/gitzone.cli.js +8 -1
- package/dist_ts/helpers.changelog.d.ts +16 -0
- package/dist_ts/helpers.changelog.js +114 -0
- package/dist_ts/helpers.smartconfigmigrations.d.ts +7 -0
- package/dist_ts/helpers.smartconfigmigrations.js +161 -0
- package/dist_ts/helpers.workflow.d.ts +97 -0
- package/dist_ts/helpers.workflow.js +258 -0
- package/dist_ts/mod_commit/index.js +190 -303
- package/dist_ts/mod_commit/mod.helpers.d.ts +23 -0
- package/dist_ts/mod_commit/mod.helpers.js +22 -15
- package/dist_ts/mod_commit/mod.ui.d.ts +1 -1
- package/dist_ts/mod_commit/mod.ui.js +7 -2
- package/dist_ts/mod_config/classes.commitconfig.d.ts +6 -0
- package/dist_ts/mod_config/classes.commitconfig.js +28 -4
- package/dist_ts/mod_config/classes.releaseconfig.js +13 -7
- package/dist_ts/mod_config/index.js +77 -29
- package/dist_ts/mod_format/formatters/smartconfig.formatter.js +3 -57
- package/dist_ts/mod_release/index.d.ts +3 -0
- package/dist_ts/mod_release/index.js +299 -0
- package/dist_ts/mod_release/mod.plugins.d.ts +3 -0
- package/dist_ts/mod_release/mod.plugins.js +4 -0
- package/dist_ts/mod_standard/index.js +16 -3
- package/license +2 -2
- package/package.json +20 -30
- package/readme.hints.md +15 -17
- package/readme.md +239 -421
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/gitzone.cli.ts +8 -0
- package/ts/helpers.changelog.ts +165 -0
- package/ts/helpers.smartconfigmigrations.ts +192 -0
- package/ts/helpers.workflow.ts +387 -0
- package/ts/mod_commit/index.ts +233 -435
- package/ts/mod_commit/mod.helpers.ts +28 -16
- package/ts/mod_commit/mod.ui.ts +7 -2
- package/ts/mod_config/classes.commitconfig.ts +33 -3
- package/ts/mod_config/classes.releaseconfig.ts +14 -7
- package/ts/mod_config/index.ts +89 -28
- package/ts/mod_format/formatters/smartconfig.formatter.ts +2 -62
- package/ts/mod_release/index.ts +393 -0
- package/ts/mod_release/mod.plugins.ts +5 -0
- package/ts/mod_standard/index.ts +15 -2
package/ts/mod_commit/index.ts
CHANGED
|
@@ -3,15 +3,11 @@
|
|
|
3
3
|
import * as plugins from "./mod.plugins.js";
|
|
4
4
|
import * as paths from "../paths.js";
|
|
5
5
|
import { logger } from "../gitzone.logging.js";
|
|
6
|
-
import * as helpers from "./mod.helpers.js";
|
|
7
6
|
import * as ui from "./mod.ui.js";
|
|
8
|
-
import { ReleaseConfig } from "../mod_config/classes.releaseconfig.js";
|
|
9
7
|
import type { ICliMode } from "../helpers.climode.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
runWithSuppressedOutput,
|
|
14
|
-
} from "../helpers.climode.js";
|
|
8
|
+
import { getCliMode, printJson, runWithSuppressedOutput } from "../helpers.climode.js";
|
|
9
|
+
import { appendPendingChangelogEntry } from "../helpers.changelog.js";
|
|
10
|
+
import { resolveCommitWorkflow, type IResolvedCommitWorkflow } from "../helpers.workflow.js";
|
|
15
11
|
|
|
16
12
|
export const run = async (argvArg: any) => {
|
|
17
13
|
const mode = await getCliMode(argvArg);
|
|
@@ -36,430 +32,246 @@ export const run = async (argvArg: any) => {
|
|
|
36
32
|
return;
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
}>("@git.zone/cli", {});
|
|
47
|
-
const commitConfig = gitzoneConfig.commit || {};
|
|
48
|
-
|
|
49
|
-
// Check flags and merge with config options
|
|
50
|
-
const wantsRelease = !!(argvArg.r || argvArg.release);
|
|
51
|
-
const wantsTest = !!(argvArg.t || argvArg.test || commitConfig.alwaysTest);
|
|
52
|
-
const wantsBuild = !!(argvArg.b || argvArg.build || commitConfig.alwaysBuild);
|
|
53
|
-
let releaseConfig: ReleaseConfig | null = null;
|
|
54
|
-
|
|
55
|
-
if (wantsRelease) {
|
|
56
|
-
releaseConfig = await ReleaseConfig.fromCwd();
|
|
57
|
-
if (!releaseConfig.hasRegistries()) {
|
|
58
|
-
logger.log("error", "No release registries configured.");
|
|
59
|
-
console.log("");
|
|
60
|
-
console.log(
|
|
61
|
-
" Run `gitzone config add <registry-url>` to add registries.",
|
|
62
|
-
);
|
|
63
|
-
console.log("");
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
35
|
+
const workflow = await resolveCommitWorkflow(argvArg);
|
|
36
|
+
if (workflow.releaseFlagRequested) {
|
|
37
|
+
logger.log(
|
|
38
|
+
"warn",
|
|
39
|
+
"`gitzone commit -r` is deprecated and no longer releases. Use `gitzone release` after committing.",
|
|
40
|
+
);
|
|
66
41
|
}
|
|
67
42
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
push: !!(argvArg.p || argvArg.push),
|
|
72
|
-
test: wantsTest,
|
|
73
|
-
build: wantsBuild,
|
|
74
|
-
release: wantsRelease,
|
|
75
|
-
format: !!argvArg.format,
|
|
76
|
-
registries: releaseConfig?.getRegistries(),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (argvArg.format) {
|
|
80
|
-
const formatMod = await import("../mod_format/index.js");
|
|
81
|
-
await formatMod.run();
|
|
43
|
+
printCommitExecutionPlan(workflow);
|
|
44
|
+
if (workflow.confirmation === "plan") {
|
|
45
|
+
return;
|
|
82
46
|
}
|
|
83
47
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
48
|
+
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
49
|
+
executor: "bash",
|
|
50
|
+
sourceFilePaths: [],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let nextCommitObject: any;
|
|
54
|
+
let answerBucket: plugins.smartinteract.AnswerBucket | undefined;
|
|
55
|
+
|
|
56
|
+
for (const step of workflow.steps) {
|
|
57
|
+
switch (step) {
|
|
58
|
+
case "format":
|
|
59
|
+
await runFormatStep();
|
|
60
|
+
break;
|
|
61
|
+
case "test":
|
|
62
|
+
await runCommandStep(smartshellInstance, "Running tests", workflow.testCommand);
|
|
63
|
+
break;
|
|
64
|
+
case "build":
|
|
65
|
+
await runCommandStep(smartshellInstance, "Running build", workflow.buildCommand);
|
|
66
|
+
break;
|
|
67
|
+
case "analyze":
|
|
68
|
+
nextCommitObject = await runAnalyzeStep();
|
|
69
|
+
answerBucket = await buildAnswerBucket(nextCommitObject, workflow, mode, argvArg);
|
|
70
|
+
break;
|
|
71
|
+
case "changelog":
|
|
72
|
+
assertAnalysisComplete(answerBucket, nextCommitObject);
|
|
73
|
+
await runChangelogStep(workflow, answerBucket!, nextCommitObject);
|
|
74
|
+
break;
|
|
75
|
+
case "commit":
|
|
76
|
+
assertAnalysisComplete(answerBucket, nextCommitObject);
|
|
77
|
+
await runCommitStep(smartshellInstance, answerBucket!);
|
|
78
|
+
break;
|
|
79
|
+
case "push":
|
|
80
|
+
await runPushStep(smartshellInstance, workflow);
|
|
81
|
+
break;
|
|
95
82
|
}
|
|
96
|
-
logger.log("success", "All tests passed.");
|
|
97
83
|
}
|
|
98
84
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
const commitShaResult = await smartshellInstance.exec("git rev-parse --short HEAD");
|
|
86
|
+
const currentBranch = await detectCurrentBranch(smartshellInstance);
|
|
87
|
+
ui.printSummary({
|
|
88
|
+
projectType: "source",
|
|
89
|
+
branch: currentBranch,
|
|
90
|
+
commitType: answerBucket!.getAnswerFor("commitType"),
|
|
91
|
+
commitScope: answerBucket!.getAnswerFor("commitScope"),
|
|
92
|
+
commitMessage: answerBucket!.getAnswerFor("commitDescription"),
|
|
93
|
+
commitSha: commitShaResult.stdout.trim(),
|
|
94
|
+
pushed: workflow.steps.includes("push"),
|
|
95
|
+
});
|
|
96
|
+
};
|
|
105
97
|
|
|
106
|
-
|
|
98
|
+
async function runFormatStep(): Promise<void> {
|
|
99
|
+
ui.printHeader("Formatting project files");
|
|
100
|
+
const formatMod = await import("../mod_format/index.js");
|
|
101
|
+
await formatMod.run({ write: true, yes: true, interactive: false });
|
|
102
|
+
}
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
async function runCommandStep(
|
|
105
|
+
smartshellInstance: plugins.smartshell.Smartshell,
|
|
106
|
+
label: string,
|
|
107
|
+
command: string,
|
|
108
|
+
): Promise<void> {
|
|
109
|
+
ui.printHeader(label);
|
|
110
|
+
const result = await smartshellInstance.exec(command);
|
|
111
|
+
if (result.exitCode !== 0) {
|
|
112
|
+
logger.log("error", `${label} failed. Aborting commit.`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
logger.log("success", `${label} passed.`);
|
|
116
|
+
}
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
async function runAnalyzeStep(): Promise<any> {
|
|
119
|
+
ui.printHeader("Analyzing repository changes");
|
|
120
|
+
const aidoc = new plugins.tsdoc.AiDoc();
|
|
121
|
+
await aidoc.start();
|
|
122
|
+
try {
|
|
123
|
+
const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd);
|
|
124
|
+
ui.printRecommendation({
|
|
125
|
+
recommendedNextVersion: nextCommitObject.recommendedNextVersion,
|
|
126
|
+
recommendedNextVersionLevel: nextCommitObject.recommendedNextVersionLevel,
|
|
127
|
+
recommendedNextVersionScope: nextCommitObject.recommendedNextVersionScope,
|
|
128
|
+
recommendedNextVersionMessage: nextCommitObject.recommendedNextVersionMessage,
|
|
129
|
+
});
|
|
130
|
+
return nextCommitObject;
|
|
131
|
+
} finally {
|
|
132
|
+
await aidoc.stop();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
117
135
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
async function buildAnswerBucket(
|
|
137
|
+
nextCommitObject: any,
|
|
138
|
+
workflow: IResolvedCommitWorkflow,
|
|
139
|
+
mode: ICliMode,
|
|
140
|
+
argvArg: any,
|
|
141
|
+
): Promise<plugins.smartinteract.AnswerBucket> {
|
|
142
|
+
const isBreakingChange = nextCommitObject.recommendedNextVersionLevel === "BREAKING CHANGE";
|
|
143
|
+
const canAutoAccept = workflow.confirmation === "auto" && !isBreakingChange;
|
|
123
144
|
|
|
124
145
|
if (canAutoAccept) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
name: "commitType",
|
|
131
|
-
value: nextCommitObject.recommendedNextVersionLevel,
|
|
132
|
-
});
|
|
133
|
-
answerBucket.addAnswer({
|
|
134
|
-
name: "commitScope",
|
|
135
|
-
value: nextCommitObject.recommendedNextVersionScope,
|
|
136
|
-
});
|
|
137
|
-
answerBucket.addAnswer({
|
|
138
|
-
name: "commitDescription",
|
|
139
|
-
value: nextCommitObject.recommendedNextVersionMessage,
|
|
140
|
-
});
|
|
141
|
-
answerBucket.addAnswer({
|
|
142
|
-
name: "pushToOrigin",
|
|
143
|
-
value: !!(argvArg.p || argvArg.push), // Only push if -p flag also provided
|
|
146
|
+
logger.log("info", "Auto-accepting AI recommendations");
|
|
147
|
+
return createAnswerBucket({
|
|
148
|
+
commitType: nextCommitObject.recommendedNextVersionLevel,
|
|
149
|
+
commitScope: nextCommitObject.recommendedNextVersionScope,
|
|
150
|
+
commitDescription: nextCommitObject.recommendedNextVersionMessage,
|
|
144
151
|
});
|
|
145
|
-
answerBucket.addAnswer({
|
|
146
|
-
name: "createRelease",
|
|
147
|
-
value: wantsRelease,
|
|
148
|
-
});
|
|
149
|
-
} else {
|
|
150
|
-
// Warn if --yes was provided but we're requiring confirmation due to breaking change
|
|
151
|
-
if (isBreakingChange && (argvArg.y || argvArg.yes)) {
|
|
152
|
-
logger.log(
|
|
153
|
-
"warn",
|
|
154
|
-
"⚠️ BREAKING CHANGE detected - manual confirmation required",
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
// Interactive mode: prompt user for input
|
|
158
|
-
const commitInteract = new plugins.smartinteract.SmartInteract();
|
|
159
|
-
commitInteract.addQuestions([
|
|
160
|
-
{
|
|
161
|
-
type: "list",
|
|
162
|
-
name: `commitType`,
|
|
163
|
-
message: `Choose TYPE of the commit:`,
|
|
164
|
-
choices: [`fix`, `feat`, `BREAKING CHANGE`],
|
|
165
|
-
default: nextCommitObject.recommendedNextVersionLevel,
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
type: "input",
|
|
169
|
-
name: `commitScope`,
|
|
170
|
-
message: `What is the SCOPE of the commit:`,
|
|
171
|
-
default: nextCommitObject.recommendedNextVersionScope,
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
type: `input`,
|
|
175
|
-
name: `commitDescription`,
|
|
176
|
-
message: `What is the DESCRIPTION of the commit?`,
|
|
177
|
-
default: nextCommitObject.recommendedNextVersionMessage,
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
type: "confirm",
|
|
181
|
-
name: `pushToOrigin`,
|
|
182
|
-
message: `Do you want to push this version now?`,
|
|
183
|
-
default: true,
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
type: "confirm",
|
|
187
|
-
name: `createRelease`,
|
|
188
|
-
message: `Do you want to publish to npm registries?`,
|
|
189
|
-
default: wantsRelease,
|
|
190
|
-
},
|
|
191
|
-
]);
|
|
192
|
-
answerBucket = await commitInteract.runQueue();
|
|
193
|
-
}
|
|
194
|
-
const commitString = createCommitStringFromAnswerBucket(answerBucket);
|
|
195
|
-
const commitType = answerBucket.getAnswerFor("commitType");
|
|
196
|
-
let commitVersionType: helpers.VersionType;
|
|
197
|
-
switch (commitType) {
|
|
198
|
-
case "fix":
|
|
199
|
-
commitVersionType = "patch";
|
|
200
|
-
break;
|
|
201
|
-
case "feat":
|
|
202
|
-
commitVersionType = "minor";
|
|
203
|
-
break;
|
|
204
|
-
case "BREAKING CHANGE":
|
|
205
|
-
commitVersionType = "major";
|
|
206
|
-
break;
|
|
207
|
-
default:
|
|
208
|
-
throw new Error(`Unsupported commit type: ${commitType}`);
|
|
209
152
|
}
|
|
210
153
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
executor: "bash",
|
|
215
|
-
sourceFilePaths: [],
|
|
216
|
-
});
|
|
154
|
+
if (isBreakingChange && (workflow.confirmation === "auto" || argvArg.y || argvArg.yes)) {
|
|
155
|
+
logger.log("warn", "BREAKING CHANGE detected - manual confirmation required");
|
|
156
|
+
}
|
|
217
157
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
releaseConfig = await ReleaseConfig.fromCwd();
|
|
221
|
-
if (!releaseConfig.hasRegistries()) {
|
|
222
|
-
logger.log("error", "No release registries configured.");
|
|
223
|
-
console.log("");
|
|
224
|
-
console.log(
|
|
225
|
-
" Run `gitzone config add <registry-url>` to add registries.",
|
|
226
|
-
);
|
|
227
|
-
console.log("");
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
158
|
+
if (!mode.interactive) {
|
|
159
|
+
throw new Error("Commit confirmation requires an interactive terminal. Use `-y` or set commit.confirmation to `auto`.");
|
|
230
160
|
}
|
|
231
161
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
);
|
|
257
|
-
await commitInfo.writeIntoPotentialDirs();
|
|
258
|
-
ui.printStep(
|
|
259
|
-
currentStep,
|
|
260
|
-
totalSteps,
|
|
261
|
-
"🔧 Baking commit info into code",
|
|
262
|
-
"done",
|
|
263
|
-
);
|
|
162
|
+
const commitInteract = new plugins.smartinteract.SmartInteract();
|
|
163
|
+
commitInteract.addQuestions([
|
|
164
|
+
{
|
|
165
|
+
type: "list",
|
|
166
|
+
name: "commitType",
|
|
167
|
+
message: "Choose TYPE of the commit:",
|
|
168
|
+
choices: ["fix", "feat", "BREAKING CHANGE"],
|
|
169
|
+
default: nextCommitObject.recommendedNextVersionLevel,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: "input",
|
|
173
|
+
name: "commitScope",
|
|
174
|
+
message: "What is the SCOPE of the commit:",
|
|
175
|
+
default: nextCommitObject.recommendedNextVersionScope,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: "input",
|
|
179
|
+
name: "commitDescription",
|
|
180
|
+
message: "What is the DESCRIPTION of the commit?",
|
|
181
|
+
default: nextCommitObject.recommendedNextVersionMessage,
|
|
182
|
+
},
|
|
183
|
+
]);
|
|
184
|
+
return await commitInteract.runQueue();
|
|
185
|
+
}
|
|
264
186
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let changelog = nextCommitObject.changelog || "# Changelog\n";
|
|
274
|
-
changelog = changelog.replaceAll(
|
|
275
|
-
"{{nextVersion}}",
|
|
276
|
-
(await commitInfo.getNextPlannedVersion()).versionString,
|
|
277
|
-
);
|
|
278
|
-
changelog = changelog.replaceAll(
|
|
279
|
-
"{{nextVersionScope}}",
|
|
280
|
-
`${await answerBucket.getAnswerFor("commitType")}(${await answerBucket.getAnswerFor("commitScope")})`,
|
|
281
|
-
);
|
|
282
|
-
changelog = changelog.replaceAll(
|
|
283
|
-
"{{nextVersionMessage}}",
|
|
284
|
-
nextCommitObject.recommendedNextVersionMessage,
|
|
285
|
-
);
|
|
286
|
-
if (nextCommitObject.recommendedNextVersionDetails?.length > 0) {
|
|
287
|
-
changelog = changelog.replaceAll(
|
|
288
|
-
"{{nextVersionDetails}}",
|
|
289
|
-
"- " + nextCommitObject.recommendedNextVersionDetails.join("\n- "),
|
|
290
|
-
);
|
|
291
|
-
} else {
|
|
292
|
-
changelog = changelog.replaceAll("\n{{nextVersionDetails}}", "");
|
|
187
|
+
function createAnswerBucket(answers: {
|
|
188
|
+
commitType: string;
|
|
189
|
+
commitScope: string;
|
|
190
|
+
commitDescription: string;
|
|
191
|
+
}): plugins.smartinteract.AnswerBucket {
|
|
192
|
+
const answerBucket = new plugins.smartinteract.AnswerBucket();
|
|
193
|
+
for (const [name, value] of Object.entries(answers)) {
|
|
194
|
+
answerBucket.addAnswer({ name, value });
|
|
293
195
|
}
|
|
196
|
+
return answerBucket;
|
|
197
|
+
}
|
|
294
198
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
ui.printStep(
|
|
310
|
-
currentStep,
|
|
311
|
-
totalSteps,
|
|
312
|
-
"💾 Creating git commit",
|
|
313
|
-
"in-progress",
|
|
314
|
-
);
|
|
315
|
-
await smartshellInstance.exec(`git commit -m "${commitString}"`);
|
|
316
|
-
ui.printStep(currentStep, totalSteps, "💾 Creating git commit", "done");
|
|
317
|
-
|
|
318
|
-
// Step 5: Bumping version
|
|
319
|
-
currentStep++;
|
|
320
|
-
const projectType = await helpers.detectProjectType();
|
|
321
|
-
const newVersion = await helpers.bumpProjectVersion(
|
|
322
|
-
projectType,
|
|
323
|
-
commitVersionType,
|
|
324
|
-
currentStep,
|
|
325
|
-
totalSteps,
|
|
199
|
+
async function runChangelogStep(
|
|
200
|
+
workflow: IResolvedCommitWorkflow,
|
|
201
|
+
answerBucket: plugins.smartinteract.AnswerBucket,
|
|
202
|
+
nextCommitObject: any,
|
|
203
|
+
): Promise<void> {
|
|
204
|
+
await appendPendingChangelogEntry(
|
|
205
|
+
plugins.path.join(paths.cwd, workflow.changelogFile),
|
|
206
|
+
workflow.changelogSection,
|
|
207
|
+
{
|
|
208
|
+
type: answerBucket.getAnswerFor("commitType"),
|
|
209
|
+
scope: answerBucket.getAnswerFor("commitScope"),
|
|
210
|
+
message: answerBucket.getAnswerFor("commitDescription"),
|
|
211
|
+
details: nextCommitObject.recommendedNextVersionDetails || [],
|
|
212
|
+
},
|
|
326
213
|
);
|
|
214
|
+
logger.log("success", `Updated ${workflow.changelogFile} pending section.`);
|
|
215
|
+
}
|
|
327
216
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
// Step 7: Verify no uncommitted changes
|
|
341
|
-
currentStep++;
|
|
342
|
-
ui.printStep(
|
|
343
|
-
currentStep,
|
|
344
|
-
totalSteps,
|
|
345
|
-
"🔍 Verifying clean working tree",
|
|
346
|
-
"in-progress",
|
|
347
|
-
);
|
|
348
|
-
const statusResult = await smartshellInstance.exec(
|
|
349
|
-
"git status --porcelain",
|
|
350
|
-
);
|
|
351
|
-
if (statusResult.stdout.trim() !== "") {
|
|
352
|
-
ui.printStep(
|
|
353
|
-
currentStep,
|
|
354
|
-
totalSteps,
|
|
355
|
-
"🔍 Verifying clean working tree",
|
|
356
|
-
"error",
|
|
357
|
-
);
|
|
358
|
-
logger.log(
|
|
359
|
-
"error",
|
|
360
|
-
"Build produced uncommitted changes. This usually means build output is not gitignored.",
|
|
361
|
-
);
|
|
362
|
-
logger.log("error", "Uncommitted files:");
|
|
363
|
-
console.log(statusResult.stdout);
|
|
364
|
-
logger.log(
|
|
365
|
-
"error",
|
|
366
|
-
"Aborting release. Please ensure build artifacts are in .gitignore",
|
|
367
|
-
);
|
|
368
|
-
process.exit(1);
|
|
369
|
-
}
|
|
370
|
-
ui.printStep(
|
|
371
|
-
currentStep,
|
|
372
|
-
totalSteps,
|
|
373
|
-
"🔍 Verifying clean working tree",
|
|
374
|
-
"done",
|
|
375
|
-
);
|
|
217
|
+
async function runCommitStep(
|
|
218
|
+
smartshellInstance: plugins.smartshell.Smartshell,
|
|
219
|
+
answerBucket: plugins.smartinteract.AnswerBucket,
|
|
220
|
+
): Promise<void> {
|
|
221
|
+
ui.printHeader("Creating Semantic Commit");
|
|
222
|
+
const commitString = createCommitStringFromAnswerBucket(answerBucket);
|
|
223
|
+
ui.printCommitMessage(commitString);
|
|
224
|
+
await smartshellInstance.exec("git add -A");
|
|
225
|
+
const result = await smartshellInstance.exec(`git commit -m ${shellQuote(commitString)}`);
|
|
226
|
+
if (result.exitCode !== 0) {
|
|
227
|
+
logger.log("error", "git commit failed.");
|
|
228
|
+
process.exit(1);
|
|
376
229
|
}
|
|
230
|
+
}
|
|
377
231
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
);
|
|
391
|
-
ui.printStep(
|
|
392
|
-
currentStep,
|
|
393
|
-
totalSteps,
|
|
394
|
-
`🚀 Pushing to origin/${currentBranch}`,
|
|
395
|
-
"done",
|
|
396
|
-
);
|
|
232
|
+
async function runPushStep(
|
|
233
|
+
smartshellInstance: plugins.smartshell.Smartshell,
|
|
234
|
+
workflow: IResolvedCommitWorkflow,
|
|
235
|
+
): Promise<void> {
|
|
236
|
+
const currentBranch = await detectCurrentBranch(smartshellInstance);
|
|
237
|
+
const followTags = workflow.pushFollowTags ? " --follow-tags" : "";
|
|
238
|
+
const result = await smartshellInstance.exec(
|
|
239
|
+
`git push ${workflow.pushRemote} ${currentBranch}${followTags}`,
|
|
240
|
+
);
|
|
241
|
+
if (result.exitCode !== 0) {
|
|
242
|
+
logger.log("error", "git push failed.");
|
|
243
|
+
process.exit(1);
|
|
397
244
|
}
|
|
245
|
+
}
|
|
398
246
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
currentStep,
|
|
406
|
-
totalSteps,
|
|
407
|
-
`📦 Publishing to ${registries.length} registr${registries.length === 1 ? "y" : "ies"}`,
|
|
408
|
-
"in-progress",
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
const accessLevel = releaseConfig.getAccessLevel();
|
|
412
|
-
for (const registry of registries) {
|
|
413
|
-
try {
|
|
414
|
-
await smartshellInstance.exec(
|
|
415
|
-
`npm publish --registry=${registry} --access=${accessLevel}`,
|
|
416
|
-
);
|
|
417
|
-
releasedRegistries.push(registry);
|
|
418
|
-
} catch (error) {
|
|
419
|
-
logger.log("error", `Failed to publish to ${registry}: ${error}`);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
247
|
+
async function detectCurrentBranch(
|
|
248
|
+
smartshellInstance: plugins.smartshell.Smartshell,
|
|
249
|
+
): Promise<string> {
|
|
250
|
+
const branchResult = await smartshellInstance.exec("git branch --show-current");
|
|
251
|
+
return branchResult.stdout.trim() || "master";
|
|
252
|
+
}
|
|
422
253
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
);
|
|
430
|
-
} else {
|
|
431
|
-
ui.printStep(
|
|
432
|
-
currentStep,
|
|
433
|
-
totalSteps,
|
|
434
|
-
`📦 Publishing to ${registries.length} registr${registries.length === 1 ? "y" : "ies"}`,
|
|
435
|
-
"error",
|
|
436
|
-
);
|
|
437
|
-
}
|
|
254
|
+
function assertAnalysisComplete(
|
|
255
|
+
answerBucket: plugins.smartinteract.AnswerBucket | undefined,
|
|
256
|
+
nextCommitObject: any,
|
|
257
|
+
): void {
|
|
258
|
+
if (!answerBucket || !nextCommitObject) {
|
|
259
|
+
throw new Error("Commit workflow requires analyze before changelog and commit steps.");
|
|
438
260
|
}
|
|
261
|
+
}
|
|
439
262
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const commitShaResult = await smartshellInstance.exec(
|
|
444
|
-
"git rev-parse --short HEAD",
|
|
445
|
-
);
|
|
446
|
-
const commitSha = commitShaResult.stdout.trim();
|
|
263
|
+
function shellQuote(value: string): string {
|
|
264
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
265
|
+
}
|
|
447
266
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
commitSha: commitSha,
|
|
457
|
-
pushed: willPush,
|
|
458
|
-
released: releasedRegistries.length > 0,
|
|
459
|
-
releasedRegistries:
|
|
460
|
-
releasedRegistries.length > 0 ? releasedRegistries : undefined,
|
|
461
|
-
});
|
|
462
|
-
};
|
|
267
|
+
function printCommitExecutionPlan(workflow: IResolvedCommitWorkflow): void {
|
|
268
|
+
console.log("");
|
|
269
|
+
console.log("gitzone commit - resolved workflow");
|
|
270
|
+
console.log(`confirmation: ${workflow.confirmation}`);
|
|
271
|
+
console.log(`steps: ${workflow.steps.join(" -> ")}`);
|
|
272
|
+
console.log(`changelog: ${workflow.changelogFile}#${workflow.changelogSection}`);
|
|
273
|
+
console.log("");
|
|
274
|
+
}
|
|
463
275
|
|
|
464
276
|
async function handleRecommend(mode: ICliMode): Promise<void> {
|
|
465
277
|
const recommendationBuilder = async () => {
|
|
@@ -507,40 +319,27 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
507
319
|
printJson({
|
|
508
320
|
command: "commit",
|
|
509
321
|
usage: "gitzone commit [recommend] [options]",
|
|
510
|
-
description:
|
|
511
|
-
"Creates semantic commits or emits a read-only recommendation.",
|
|
322
|
+
description: "Analyzes changes and creates one semantic source commit.",
|
|
512
323
|
commands: [
|
|
513
324
|
{
|
|
514
325
|
name: "recommend",
|
|
515
|
-
description:
|
|
516
|
-
"Generate a commit recommendation without mutating the repository",
|
|
326
|
+
description: "Generate a commit recommendation without mutating the repository",
|
|
517
327
|
},
|
|
518
328
|
],
|
|
519
329
|
flags: [
|
|
520
|
-
{ flag: "-y, --yes", description: "Auto-accept AI recommendations" },
|
|
521
|
-
{ flag: "-p, --push", description: "Push to origin after
|
|
522
|
-
{ flag: "-t, --test", description: "Run tests
|
|
523
|
-
{
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
},
|
|
527
|
-
{
|
|
528
|
-
flag: "-r, --release",
|
|
529
|
-
description: "Publish to configured registries after push",
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
flag: "--format",
|
|
533
|
-
description: "Run gitzone format before committing",
|
|
534
|
-
},
|
|
535
|
-
{
|
|
536
|
-
flag: "--json",
|
|
537
|
-
description: "Emit JSON for `commit recommend` only",
|
|
538
|
-
},
|
|
330
|
+
{ flag: "-y, --yes", description: "Auto-accept safe AI recommendations" },
|
|
331
|
+
{ flag: "-p, --push", description: "Push to origin after committing" },
|
|
332
|
+
{ flag: "-t, --test", description: "Run tests as part of the commit workflow" },
|
|
333
|
+
{ flag: "-b, --build", description: "Run build as part of the commit workflow" },
|
|
334
|
+
{ flag: "-f, --format", description: "Run gitzone format before committing" },
|
|
335
|
+
{ flag: "--plan", description: "Show resolved workflow without mutating files" },
|
|
336
|
+
{ flag: "--json", description: "Emit JSON for `commit recommend` only" },
|
|
539
337
|
],
|
|
540
338
|
examples: [
|
|
541
339
|
"gitzone commit recommend --json",
|
|
542
340
|
"gitzone commit -y",
|
|
543
|
-
"gitzone commit -
|
|
341
|
+
"gitzone commit -ytbp",
|
|
342
|
+
"gitzone release",
|
|
544
343
|
],
|
|
545
344
|
});
|
|
546
345
|
return;
|
|
@@ -549,25 +348,24 @@ export function showHelp(mode?: ICliMode): void {
|
|
|
549
348
|
console.log("");
|
|
550
349
|
console.log("Usage: gitzone commit [recommend] [options]");
|
|
551
350
|
console.log("");
|
|
351
|
+
console.log("Creates one semantic source commit. It does not version, tag, or publish.");
|
|
352
|
+
console.log("");
|
|
552
353
|
console.log("Commands:");
|
|
553
|
-
console.log(
|
|
554
|
-
" recommend Generate a commit recommendation without mutating the repository",
|
|
555
|
-
);
|
|
354
|
+
console.log(" recommend Generate a commit recommendation without mutating the repository");
|
|
556
355
|
console.log("");
|
|
557
356
|
console.log("Flags:");
|
|
558
|
-
console.log(" -y, --yes Auto-accept AI recommendations");
|
|
559
|
-
console.log(" -p, --push Push
|
|
560
|
-
console.log(" -t, --test Run tests
|
|
561
|
-
console.log(" -b, --build Run
|
|
562
|
-
console.log(
|
|
563
|
-
|
|
564
|
-
);
|
|
565
|
-
console.log(" --format Run gitzone format before committing");
|
|
357
|
+
console.log(" -y, --yes Auto-accept safe AI recommendations");
|
|
358
|
+
console.log(" -p, --push Push after commit");
|
|
359
|
+
console.log(" -t, --test Run tests in the configured order");
|
|
360
|
+
console.log(" -b, --build Run build in the configured order");
|
|
361
|
+
console.log(" -f, --format Run gitzone format before committing");
|
|
362
|
+
console.log(" --plan Show resolved workflow without mutating files");
|
|
566
363
|
console.log(" --json Emit JSON for `commit recommend` only");
|
|
567
364
|
console.log("");
|
|
568
365
|
console.log("Examples:");
|
|
569
366
|
console.log(" gitzone commit recommend --json");
|
|
570
367
|
console.log(" gitzone commit -y");
|
|
571
|
-
console.log(" gitzone commit -
|
|
368
|
+
console.log(" gitzone commit -ytbp");
|
|
369
|
+
console.log(" gitzone release");
|
|
572
370
|
console.log("");
|
|
573
371
|
}
|