-
Notifications
You must be signed in to change notification settings - Fork 2
/
git-commit-prompt.js
356 lines (308 loc) · 11.8 KB
/
git-commit-prompt.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
`use strict`;
const { exec } = require("child_process");
const readline = require("readline");
const chalk = require("./utils/chalk-messages.js");
/**
* The execAsync() function is a utility function that wraps the `exec()` method from the `child_process` module in Node.js and returns a promise.
* This allows you to execute shell commands asynchronously in a more structured and predictable way.
* The function takes two arguments: `command` and `rl`. The `command` argument is a string that specifies the shell `command` to be executed.
* The `rl` argument is an optional readline interface that can be closed if the `command` execution fails.
*/
function execAsync(command, rl) {
return new Promise((resolve, reject) => {
// Execute the command using the `exec()` method from the `child_process` module
exec(command, (error, stdout, stderr) => {
if (error) {
reject(error);
rl.close();
} else if (stderr) {
reject(stderr);
rl.close();
} else {
// If there is no error, resolve the promise with the stdout
resolve(stdout);
}
});
});
}
function readlineQuestionAsync(question, rl) {
return new Promise((resolve, reject) => {
rl.question(chalk.consoleB(question) + " ", (answer) => {
resolve(answer);
});
});
}
const COMMIT_TYPES = Object.freeze({
1: "test",
2: "feat",
3: "refactor",
4: "style",
5: "fix",
6: "chore",
7: "docs",
8: "build",
9: "perf",
10: "ci",
11: "revert",
12: "delete",
});
/**
* @function askCommitPrompt
* @description This function prompts the user for input using the provided prompt message and
* validates the response according to the provided prompt flag.
* @param {string} prompt - The message to prompt the user for input
* @param {readline.Interface} rl - The readline interface object used for user input
* @param {string} promptFlag - A flag indicating the type of prompt to display,
* either "TYPE", "DOMAIN", "MESSAGE", "CONFIRM",
* "AMEND", or "ORIGIN"
* @returns {Promise<string>} - The validated user input as a string
*/
async function askCommitPrompt(prompt, rl, promptFlag) {
// Prompt the user for input and await the response
let promptResponse = await readlineQuestionAsync(`${prompt}`, rl);
// Validate the user input based on the prompt flag
if (typeof promptResponse !== "string" || promptResponse.trim() === "") {
console.log(chalk.consoleYlow(`Response must be a non-empty string`));
// Recursively call the function until a valid input is received
promptResponse = askCommitPrompt(prompt, rl, promptFlag);
} else {
switch (promptFlag) {
case "TYPE":
// Check if the input is at least 2 characters long
if (promptResponse.length < 2) {
console.log(chalk.consoleYlow("Commit type must be at least 2 characters long"));
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
// Check if the input is a valid commit type
if (!Object.values(COMMIT_TYPES).includes(promptResponse.toLowerCase())) {
console.log(chalk.consoleYlow(`Invalid input. Please enter a correct type:`));
console.log(COMMIT_TYPES);
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
break;
case "DOMAIN":
// Check if the input is at least 3 characters long
if (promptResponse.length < 3) {
console.log(chalk.consoleYlow("Commit domain must be at least 3 characters long"));
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
break;
case "MESSAGE":
// Check if the input is at least 10 characters long
if (promptResponse.length < 10) {
console.log(chalk.consoleYlow("Commit message must be at least 10 characters long"));
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
break;
case "CONFIRM":
// Check if the input is a valid confirmation response
if (!["yes", "y", "no", "n", "quit", "end", "close"].includes(promptResponse.toLowerCase())) {
console.log(chalk.consoleYlow("Invalid input. Please enter 'Y', 'N', 'END' or 'QUIT'"));
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
break;
case "AMEND":
// Check if the input is a valid amend type
if (!["TYPE", "DOMAIN", "MESSAGE", "NONE"].includes(promptResponse.toUpperCase())) {
console.log(chalk.consoleYlow("Invalid input. Please enter 'TYPE', 'DOMAIN', 'MESSAGE' or 'NONE'"));
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
break;
case "ORIGIN":
if (!["yes", "y", "no", "n"].includes(promptResponse.toLowerCase())) {
console.log(chalk.consoleYlow("Invalid input. Please enter 'Y' or 'N'"));
promptResponse = await askCommitPrompt(prompt, rl, promptFlag);
}
default:
// resolve(answer);
break;
}
}
return promptResponse;
}
async function writeLocalCommit(commitMsg, readLineInterface) {
console.log(chalk.consoleGy("Writing local commit .."));
// Add and commit the changes using the complete commit message
try {
const commitResponse = await execAsync(`git add -A && git commit -m "${commitMsg}`, readLineInterface);
console.log(`commitResponse:`);
console.log(chalk.consoleG(commitResponse));
} catch (error) {
console.error(chalk.warningBright(`local commit error: ${error}`));
}
}
async function writeOriginCommit(readLineInterface) {
console.log(chalk.consoleGy("Committing to origin .."));
try {
const pushOriginResponse = await execAsync(`git push origin master`, readLineInterface);
console.log(`pushOriginResponse:`);
console.log(chalk.consoleG(pushOriginResponse));
return true;
} catch (error) {
return false;
}
}
async function forceOriginCommit(readLineInterface) {
const askForceCommitOrigin = await askCommitPrompt("Force push commit to remote origin? ( Y / N )", readLineInterface, "ORIGIN");
if (["yes", "y"].includes(askForceCommitOrigin.toLowerCase())) {
console.log(chalk.consoleGy("Force pushing to origin .."));
try {
pushOriginResponse = await execAsync(`git push origin master --force`, readLineInterface);
console.log({ pushOriginResponse });
} catch (error) {
console.error(chalk.fail(`origin commit error: ${error}`));
process.exit(0);
}
}
console.log("yeo are here");
rl.close();
process.exit(0);
};
/**
* @description Prompts the user for a commit message and number of log lines, then
* executes a git commit and push to origin.
* @function executeCommitPrompts
*/
async function executeCommitPrompts() {
// Create a readline interface to prompt the user for input
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log(chalk.consoleYlow(`Valid commit types:`));
console.log(COMMIT_TYPES);
// Declare variables to store commit information
let commitType,
commitDomain,
commitMsg,
completeCommitMsg,
commitConfirm,
commitAmendChoice,
// commitResponse,
askOriginCommit,
originCommitOk,
askForceCommitOrigin,
pushOriginResponse;
try {
// Prompt the user for commit information until they confirm their message
while (true) {
// Check if the user has requested to change a specific part of the commit message
switch (commitAmendChoice?.toUpperCase()) {
case "TYPE":
commitType = await askCommitPrompt("Enter a commit TYPE:", rl, "TYPE");
break;
case "DOMAIN":
commitDomain = await askCommitPrompt("Enter a commit DOMAIN:", rl, "DOMAIN");
break;
case "MESSAGE":
commitMsg = await askCommitPrompt("Enter a commit MESSAGE:", rl, "MESSAGE");
break;
case "NONE":
break;
default:
// Prompt the user for the full commit message if no amendment is requested
commitType = await askCommitPrompt("Enter a commit TYPE:", rl, "TYPE");
commitDomain = await askCommitPrompt("Enter a commit DOMAIN:", rl, "DOMAIN");
commitMsg = await askCommitPrompt("Enter a commit MESSAGE:", rl, "MESSAGE");
break;
}
// Combine the commit information into a single message
completeCommitMsg = `${commitType} (${commitDomain}): ${commitMsg}"`;
console.log({ completeCommitMsg });
// Confirm the commit message with the user
commitConfirm = await askCommitPrompt("Confirm commit message is OK? ( Y / N / QUIT):", rl, "CONFIRM");
if (["yes", "y"].includes(commitConfirm.toLowerCase())) {
// Break out of while loop
break;
} else if (["quit", "q", "end"].includes(commitConfirm.toLowerCase())) {
// Quit cmd line program
process.exit(0);
} else {
// If the user doesn't confirm their message, allow them to amend it
console.log({ commitType });
console.log({ commitDomain });
console.log({ commitMsg });
commitAmendChoice = await askCommitPrompt(
`Select which prompt to amend ( "TYPE", "DOMAIN", "MESSAGE", "NONE"):`,
rl,
"AMEND"
);
}
}
// // REMOVE
// console.log(chalk.consoleGy("Writing local commit .."));
// // REMOVE
// // Add and commit the changes using the complete commit message
// try {
// commitResponse = await execAsync(`git add -A && git commit -m "${completeCommitMsg}`, rl);
// console.log(`commitResponse:`);
// console.log(chalk.consoleG(commitResponse));
// } catch (error) {
// console.error(chalk.warningBright(`local commit error: ${error}`));
// }
// Make a local commit
await writeLocalCommit(completeCommitMsg, rl);
// Prompt user to commit to origin / master
askOriginCommit = await askCommitPrompt("Push commit to remote origin? ( Y / N )", rl, "ORIGIN");
originCommitOk = writeOriginCommit(rl);
if (!originCommitOk) forceOriginCommit(rl);
process.exit(0);
// User chooses to commit to remote origin
if (["yes", "y"].includes(askOriginCommit.toLowerCase())) {
console.log(chalk.consoleGy("Committing to origin .."));
pushOriginResponse = await execAsync(`git push origin master`, rl);
console.log(`pushOriginResponse:`);
console.log(chalk.consoleG(pushOriginResponse));
// try {
// pushOriginResponse = await execAsync(`git push origin master`, rl);
// console.log(`pushOriginResponse`);
// console.log(chalk.consoleG(pushOriginResponse));
// } catch (error) {
// console.error(chalk.fail(`origin commit error:`))
// console.error(chalk.fail(error))
// // Give option to user to force the commit to origin
// console.log(error)
// // if (error.message.toLowerCase().includes("command failed")) {
// // const askForceCommitOrigin = await askCommitPrompt("Force push commit to remote origin? ( Y / N )", rl, "ORIGIN");
// // if (["yes", "y"].includes(askForceCommitOrigin.toLowerCase())) {
// // console.log(chalk.consoleGy("Committing to origin .."));
// // try {
// // pushOriginResponse = await execAsync(`git push origin master`, rl);
// // console.log(`pushOriginResponse:`);
// // console.log(chalk.consoleG(pushOriginResponse));
// // } catch (error) {
// // console.error(chalk.fail(`origin commit error: ${error}`));
// // }
// // }
// // }
// } finally {
// rl.close();
// process.exit();
// }
} else {
}
} catch (error) {
console.error(chalk.fail(`executeCommitPrompts error`));
console.error(chalk.consoleYlow(error.message));
// if (error.message.toLowerCase().includes("command failed")) {
// askForceCommitOrigin = await askCommitPrompt("Force push commit to remote origin? ( Y / N )", rl, "ORIGIN");
// if (["yes", "y"].includes(askForceCommitOrigin.toLowerCase())) {
// console.log(chalk.consoleGy("Committing to origin .."));
// try {
// pushOriginResponse = await execAsync(`git push origin master --force`, rl);
// console.log({ pushOriginResponse });
// } catch (error) {
// console.error(chalk.fail(`origin commit error: ${error}`));
// }
// }
// console.log("yeo are here")
// rl.close();
// process.exit(0);
// }
} finally {
// Close the readline interface and exit the process
rl.close();
process.exit();
}
}
executeCommitPrompts();