Files
cli/ts/mod_commit/index.ts

179 lines
6.2 KiB
TypeScript

// this file contains code to create commits in a consistent way
import * as plugins from './mod.plugins.js';
import * as paths from '../paths.js';
import { logger } from '../gitzone.logging.js';
import * as helpers from './mod.helpers.js';
import * as ui from './mod.ui.js';
export const run = async (argvArg: any) => {
if (argvArg.format) {
const formatMod = await import('../mod_format/index.js');
await formatMod.run();
}
ui.printHeader('🔍 Analyzing repository changes...');
const aidoc = new plugins.tsdoc.AiDoc();
await aidoc.start();
const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd);
await aidoc.stop();
ui.printRecommendation({
recommendedNextVersion: nextCommitObject.recommendedNextVersion,
recommendedNextVersionLevel: nextCommitObject.recommendedNextVersionLevel,
recommendedNextVersionScope: nextCommitObject.recommendedNextVersionScope,
recommendedNextVersionMessage: nextCommitObject.recommendedNextVersionMessage,
});
const commitInteract = new plugins.smartinteract.SmartInteract();
commitInteract.addQuestions([
{
type: 'list',
name: `commitType`,
message: `Choose TYPE of the commit:`,
choices: [`fix`, `feat`, `BREAKING CHANGE`],
default: nextCommitObject.recommendedNextVersionLevel,
},
{
type: 'input',
name: `commitScope`,
message: `What is the SCOPE of the commit:`,
default: nextCommitObject.recommendedNextVersionScope,
},
{
type: `input`,
name: `commitDescription`,
message: `What is the DESCRIPTION of the commit?`,
default: nextCommitObject.recommendedNextVersionMessage,
},
{
type: 'confirm',
name: `pushToOrigin`,
message: `Do you want to push this version now?`,
default: true,
},
]);
const answerBucket = await commitInteract.runQueue();
const commitString = createCommitStringFromAnswerBucket(answerBucket);
const commitVersionType = (() => {
switch (answerBucket.getAnswerFor('commitType')) {
case 'fix':
return 'patch';
case 'feat':
return 'minor';
case 'BREAKING CHANGE':
return 'major';
}
})();
ui.printHeader('✨ Creating Semantic Commit');
ui.printCommitMessage(commitString);
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Determine total steps (6 if pushing, 5 if not)
const totalSteps = answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true') ? 6 : 5;
let currentStep = 0;
// Step 1: Baking commitinfo
currentStep++;
ui.printStep(currentStep, totalSteps, '🔧 Baking commit info into code', 'in-progress');
const commitInfo = new plugins.commitinfo.CommitInfo(
paths.cwd,
commitVersionType,
);
await commitInfo.writeIntoPotentialDirs();
ui.printStep(currentStep, totalSteps, '🔧 Baking commit info into code', 'done');
// Step 2: Writing changelog
currentStep++;
ui.printStep(currentStep, totalSteps, '📄 Generating changelog.md', 'in-progress');
let changelog = nextCommitObject.changelog;
changelog = changelog.replaceAll(
'{{nextVersion}}',
(await commitInfo.getNextPlannedVersion()).versionString,
);
changelog = changelog.replaceAll(
'{{nextVersionScope}}',
`${await answerBucket.getAnswerFor('commitType')}(${await answerBucket.getAnswerFor('commitScope')})`,
);
changelog = changelog.replaceAll(
'{{nextVersionMessage}}',
nextCommitObject.recommendedNextVersionMessage,
);
if (nextCommitObject.recommendedNextVersionDetails?.length > 0) {
changelog = changelog.replaceAll(
'{{nextVersionDetails}}',
'- ' + nextCommitObject.recommendedNextVersionDetails.join('\n- '),
);
} else {
changelog = changelog.replaceAll('\n{{nextVersionDetails}}', '');
}
await plugins.smartfile.memory.toFs(
changelog,
plugins.path.join(paths.cwd, `changelog.md`),
);
ui.printStep(currentStep, totalSteps, '📄 Generating changelog.md', 'done');
// Step 3: Staging files
currentStep++;
ui.printStep(currentStep, totalSteps, '📦 Staging files', 'in-progress');
await smartshellInstance.exec(`git add -A`);
ui.printStep(currentStep, totalSteps, '📦 Staging files', 'done');
// Step 4: Creating commit
currentStep++;
ui.printStep(currentStep, totalSteps, '💾 Creating git commit', 'in-progress');
await smartshellInstance.exec(`git commit -m "${commitString}"`);
ui.printStep(currentStep, totalSteps, '💾 Creating git commit', 'done');
// Step 5: Bumping version
currentStep++;
const projectType = await helpers.detectProjectType();
const newVersion = await helpers.bumpProjectVersion(projectType, commitVersionType, currentStep, totalSteps);
// Step 6: Push to remote (optional)
const currentBranch = await helpers.detectCurrentBranch();
if (
answerBucket.getAnswerFor('pushToOrigin') &&
!(process.env.CI === 'true')
) {
currentStep++;
ui.printStep(currentStep, totalSteps, `🚀 Pushing to origin/${currentBranch}`, 'in-progress');
await smartshellInstance.exec(`git push origin ${currentBranch} --follow-tags`);
ui.printStep(currentStep, totalSteps, `🚀 Pushing to origin/${currentBranch}`, 'done');
}
console.log(''); // Add spacing before summary
// Get commit SHA for summary
const commitShaResult = await smartshellInstance.exec('git rev-parse --short HEAD');
const commitSha = commitShaResult.stdout.trim();
// Print final summary
ui.printSummary({
projectType,
branch: currentBranch,
commitType: answerBucket.getAnswerFor('commitType'),
commitScope: answerBucket.getAnswerFor('commitScope'),
commitMessage: answerBucket.getAnswerFor('commitDescription'),
newVersion: newVersion,
commitSha: commitSha,
pushed: answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true'),
});
};
const createCommitStringFromAnswerBucket = (
answerBucket: plugins.smartinteract.AnswerBucket,
) => {
const commitType = answerBucket.getAnswerFor('commitType');
const commitScope = answerBucket.getAnswerFor('commitScope');
const commitDescription = answerBucket.getAnswerFor('commitDescription');
return `${commitType}(${commitScope}): ${commitDescription}`;
};