179 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			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}`;
 | |
| };
 |