@gitlab/semantic-release-merge-request-analyzer
Version:
Semantic release plugin to determine version based on GitLab merge request labels
114 lines (93 loc) • 3.13 kB
JavaScript
import getGitLabClient from "./get-gitlab-client.js";
import {
getMergeRequestForCommit,
getReleaseType,
} from "./get-merge-request.js";
import { getDefaultLabelConfig, releaseNoteSections } from "./config.js";
/**
* Generates release notes based on merge requests.
*
* @param {Object} pluginConfig The plugin configuration.
* @param {Object} context The semantic-release context.
* @returns {Promise<string>} The generated release notes.
*/
export async function generateNotes(pluginConfig, context) {
const { env, commits, logger, nextRelease } = context;
const projectId = env.CI_PROJECT_ID;
const gitlab = getGitLabClient(context);
logger.log(`Generating release notes for version ${nextRelease.version}`);
// Get label configuration from plugin config or use defaults
const labelConfig =
(pluginConfig && pluginConfig.labels) || getDefaultLabelConfig();
// Get section customizations if provided
const sectionEmojis =
pluginConfig?.sections?.emojis || releaseNoteSections.emojis;
const sectionTitles =
pluginConfig?.sections?.titles || releaseNoteSections.titles;
let releaseNotes = `## ${nextRelease.version}\n\n`;
// Categorize merge requests by release type
const mrByType = {
major: [],
premajor: [],
minor: [],
preminor: [],
patch: [],
prepatch: [],
prerelease: [],
other: [],
};
// Track processed merge requests to avoid duplicates
const processedMRs = new Set();
// Process each commit
logger.log(`Processing ${commits.length} commits for release notes`);
for (const commit of commits) {
const mergeRequest = await getMergeRequestForCommit(
gitlab,
projectId,
commit.hash,
logger,
);
if (!mergeRequest) {
const entry =`- ${commit.message} (${commit.hash})`
mrByType.other.push(entry);
continue;
}
const { iid, title, web_url, author } = mergeRequest;
if (processedMRs.has(iid)) {
continue;
}
processedMRs.add(iid);
const mrEntry = `- ${title} ([!${iid}](${web_url})) by [${author.name}](${author.web_url})`
// Determine the release type for this MR using the configured labels
const releaseType = getReleaseType(mergeRequest, labelConfig, logger);
if (releaseType && releaseType in mrByType) {
logger.log(`Adding merge request !${iid} to ${releaseType} section`);
mrByType[releaseType].push(mrEntry);
} else {
logger.log(`Adding merge request !${iid} to other section`);
mrByType.other.push(mrEntry);
}
}
// Add sections in a specific order of importance
const sectionOrder = [
"major",
"premajor",
"minor",
"preminor",
"patch",
"prepatch",
"prerelease",
"other",
];
for (const type of sectionOrder) {
const entries = mrByType[type];
if (entries.length > 0) {
const emoji = sectionEmojis[type];
const title = sectionTitles[type];
releaseNotes += `### ${emoji} ${title}\n\n${entries.join("\n")}\n\n`;
}
}
logger.success("Release notes generated");
return releaseNotes;
}
export default generateNotes;