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}`;
 | 
						|
};
 |