Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,15 @@ Just as in the **subject**, use the imperative, present tense: "change" not "cha
The body should include the motivation for the change and contrast this with previous behavior.

### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
The footer should contain any information about **Breaking Changes** or **Deprecations** and
is also the place to reference GitHub issues that this commit **Closes**.

**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines.
The rest of the commit message is then used for this.

**Deprecations** should start with the word `DEPRECATED:`. The rest of the commit message will be
used as content for the note.

A detailed explanation can be found in this [document][commit-message-format].

## <a name="cla"></a> Signing the CLA
Expand Down
8 changes: 8 additions & 0 deletions tools/release/changelog-root-template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ _Breaking changes:_
* {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{text}}
{{/each}}

{{/if}}
{{#if deprecations.length}}
_Deprecations:_

{{#each deprecations}}
* {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{text}}
{{/each}}

{{/if}}
| | |
| ---------- | --------------------- |
Expand Down
39 changes: 35 additions & 4 deletions tools/release/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ const conventionalChangelog = require('conventional-changelog');
const changelogCompare = require('conventional-changelog-writer/lib/util');
const merge2 = require('merge2');

/**
* Maps a commit note to a string that will be used to match notes of the
* given type in commit messages.
*/
const enum CommitNote {
Deprecation = 'DEPRECATED',
BreakingChange = 'BREAKING CHANGE',
}

/** Interface that describes a package in the changelog. */
interface ChangelogPackage {
commits: any[];
breakingChanges: any[];
deprecations: any[];
}

/** Hardcoded order of packages shown in the changelog. */
Expand Down Expand Up @@ -41,6 +51,7 @@ export async function promptAndGenerateChangelog(changelogPath: string) {
* @param releaseName Name of the release that should show up in the changelog.
*/
export async function prependChangelogFromLatestTag(changelogPath: string, releaseName: string) {
const angularPresetWriterOptions = await require('conventional-changelog-angular/writer-opts');
const outputStream: Readable = conventionalChangelog(
/* core options */ {preset: 'angular'},
/* context options */ {title: releaseName},
Expand All @@ -50,8 +61,9 @@ export async function prependChangelogFromLatestTag(changelogPath: string, relea
// name from the commit message.
headerPattern: /^(\w*)(?:\((?:([^/]+)\/)?(.*)\))?: (.*)$/,
headerCorrespondence: ['type', 'package', 'scope', 'subject'],
noteKeywords: [CommitNote.BreakingChange, CommitNote.Deprecation],
},
/* writer options */ createChangelogWriterOptions(changelogPath));
/* writer options */ createChangelogWriterOptions(changelogPath, angularPresetWriterOptions));

// Stream for reading the existing changelog. This is necessary because we want to
// actually prepend the new changelog to the existing one.
Expand Down Expand Up @@ -93,7 +105,7 @@ export async function promptChangelogReleaseName(): Promise<string> {
* build the changelog from last major version to master's HEAD when a new major version is being
* published from the "master" branch.
*/
function createChangelogWriterOptions(changelogPath: string) {
function createChangelogWriterOptions(changelogPath: string, presetWriterOptions: any) {
const existingChangelogContent = readFileSync(changelogPath, 'utf8');
const commitSortFunction = changelogCompare.functionify(['type', 'scope', 'subject']);
const allPackages = [...orderedChangelogPackages, ...excludedChangelogPackages];
Expand All @@ -105,6 +117,14 @@ function createChangelogWriterOptions(changelogPath: string) {
mainTemplate: readFileSync(join(__dirname, 'changelog-root-template.hbs'), 'utf8'),
commitPartial: readFileSync(join(__dirname, 'changelog-commit-template.hbs'), 'utf8'),

// Overwrites the conventional-changelog-angular preset transform function. This is necessary
// because the Angular preset changes every commit note to a breaking change note. Since we
// have a custom note type for deprecations, we need to keep track of the original type.
transform: (commit, context) => {
commit.notes.forEach(n => n.type = n.title);
return presetWriterOptions.transform(commit, context);
},

// Specify a writer option that can be used to modify the content of a new changelog section.
// See: conventional-changelog/tree/master/packages/conventional-changelog-writer
finalizeContext: (context: any) => {
Expand Down Expand Up @@ -139,11 +159,21 @@ function createChangelogWriterOptions(changelogPath: string) {
const type = getTypeOfCommitGroupDescription(group.title);

if (!packageGroups[packageName]) {
packageGroups[packageName] = {commits: [], breakingChanges: []};
packageGroups[packageName] = {commits: [], breakingChanges: [], deprecations: []};
}
const packageGroup = packageGroups[packageName];

packageGroup.breakingChanges.push(...commit.notes);
// Collect all notes of the commit. Either breaking change or deprecation notes.
commit.notes.forEach(n => {
if (n.type === CommitNote.Deprecation) {
packageGroup.deprecations.push(n);
} else if (n.type === CommitNote.BreakingChange) {
packageGroup.breakingChanges.push(n);
} else {
throw Error(`Found commit note that is not known: ${JSON.stringify(n, null, 2)}`);
}
});

packageGroup.commits.push({...commit, type});
});
});
Expand All @@ -159,6 +189,7 @@ function createChangelogWriterOptions(changelogPath: string) {
title: pkgName,
commits: packageGroup.commits.sort(commitSortFunction),
breakingChanges: packageGroup.breakingChanges,
deprecations: packageGroup.deprecations,
};
});

Expand Down