Skip to content

Commit

Permalink
Update: Major version changelogs include all prereleases (#37)
Browse files Browse the repository at this point in the history
* Update: Major version changelogs include all prereleases

For a new stable release following a prerelease sequence, the changelog
will include the commits for all of the prereleases going back to the
last stable release. For example, given the following release sequence:

- 6.7.8
- 6.8.0
- 7.0.0-alpha.0
- 7.0.0-alpha.1
- 7.0.0

The `7.0.0` changelog will include all changes since 6.8.0, whereas the
`7.0.0-alpha.1` changelog will include only changes since
`7.0.0-alpha.0`.

If we prefer to include all changes going back to the previous stable
release even for prerelease changelogs, that's an easy change and
actually simplifies the logic a bit.

* Update comment about version tags
  • Loading branch information
btmills committed May 8, 2020
1 parent 13d962b commit 3d05dc5
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 4 deletions.
37 changes: 33 additions & 4 deletions lib/release-ops.js
Expand Up @@ -250,18 +250,46 @@ function calculateReleaseFromGitLogs(currentVersion, logs, prereleaseId) {
}

/**
* Gets all changes since the last tag that represents a version.
* Gets the range of commits to include in a changelog.
* If this will be the first stable release following a prerelease sequence,
* all commits going back to the previous stable release are included.
* @param {string[]} tags All prior version tags.
* @param {string} [prereleaseId] The prerelease identifier if this is a prerelease.
* @returns {string} The commit range to include in the changelog.
* @private
*/
function getChangelogCommitRange(tags, prereleaseId) {
let lastTag = null;

// If this will be a stable release after a prerelease...
if (!prereleaseId && semver.prerelease(tags[tags.length - 1])) {
let i = 2;

do {
lastTag = tags[tags.length - i];
i++;
} while (semver.prerelease(lastTag));
} else {
lastTag = tags[tags.length - 1];
}

return lastTag ? `${lastTag}..HEAD` : "";
}

/**
* Gets all changes for this release.
* If this will be the first stable release following a prerelease sequence,
* all changes from all prereleases since the last stable release are included.
* @param {string} [prereleaseId] The prerelease identifier if this is a prerelease.
* @returns {Object} An object containing all the changes since the last version.
* @private
*/
function calculateReleaseInfo(prereleaseId) {

// get most recent tag
// get last version tag
const pkg = getPackageInfo(),
tags = getVersionTags(),
lastTag = tags[tags.length - 1],
commitRange = lastTag ? `${lastTag}..HEAD` : "";
commitRange = getChangelogCommitRange(tags, prereleaseId);

// get log statements
const logs = ShellOps.execSilent(`git log --no-merges --pretty=format:"* %H %s (%an)%n%b" ${commitRange}`).split(/\n/g);
Expand Down Expand Up @@ -427,6 +455,7 @@ module.exports = {
generateRelease,
publishRelease,
calculateReleaseInfo,
getChangelogCommitRange,
calculateReleaseFromGitLogs,
writeChangelog
};
62 changes: 62 additions & 0 deletions tests/lib/release-ops.js
Expand Up @@ -46,6 +46,38 @@ describe("ReleaseOps", () => {

});

describe("getChangelogCommitRange", () => {

it("returns an empty string when there are no prior releases", () => {
const tags = [];
const range = ReleaseOps.getChangelogCommitRange(tags);

assert.strictEqual(range, "");
});

it("finds the most recent tag for normal releases", () => {
const tags = ["1.0.0", "1.0.1"];
const range = ReleaseOps.getChangelogCommitRange(tags);

assert.strictEqual(range, "1.0.1..HEAD");
});

it("finds the most recent tag for prereleases", () => {
const tags = ["1.0.0", "1.0.1", "2.0.0-alpha.0", "2.0.0-alpha.1"];
const range = ReleaseOps.getChangelogCommitRange(tags, "beta");

assert.strictEqual(range, "2.0.0-alpha.1..HEAD");
});

it("finds the last stable tag for a new stable following prereleases", () => {
const tags = ["1.0.0", "1.0.1", "2.0.0-alpha.0", "2.0.0-rc.0"];
const range = ReleaseOps.getChangelogCommitRange(tags);

assert.strictEqual(range, "1.0.1..HEAD");
});

});

describe("calculateReleaseFromGitLogs()", () => {

it("should create a patch release when only bug fixes are present", () => {
Expand Down Expand Up @@ -252,6 +284,36 @@ describe("ReleaseOps", () => {
});
});

it("should create the next stable release following a prerelease", () => {
const logs = [
"* 7e8a43b2b6350e13a61858f33b4099c964cdd758 Breaking: Remove API (githubhandle)",
"* 0c07d6ac037076557e34d569cd0290e529b3318a Docs: Something else (Committer Name)",
"* 196d32dbfb7cb37b886e7c4ba0adff499c6b26ac Fix: Something else (Abc D. Efg)"
],
releaseInfo = ReleaseOps.calculateReleaseFromGitLogs("2.0.0-rc.0", logs);

assert.deepStrictEqual(releaseInfo, {
version: "2.0.0",
type: "major",
changelog: {
fix: [
"* [`196d32d`](https://github.com/eslint/eslint-release/commit/196d32dbfb7cb37b886e7c4ba0adff499c6b26ac) Fix: Something else (Abc D. Efg)"
],
docs: [
"* [`0c07d6a`](https://github.com/eslint/eslint-release/commit/0c07d6ac037076557e34d569cd0290e529b3318a) Docs: Something else (Committer Name)"
],
breaking: [
"* [`7e8a43b`](https://github.com/eslint/eslint-release/commit/7e8a43b2b6350e13a61858f33b4099c964cdd758) Breaking: Remove API (githubhandle)"
]
},
rawChangelog: [
"* [`7e8a43b`](https://github.com/eslint/eslint-release/commit/7e8a43b2b6350e13a61858f33b4099c964cdd758) Breaking: Remove API (githubhandle)",
"* [`0c07d6a`](https://github.com/eslint/eslint-release/commit/0c07d6ac037076557e34d569cd0290e529b3318a) Docs: Something else (Committer Name)",
"* [`196d32d`](https://github.com/eslint/eslint-release/commit/196d32dbfb7cb37b886e7c4ba0adff499c6b26ac) Fix: Something else (Abc D. Efg)"
].join("\n")
});
});

it("should gracefully handle unformatted commit messages", () => {
const logs = [
"* 70222e95932d3a391ac5717252e13b478d686ba9 0.4.0-alpha.4 (Nicholas C. Zakas)",
Expand Down

0 comments on commit 3d05dc5

Please sign in to comment.