nx
Version:
221 lines (220 loc) • 12.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.readRawVersionPlans = readRawVersionPlans;
exports.setResolvedVersionPlansOnGroups = setResolvedVersionPlansOnGroups;
exports.getVersionPlansAbsolutePath = getVersionPlansAbsolutePath;
const node_child_process_1 = require("node:child_process");
const node_fs_1 = require("node:fs");
const promises_1 = require("node:fs/promises");
const path_1 = require("path");
const semver_1 = require("semver");
const workspace_root_1 = require("../../../utils/workspace-root");
const config_1 = require("./config");
const fm = require('front-matter');
const versionPlansDirectory = (0, path_1.join)('.nx', 'version-plans');
async function readRawVersionPlans() {
const versionPlansPath = getVersionPlansAbsolutePath();
if (!(0, node_fs_1.existsSync)(versionPlansPath)) {
return [];
}
const versionPlans = [];
const versionPlanFiles = (0, node_fs_1.readdirSync)(versionPlansPath);
for (const versionPlanFile of versionPlanFiles) {
const filePath = (0, path_1.join)(versionPlansPath, versionPlanFile);
const versionPlanContent = (0, node_fs_1.readFileSync)(filePath).toString();
const versionPlanStats = await (0, promises_1.stat)(filePath);
const parsedContent = fm(versionPlanContent);
versionPlans.push({
absolutePath: filePath,
relativePath: (0, path_1.join)(versionPlansDirectory, versionPlanFile),
fileName: versionPlanFile,
content: parsedContent.attributes,
message: parsedContent.body,
createdOnMs: versionPlanStats.birthtimeMs,
});
}
return versionPlans;
}
async function setResolvedVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNamesInWorkspace, isVerbose) {
const groupsByName = releaseGroups.reduce((acc, group) => acc.set(group.name, group), new Map());
const isDefaultGroup = isDefault(releaseGroups);
for (const rawVersionPlan of rawVersionPlans) {
if (!rawVersionPlan.message) {
throw new Error(`Please add a changelog message to version plan: '${rawVersionPlan.fileName}'`);
}
for (const [key, value] of Object.entries(rawVersionPlan.content)) {
if (groupsByName.has(key)) {
const group = groupsByName.get(key);
if (!group.resolvedVersionPlans) {
if (isDefaultGroup) {
throw new Error(`Found a version bump in '${rawVersionPlan.fileName}' but version plans are not enabled.`);
}
else {
throw new Error(`Found a version bump for group '${key}' in '${rawVersionPlan.fileName}' but the group does not have version plans enabled.`);
}
}
if (group.projectsRelationship === 'independent') {
if (isDefaultGroup) {
throw new Error(`Found a version bump in '${rawVersionPlan.fileName}' but projects are configured to be independently versioned. Individual projects should be bumped instead.`);
}
else {
throw new Error(`Found a version bump for group '${key}' in '${rawVersionPlan.fileName}' but the group's projects are independently versioned. Individual projects of '${key}' should be bumped instead.`);
}
}
if (!isReleaseType(value)) {
if (isDefaultGroup) {
throw new Error(`Found a version bump in '${rawVersionPlan.fileName}' with an invalid release type. Please specify one of ${semver_1.RELEASE_TYPES.join(', ')}.`);
}
else {
throw new Error(`Found a version bump for group '${key}' in '${rawVersionPlan.fileName}' with an invalid release type. Please specify one of ${semver_1.RELEASE_TYPES.join(', ')}.`);
}
}
const existingPlan = (group.resolvedVersionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
if (existingPlan) {
if (existingPlan.groupVersionBump !== value) {
if (isDefaultGroup) {
throw new Error(`Found a version bump in '${rawVersionPlan.fileName}' that conflicts with another version bump. When in fixed versioning mode, all version bumps must match.`);
}
else {
throw new Error(`Found a version bump for group '${key}' in '${rawVersionPlan.fileName}' that conflicts with another version bump for this group. When the group is in fixed versioning mode, all groups' version bumps within the same version plan must match.`);
}
}
}
else {
group.resolvedVersionPlans.push({
absolutePath: rawVersionPlan.absolutePath,
relativePath: rawVersionPlan.relativePath,
fileName: rawVersionPlan.fileName,
createdOnMs: rawVersionPlan.createdOnMs,
message: rawVersionPlan.message,
groupVersionBump: value,
commit: await getCommitForVersionPlanFile(rawVersionPlan, isVerbose),
});
}
}
else {
const groupForProject = releaseGroups.find((group) => group.projects.includes(key));
if (!groupForProject) {
if (!allProjectNamesInWorkspace.includes(key)) {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but the project does not exist in the workspace.`);
}
if (isDefaultGroup) {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but the project is not configured for release. Ensure it is included by the 'release.projects' globs in nx.json.`);
}
else {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but the project is not in any configured release groups.`);
}
}
if (!groupForProject.resolvedVersionPlans) {
if (isDefaultGroup) {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but version plans are not enabled.`);
}
else {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but the project's group '${groupForProject.name}' does not have version plans enabled.`);
}
}
if (!isReleaseType(value)) {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' with an invalid release type. Please specify one of ${semver_1.RELEASE_TYPES.join(', ')}.`);
}
if (groupForProject.projectsRelationship === 'independent') {
const existingPlan = (groupForProject.resolvedVersionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
if (existingPlan) {
existingPlan.projectVersionBumps[key] = value;
}
else {
groupForProject.resolvedVersionPlans.push({
absolutePath: rawVersionPlan.absolutePath,
relativePath: rawVersionPlan.relativePath,
fileName: rawVersionPlan.fileName,
createdOnMs: rawVersionPlan.createdOnMs,
message: rawVersionPlan.message,
projectVersionBumps: {
[key]: value,
},
commit: await getCommitForVersionPlanFile(rawVersionPlan, isVerbose),
});
}
}
else {
const existingPlan = (groupForProject.resolvedVersionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
// This can occur if the same fixed release group has multiple entries for different projects within
// the same version plan file. This will be the case when users are using the default release group.
if (existingPlan) {
if (existingPlan.groupVersionBump !== value) {
if (isDefaultGroup) {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' that conflicts with another version bump. When in fixed versioning mode, all version bumps must match.`);
}
else {
throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' that conflicts with another project's version bump in the same release group '${groupForProject.name}'. When the group is in fixed versioning mode, all projects' version bumps within the same group must match.`);
}
}
else {
existingPlan.triggeredByProjects.push(key);
}
}
else {
groupForProject.resolvedVersionPlans.push({
absolutePath: rawVersionPlan.absolutePath,
relativePath: rawVersionPlan.relativePath,
fileName: rawVersionPlan.fileName,
createdOnMs: rawVersionPlan.createdOnMs,
message: rawVersionPlan.message,
// This is a fixed group, so the version bump is for the group, even if a project within it was specified
// but we track the projects that triggered the version bump so that we can accurately produce changelog entries.
groupVersionBump: value,
triggeredByProjects: [key],
commit: await getCommitForVersionPlanFile(rawVersionPlan, isVerbose),
});
}
}
}
}
}
// Order the plans from newest to oldest
releaseGroups.forEach((group) => {
if (group.resolvedVersionPlans) {
group.resolvedVersionPlans.sort((a, b) => b.createdOnMs - a.createdOnMs);
}
});
return releaseGroups;
}
function isDefault(releaseGroups) {
return (releaseGroups.length === 1 &&
releaseGroups.some((group) => group.name === config_1.IMPLICIT_DEFAULT_RELEASE_GROUP));
}
function getVersionPlansAbsolutePath() {
return (0, path_1.join)(workspace_root_1.workspaceRoot, versionPlansDirectory);
}
function isReleaseType(value) {
return semver_1.RELEASE_TYPES.includes(value);
}
async function getCommitForVersionPlanFile(rawVersionPlan, isVerbose) {
return new Promise((resolve) => {
(0, node_child_process_1.exec)(`git log --diff-filter=A --pretty=format:"%s|%h|%an|%ae|%b" -n 1 -- ${rawVersionPlan.absolutePath}`, {
windowsHide: false,
}, (error, stdout, stderr) => {
if (error) {
if (isVerbose) {
console.error(`Error executing git command for ${rawVersionPlan.relativePath}: ${error.message}`);
}
return resolve(null);
}
if (stderr) {
if (isVerbose) {
console.error(`Git command stderr for ${rawVersionPlan.relativePath}: ${stderr}`);
}
return resolve(null);
}
const [message, shortHash, authorName, authorEmail, ...body] = stdout
.trim()
.split('|');
const commitDetails = {
message: message || '',
shortHash: shortHash || '',
author: { name: authorName || '', email: authorEmail || '' },
body: body.join('|') || '', // Handle case where body might be empty or contain multiple '|'
};
return resolve(commitDetails);
});
});
}
;