summaryrefslogtreecommitdiffstats
path: root/tools/conventional-changelog-tf-a
diff options
context:
space:
mode:
Diffstat (limited to 'tools/conventional-changelog-tf-a')
-rw-r--r--tools/conventional-changelog-tf-a/index.js222
-rw-r--r--tools/conventional-changelog-tf-a/package.json13
-rw-r--r--tools/conventional-changelog-tf-a/templates/commit-section.hbs17
-rw-r--r--tools/conventional-changelog-tf-a/templates/commit.hbs15
-rw-r--r--tools/conventional-changelog-tf-a/templates/footer.hbs0
-rw-r--r--tools/conventional-changelog-tf-a/templates/header.hbs13
-rw-r--r--tools/conventional-changelog-tf-a/templates/note-section.hbs13
-rw-r--r--tools/conventional-changelog-tf-a/templates/note.hbs3
-rw-r--r--tools/conventional-changelog-tf-a/templates/template.hbs9
9 files changed, 305 insertions, 0 deletions
diff --git a/tools/conventional-changelog-tf-a/index.js b/tools/conventional-changelog-tf-a/index.js
new file mode 100644
index 0000000..2a9d5b4
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/index.js
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/* eslint-env es6 */
+
+"use strict";
+
+const Handlebars = require("handlebars");
+const Q = require("q");
+const _ = require("lodash");
+
+const ccConventionalChangelog = require("conventional-changelog-conventionalcommits/conventional-changelog");
+const ccParserOpts = require("conventional-changelog-conventionalcommits/parser-opts");
+const ccRecommendedBumpOpts = require("conventional-changelog-conventionalcommits/conventional-recommended-bump");
+const ccWriterOpts = require("conventional-changelog-conventionalcommits/writer-opts");
+
+const execa = require("execa");
+
+const readFileSync = require("fs").readFileSync;
+const resolve = require("path").resolve;
+
+/*
+ * Register a Handlebars helper that lets us generate Markdown lists that can support multi-line
+ * strings. This is driven by inconsistent formatting of breaking changes, which may be multiple
+ * lines long and can terminate the list early unintentionally.
+ */
+Handlebars.registerHelper("tf-a-mdlist", function (indent, options) {
+ const spaces = new Array(indent + 1).join(" ");
+ const first = spaces + "- ";
+ const nth = spaces + " ";
+
+ return first + options.fn(this).replace(/\n(?!\s*\n)/gm, `\n${nth}`).trim() + "\n";
+});
+
+/*
+ * Register a Handlebars helper that concatenates multiple variables. We use this to generate the
+ * title for the section partials.
+ */
+Handlebars.registerHelper("tf-a-concat", function () {
+ let argv = Array.prototype.slice.call(arguments, 0);
+
+ argv.pop();
+
+ return argv.join("");
+});
+
+function writerOpts(config) {
+ /*
+ * Flatten the configuration's sections list. This helps us iterate over all of the sections
+ * when we don't care about the hierarchy.
+ */
+
+ const flattenSections = function (sections) {
+ return sections.flatMap(section => {
+ const subsections = flattenSections(section.sections || []);
+
+ return [section].concat(subsections);
+ })
+ };
+
+ const flattenedSections = flattenSections(config.sections);
+
+ /*
+ * Register a helper to return a restructured version of the note groups that includes notes
+ * categorized by their section.
+ */
+ Handlebars.registerHelper("tf-a-notes", function (noteGroups, options) {
+ const generateTemplateData = function (sections, notes) {
+ return (sections || []).flatMap(section => {
+ const templateData = {
+ title: section.title,
+ sections: generateTemplateData(section.sections, notes),
+ notes: notes.filter(note => section.scopes?.includes(note.commit.scope)),
+ };
+
+ /*
+ * Don't return a section if it contains no notes and no sub-sections.
+ */
+ if ((templateData.sections.length == 0) && (templateData.notes.length == 0)) {
+ return [];
+ }
+
+ return [templateData];
+ });
+ };
+
+ return noteGroups.map(noteGroup => {
+ return {
+ title: noteGroup.title,
+ sections: generateTemplateData(config.sections, noteGroup.notes),
+ notes: noteGroup.notes.filter(note =>
+ !flattenedSections.some(section => section.scopes?.includes(note.commit.scope))),
+ };
+ });
+ });
+
+ /*
+ * Register a helper to return a restructured version of the commit groups that includes commits
+ * categorized by their section.
+ */
+ Handlebars.registerHelper("tf-a-commits", function (commitGroups, options) {
+ const generateTemplateData = function (sections, commits) {
+ return (sections || []).flatMap(section => {
+ const templateData = {
+ title: section.title,
+ sections: generateTemplateData(section.sections, commits),
+ commits: commits.filter(commit => section.scopes?.includes(commit.scope)),
+ };
+
+ /*
+ * Don't return a section if it contains no notes and no sub-sections.
+ */
+ if ((templateData.sections.length == 0) && (templateData.commits.length == 0)) {
+ return [];
+ }
+
+ return [templateData];
+ });
+ };
+
+ return commitGroups.map(commitGroup => {
+ return {
+ title: commitGroup.title,
+ sections: generateTemplateData(config.sections, commitGroup.commits),
+ commits: commitGroup.commits.filter(commit =>
+ !flattenedSections.some(section => section.scopes?.includes(commit.scope))),
+ };
+ });
+ });
+
+ const writerOpts = ccWriterOpts(config)
+ .then(writerOpts => {
+ const ccWriterOptsTransform = writerOpts.transform;
+
+ /*
+ * These configuration properties can't be injected directly into the template because
+ * they themselves are templates. Instead, we register them as partials, which allows
+ * them to be evaluated as part of the templates they're used in.
+ */
+ Handlebars.registerPartial("commitUrl", config.commitUrlFormat);
+ Handlebars.registerPartial("compareUrl", config.compareUrlFormat);
+ Handlebars.registerPartial("issueUrl", config.issueUrlFormat);
+
+ /*
+ * Register the partials that allow us to recursively create changelog sections.
+ */
+
+ const notePartial = readFileSync(resolve(__dirname, "./templates/note.hbs"), "utf-8");
+ const noteSectionPartial = readFileSync(resolve(__dirname, "./templates/note-section.hbs"), "utf-8");
+ const commitSectionPartial = readFileSync(resolve(__dirname, "./templates/commit-section.hbs"), "utf-8");
+
+ Handlebars.registerPartial("tf-a-note", notePartial);
+ Handlebars.registerPartial("tf-a-note-section", noteSectionPartial);
+ Handlebars.registerPartial("tf-a-commit-section", commitSectionPartial);
+
+ /*
+ * Override the base templates so that we can generate a changelog that looks at least
+ * similar to the pre-Conventional Commits TF-A changelog.
+ */
+ writerOpts.mainTemplate = readFileSync(resolve(__dirname, "./templates/template.hbs"), "utf-8");
+ writerOpts.headerPartial = readFileSync(resolve(__dirname, "./templates/header.hbs"), "utf-8");
+ writerOpts.commitPartial = readFileSync(resolve(__dirname, "./templates/commit.hbs"), "utf-8");
+ writerOpts.footerPartial = readFileSync(resolve(__dirname, "./templates/footer.hbs"), "utf-8");
+
+ writerOpts.transform = function (commit, context) {
+ /*
+ * Fix up commit trailers, which for some reason are not correctly recognized and
+ * end up showing up in the breaking changes.
+ */
+
+ commit.notes.forEach(note => {
+ const trailers = execa.sync("git", ["interpret-trailers", "--parse"], {
+ input: note.text
+ }).stdout;
+
+ note.text = note.text.replace(trailers, "").trim();
+ });
+
+ return ccWriterOptsTransform(commit, context);
+ };
+
+ return writerOpts;
+ });
+
+ return writerOpts;
+}
+
+module.exports = function (parameter) {
+ const config = parameter || {};
+
+ return Q.all([
+ ccConventionalChangelog(config),
+ ccParserOpts(config),
+ ccRecommendedBumpOpts(config),
+ writerOpts(config)
+ ]).spread((
+ conventionalChangelog,
+ parserOpts,
+ recommendedBumpOpts,
+ writerOpts
+ ) => {
+ if (_.isFunction(parameter)) {
+ return parameter(null, {
+ gitRawCommitsOpts: { noMerges: null },
+ conventionalChangelog,
+ parserOpts,
+ recommendedBumpOpts,
+ writerOpts
+ });
+ } else {
+ return {
+ conventionalChangelog,
+ parserOpts,
+ recommendedBumpOpts,
+ writerOpts
+ };
+ }
+ });
+};
diff --git a/tools/conventional-changelog-tf-a/package.json b/tools/conventional-changelog-tf-a/package.json
new file mode 100644
index 0000000..3dd9877
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "conventional-changelog-tf-a",
+ "version": "2.8.0",
+ "license": "BSD-3-Clause",
+ "private": true,
+ "main": "index.js",
+ "dependencies": {
+ "conventional-changelog-conventionalcommits": "^4.6.1",
+ "execa": "^5.1.1",
+ "lodash": "^4.17.21",
+ "q": "^1.5.1"
+ }
+}
diff --git a/tools/conventional-changelog-tf-a/templates/commit-section.hbs b/tools/conventional-changelog-tf-a/templates/commit-section.hbs
new file mode 100644
index 0000000..86b3335
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/commit-section.hbs
@@ -0,0 +1,17 @@
+{{#if title ~}}
+{{ header }}
+
+{{#if commits.length ~}}
+ {{#each commits ~}}
+ {{#tf-a-mdlist 0}}{{> commit root=@root showScope=../topLevel }}{{/tf-a-mdlist ~}}
+ {{/each}}
+
+{{/if ~}}
+
+{{#if sections.length ~}}
+ {{#each sections ~}}
+ {{#tf-a-mdlist 0}}{{> tf-a-commit-section root=@root header=(tf-a-concat "**" title "**") }}{{/tf-a-mdlist}}
+ {{/each}}
+{{/if ~}}
+
+{{/if}}
diff --git a/tools/conventional-changelog-tf-a/templates/commit.hbs b/tools/conventional-changelog-tf-a/templates/commit.hbs
new file mode 100644
index 0000000..faf264a
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/commit.hbs
@@ -0,0 +1,15 @@
+{{#if scope }}
+ {{~#if showScope }}**{{ scope }}:** {{/if}}
+{{~/if}}
+
+{{~#if subject }}
+ {{~ subject }}
+{{~else}}
+ {{~ header }}
+{{~/if}}
+
+{{~#if hash }} {{#if @root.linkReferences ~}}
+ ([{{ shortHash }}]({{> commitUrl root=@root }}))
+{{~else}}
+ {{~ shortHash }}
+{{~/if}}{{~/if}}
diff --git a/tools/conventional-changelog-tf-a/templates/footer.hbs b/tools/conventional-changelog-tf-a/templates/footer.hbs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/footer.hbs
diff --git a/tools/conventional-changelog-tf-a/templates/header.hbs b/tools/conventional-changelog-tf-a/templates/header.hbs
new file mode 100644
index 0000000..67cb297
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/header.hbs
@@ -0,0 +1,13 @@
+{{#if isPatch~}}
+ ###
+{{~else~}}
+ ##
+{{~/if}} {{#if @root.linkCompare~}}
+ [{{version}}]({{> compareUrl root=@root}})
+{{~else}}
+ {{~version}}
+{{~/if}}
+{{~#if title}} "{{title}}"
+{{~/if}}
+{{~#if date}} ({{date}})
+{{/if}}
diff --git a/tools/conventional-changelog-tf-a/templates/note-section.hbs b/tools/conventional-changelog-tf-a/templates/note-section.hbs
new file mode 100644
index 0000000..f501c96
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/note-section.hbs
@@ -0,0 +1,13 @@
+{{ header }}
+
+{{#if notes.length ~}}
+ {{#each notes ~}}
+ {{#tf-a-mdlist 0}}{{> tf-a-note root=@root showScope=../topLevel }}{{/tf-a-mdlist}}
+ {{/each ~}}
+{{/if ~}}
+
+{{#if sections.length ~}}
+ {{#each sections ~}}
+ {{#tf-a-mdlist 0}}{{> tf-a-note-section root=@root header=(tf-a-concat "**" title "**") }}{{/tf-a-mdlist}}
+ {{/each~}}
+{{/if}}
diff --git a/tools/conventional-changelog-tf-a/templates/note.hbs b/tools/conventional-changelog-tf-a/templates/note.hbs
new file mode 100644
index 0000000..c780ee8
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/note.hbs
@@ -0,0 +1,3 @@
+{{ text }}
+
+**See:** {{#with commit }}{{> commit root=@root showScope=../showScope }}{{/with}}
diff --git a/tools/conventional-changelog-tf-a/templates/template.hbs b/tools/conventional-changelog-tf-a/templates/template.hbs
new file mode 100644
index 0000000..95fb68c
--- /dev/null
+++ b/tools/conventional-changelog-tf-a/templates/template.hbs
@@ -0,0 +1,9 @@
+{{> header }}
+
+{{#each (tf-a-notes noteGroups) ~}}
+{{> tf-a-note-section root=@root header=(tf-a-concat "### ⚠ " title) topLevel=true }}
+{{/each ~}}
+
+{{#each (tf-a-commits commitGroups) ~}}
+{{> tf-a-commit-section root=@root header=(tf-a-concat "### " title) topLevel=true }}
+{{/each ~}}