Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add changelog generator #296

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
98 changes: 98 additions & 0 deletions misc/changelog-generator/changelog_generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
'use strict';

var utils = require("./utils.js");

// get log info (and latest tag if not first release)
const child = require("child_process");
const fs = require("fs");
var output;

const pastTags = child.execSync('git tag').toString('utf-8');
if (pastTags.length) {
const latestTag = child.execSync('git describe --long').toString('utf-8').split('-')[0];

output = child
.execSync(`git log ${latestTag}..HEAD --format=%B%H----DELIMITER----`)
.toString("utf-8");
} else {
output = child
.execSync(`git log --format=%B%H----DELIMITER----`)
.toString("utf-8");
}

if (output.length === 0) {
console.log("No new indicated changes since last tag");
return process.exit(1);
}

// get array of commits since last tag
const commitsArray = output
.split("----DELIMITER----\n")
.map(commit => {
const splitCommit = commit.split("\n");
const sha = splitCommit[1], message = splitCommit[0];
return { sha, message };
})
.filter(commit => Boolean(commit.sha));

// get current version info
const currNotes = fs.readFileSync("../../NEWS.md", "utf-8");
const currVersion = (require("./package.json").version).split('.');

var major = Number(currVersion[0]), minor = Number(currVersion[1]), patch = Number(currVersion[2]);

// sort commits by message tags
var changes = [], features = [], fixes = [];

commitsArray.forEach(commit => {

if (commit.message.toLowerCase().startsWith("breaking-change:")) {
changes = utils.parseMessage("breaking-change:", changes, commit);

} else if (commit.message.toLowerCase().startsWith("feature:")) {
features = utils.parseMessage("feature:", features, commit);

} else if (commit.message.toLowerCase().startsWith("fix:")) {
fixes = utils.parseMessage("fix:", fixes, commit);
}
});

// update package version (following semantic versioning)
if (changes.length) {
major += 1;
minor = 0;
patch = 0;
} else if (features.length) {
minor += 1;
patch = 0;
} else if (fixes.length) {
patch += 1;
}

const newVersion = [String(major), String(minor), String(patch)].join('.');

// format commits into markdown
let newNotes = `# azuremlsdk ${newVersion} (${
new Date().toISOString().split("T")[0]
})\n\n`;

if (changes.length) {
newNotes = utils.formatUpdates(newNotes, `## Breaking changes\n`, changes);
}
if (features.length) {
newNotes = utils.formatUpdates(newNotes, `## New features\n`, features);
}
if (fixes.length) {
newNotes = utils.formatUpdates(newNotes, `## Bug fixes\n`, fixes);
}

// prepend the new release notes to the current file
fs.writeFileSync("./NEWS.md", `${newNotes}${currNotes}`);

// update version in package.json
fs.writeFileSync("./package.json", JSON.stringify({ version: String(newVersion) }, null, 2));

// commit and tag new version
child.execSync('git add .');
child.execSync(`git commit -m "Bump to version ${newVersion}"`);
child.execSync(`git tag -a -m "Tag for version ${newVersion}" version${newVersion}`);
30 changes: 30 additions & 0 deletions misc/changelog-generator/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

### Creating Pull Requests for Inclusion in NEWS.md

If a PR is significant enough to warrant a mention in the next release notes update,
its name should begin with a prefix.
There are three options depending on the PR's
purpose.

* `breaking-change: ` if the change will make the next tag non-backward-compatible
mx-iao marked this conversation as resolved.
Show resolved Hide resolved
* `feature: ` if the change is a major addition that maintains backward-compatibility
* `fix: ` if the change is a bug fix, security patch, or other improvement

If the PR does not begin with one of these prefixes, it WILL NOT be included in
the release notes, so make sure to name important PRs accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diondrapeck should we add a prefix for documentation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point - i'll add a "documentation: " tag that won't affect the version number.

### Generating Notes and Creating a New Tag

To update release notes for and commit a new git tag:

1. Navigate to this directory in while on master branch
2. Run `node changelog_generator.js` on the command line
3. Confirm version was updated in package.json and notes were added to NEWS.md
4. Push to **origin/master** (this can only be done by owners/admins).

The generator follows semantic versioning, so:

* If there have been breaking changes since the last tag, it will increment the 1st (major) version digit by 1 and set the 2nd (minor) and 3rd (patch) to 0. (e.g. 0.6.8 → 1.0.0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is likely that we will have some breaking changes while we are still in preview, but where we do not yet want to increment the major release to 1.0

Copy link
Member Author

@diondrapeck diondrapeck Mar 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do you want to handle those? We could create another tag prefix specifically for this, but which digit should we increment and how?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to just not increment the major version?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we do this: until we do a major release, we keep the major at 0, use minor to indicate breaking changes, and use the two digits of patch to indicate new features and patches.

e.g.

breaking change: 0.6.85 --> 0.7.00
new feature: 0.6.85 --> 0.6.90
patch: 0.6.85 --> 0.6.86

Then, once we do our release, we can do 1.0.0 and do traditional semantic versioning from then on.

@heemanshusuri for his thoughts

* If there have been no breaking changes, but there have been feature additions, it will increment the minor digit by 1 and set the patch digit to 0. (e.g. 0.6.8 → 0.7.0)
* If there have been no breaking changes nor feature additions, but there have been bug fixes, it will increment the patch digit by 1. (e.g. 0.6.8 → 0.6.9)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wanted to align part of the version to the Python SDK version that we use right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe? I remember Paul asking for semantic versioning during our meeting. The issue would then be replacing the logic for generating a version # with whatever scheme the Python SDK is using - which won't really match since not all of our of breaking changes = a Python SDK breaking change. I'm not sure how we align with the Python SDK numbers while maintaining semantic versioning vis a vis our own changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the patch and minor version increments as you have here. For major version can we just keep it at 0 for now?


3 changes: 3 additions & 0 deletions misc/changelog-generator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"version": "0.6.85"
}
34 changes: 34 additions & 0 deletions misc/changelog-generator/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

function parseMessage(message_prefix, commitArray, commit) {
/*
Strips commit message of prefix and pushes to returned array
*/

commitArray.push(
`* ${commit.message.substring(message_prefix)} ([${commit.sha.substring(
0,
6
)}](https://github.com/Azure/azureml-sdk-for-r/commit/${
commit.sha
}))\n`
);

return commitArray
}

function formatUpdates(notes, sectionHeading, messages) {
/*
Format a section with a heading a corresponding commit messages
*/

notes += sectionHeading;
messages.forEach(message => {
notes += message;
});
notes += '\n';

return notes
}

exports.parseMessage = parseMessage;
exports.formatUpdates = formatUpdates;