UNPKG

projex

Version:
249 lines (247 loc) 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.organizeCommitsToChangelog = exports.validateVersion = exports.shouldUpdateChangelog = void 0; const semver_1 = require("semver"); const constants_1 = require("./constants"); const _shared_1 = require("../../../../shared/index"); const _api_1 = require("../../../../api/index"); const chalk_1 = __importDefault(require("chalk")); const shouldUpdateChangelog = (releaseType, tagName) => { return ((constants_1.releaseTypesToUpdateChangelogList.indexOf(releaseType) >= 0 && tagName && constants_1.tagNamesToUpdateChangelog.indexOf(tagName) >= 0) || (0, semver_1.valid)(releaseType)); }; exports.shouldUpdateChangelog = shouldUpdateChangelog; const validateVersion = (oldVersion, releaseType, tagName) => { if ((0, semver_1.valid)(releaseType)) { // If `releaseType` is a valid (semver) version, use it. const parsedVersion = (0, semver_1.parse)(releaseType); const newVersion = parsedVersion?.version; if (!newVersion || !(0, semver_1.gt)(newVersion, oldVersion)) { const errorMessage = `the new version (${chalk_1.default.bold(newVersion)}) must be greater than the old version (${chalk_1.default.bold(oldVersion)})`; _shared_1.log.error(_api_1.Colors.ERROR(errorMessage)); throw new Error(errorMessage); } return [oldVersion, newVersion]; } // Else `releaseType` is just a regular release type. Then we increment the actual version. // Check if releaseType is valid. if (!constants_1.supportedReleaseTypesList.includes(releaseType)) { const validReleaseTypes = constants_1.supportedReleaseTypesList.join(', '); throw new Error(`Invalid release type: ${releaseType}\nValid release types are: ${validReleaseTypes}`); } // Check if tagName is valid. if (tagName && !constants_1.supportedTagNamesList.includes(tagName)) { const validTagNames = constants_1.supportedTagNamesList.join(', '); throw new Error(`Invalid release tag: ${tagName}\nValid release tags are: ${validTagNames}`); } }; exports.validateVersion = validateVersion; var ChangelogSection; (function (ChangelogSection) { ChangelogSection["BreakingChanges"] = "\u26A0 BREAKING CHANGES"; ChangelogSection["Features"] = "Features"; ChangelogSection["BugFixes"] = "Bug Fixes"; ChangelogSection["PerformanceImprovements"] = "Performance Improvements"; ChangelogSection["Reverts"] = "Reverts"; ChangelogSection["MiscellaneousChores"] = "Miscellaneous Chores"; ChangelogSection["Documentation"] = "Documentation"; ChangelogSection["Styles"] = "Styles"; ChangelogSection["CodeRefactoring"] = "Code Refactoring"; ChangelogSection["Tests"] = "Tests"; ChangelogSection["BuildSystem"] = "Build System"; ChangelogSection["ContinuousIntegration"] = "Continuous Integration"; })(ChangelogSection || (ChangelogSection = {})); const CHANGELOG_SECTIONS = [ { type: 'feat', section: ChangelogSection.Features }, { type: 'fix', section: ChangelogSection.BugFixes }, { type: 'perf', section: ChangelogSection.PerformanceImprovements }, { type: 'revert', section: ChangelogSection.Reverts }, { type: 'chore', section: ChangelogSection.MiscellaneousChores }, { type: 'docs', section: ChangelogSection.Documentation }, { type: 'style', section: ChangelogSection.Styles }, { type: 'refactor', section: ChangelogSection.CodeRefactoring }, { type: 'test', section: ChangelogSection.Tests }, { type: 'build', section: ChangelogSection.BuildSystem }, { type: 'ci', section: ChangelogSection.ContinuousIntegration }, { type: 'breaking', section: ChangelogSection.BreakingChanges }, ]; const normalizeCommit = (commits, originUrl, section) => { return commits.map((commit) => { const commitMessage = commit.slice(41); const message = `* ${commitMessage}`; if (section === ChangelogSection.BreakingChanges) { return message; } const commitId = commit.slice(0, 40); return `${message} ([${commitId.slice(0, 8)}](${originUrl}/commit/${commitId}))`; }); }; const getScopeInCommit = (commit) => { let regex = /\(([\w\s-]*)\)/; let match = commit.match(regex); if (match) { const scope = match[1]; return scope ? `**${scope}**: ` : ''; } else { return ''; } }; const getPullRequestLink = (originUrl, pullRequestId) => { let page = ''; if (originUrl.includes('github')) { page = 'pull'; } else { page = 'pullrequest'; } return `${originUrl}/${page}/${pullRequestId}`; }; const getPullRequestIdFromAzure = (commit, originUrl) => { let regex = /merged pr\s+(\d+)(\s|:\s)/; let match = commit.match(regex); if (match) { const text = match[0]; const pullRequestId = match[1]; // Remove "Merged PR" text const withoutMergedText = commit.replace(text, ''); const url = getPullRequestLink(originUrl, pullRequestId); return `${withoutMergedText} ([#${pullRequestId}](${url}))`; } else { return commit; } }; const getPullRequestIdFromGithub = (commit, originUrl) => { let regex = /\(#(\d+)\)/; let match = commit.match(regex); if (match) { const text = match[0]; // remove parentheses const withoutParentheses = text.replace(/[\(\)]/g, ''); // remove # symbol const pullRequestId = withoutParentheses.replace('#', ''); const url = getPullRequestLink(originUrl, pullRequestId); return commit.replace(text, `([${withoutParentheses}](${url}))`); } else { return commit; } }; const getPullRequestCommit = (commit, originUrl) => { const commitWithGithubId = getPullRequestIdFromGithub(commit, originUrl); if (commitWithGithubId != commit) { return commitWithGithubId; } else { return getPullRequestIdFromAzure(commit, originUrl); } }; const getUnReleasedChanges = (changelogContent) => { let regex = /.*:\s*release\s*(v\d+\.\d+\.\d+)(\s+.*|$)/; let lines = changelogContent.split('\n'); let result = ''; for (let line of lines) { if (regex.test(line)) { break; } result += line + '\n'; } if (!result || result == '') { _shared_1.log.error(_api_1.Colors.ERROR('no unreleased changes found, please check your commits.')); process.exit(1); } return result; }; const generateChangelogSectionForTitle = (section, commits, originUrl) => { const normalizedCommits = normalizeCommit(commits, originUrl, section).join('\n'); return ` ### ${section} ${normalizedCommits} `; }; const generateChangelogSection = (changes, originUrl) => { const sections = new Map(); CHANGELOG_SECTIONS.forEach((section) => sections.set(section.section, [])); changes.forEach((change) => { const section = sections.get(change.section); if (section) { section.push(change.commit); } }); let changelog = ''; sections.forEach((commits, section) => { if (commits.length > 0) { changelog += generateChangelogSectionForTitle(section, commits, originUrl); } }); return changelog; }; const determineTypeChange = (commit) => { const changes = []; for (let section of CHANGELOG_SECTIONS) { // Get the type of the commit let regex = new RegExp(`(${section.type})(\\([\\w\\s-]*\\))?:\\s*`); let match = commit.match(regex); // Get the breaking change let breakingChangeRegex = new RegExp(`(${section.type})(\\([\\w\\s-]*\\))?!:\\s*`); let breakingChangeMatch = commit.match(breakingChangeRegex); const scope = getScopeInCommit(commit); if (match) { changes.push({ section: section.section, commit: commit.replace(match[0], scope), }); } if (breakingChangeMatch) { changes.push({ commit: commit.replace(breakingChangeMatch[0], scope), section: ChangelogSection.BreakingChanges, }); const typeChange = breakingChangeMatch[1]; const typeBreakingChange = CHANGELOG_SECTIONS.find((section) => section.type === typeChange); if (typeBreakingChange) { changes.push({ commit: commit.replace(breakingChangeMatch[0], scope), section: typeBreakingChange.section, }); } } } if (!changes.length) { changes.push({ commit, section: ChangelogSection.Features, }); } return changes; }; const determineReleaseType = (changes) => { const haveBreakingChanges = changes.some((change) => change.section === ChangelogSection.BreakingChanges); const haveChangesExcludingBugFixes = changes.some((change) => change.section !== ChangelogSection.BugFixes && change.section !== ChangelogSection.BreakingChanges); return haveBreakingChanges ? 'major' : haveChangesExcludingBugFixes ? 'minor' : 'patch'; }; const organizeCommitsToChangelog = (commits, originUrl) => { const unReleasedCommits = getUnReleasedChanges(commits); const changes = []; // Split commits by line and group them by type unReleasedCommits.split('\n').forEach((commit) => { if (commit === '') return; commit = commit.toLowerCase(); commit = getPullRequestCommit(commit, originUrl); changes.push(...determineTypeChange(commit)); }); const changelog = generateChangelogSection(changes, originUrl); const releaseType = determineReleaseType(changes); return { changelog, releaseType, }; }; exports.organizeCommitsToChangelog = organizeCommitsToChangelog;