Skip to content

Instantly share code, notes, and snippets.

@danielmcq
Created April 20, 2020 14:56
Show Gist options
  • Select an option

  • Save danielmcq/06c43fa61fda0c5a26c6d479dbedbe20 to your computer and use it in GitHub Desktop.

Select an option

Save danielmcq/06c43fa61fda0c5a26c6d479dbedbe20 to your computer and use it in GitHub Desktop.
Update changelog based off of version tags. Useful as the `version` script in an npm package.
#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const { promisify } = require("util");
const { spawn } = require("child_process");
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const versionPattern = "\\d+\\.\\d+\\.\\d";
const versionRegEx = RegExp(versionPattern, "i");
const versionSectionRegEx = RegExp(`^## ${versionPattern}\\w?.*$`);
module.exports = updateChangelogWithNewVersionInformation;
module.exports.ensureNoExistingVersionSections = ensureNoExistingVersionSections;
module.exports.gitCommits = gitCommits;
if (require.main === module) {
const changelogFilename = `${__dirname}/../docs/CHANGELOG.md`;
const changelogFilePath = path.resolve(changelogFilename);
const version = process.argv[2] || process.env["npm_package_version"];
if (!version) {
throw new Error("Version not specified. Set environment variable 'npm_package_version' or pass as argument.");
}
return updateChangelogWithNewVersionInformation(changelogFilePath, version)
.then(() => process.exit(0))
.catch((err) => {
process.stderr.write(`${err.message}\n`);
process.exit(1);
});
}
function updateChangelogWithNewVersionInformation (filepath, version) {
if (!version.match(versionRegEx)) {
throw new Error("Invalid version specified.");
}
return readFile(filepath, "utf8")
.then((contents) => contents.split("\n"))
.then((lines) => ensureNoExistingVersionSections(filepath, version, lines))
.then((lines) => gitCommits().then((commits) => ({ lines, commits })))
.then(({ lines, commits }) => {
const firstVersionSectionPosition = lines.findIndex((line) => line.match(versionSectionRegEx));
let newVersionSection = `## ${version}\n`;
if (firstVersionSectionPosition < 0) newVersionSection = `\n${newVersionSection}`;
lines.splice(firstVersionSectionPosition, 0, newVersionSection, commits);
return lines;
})
.then((lines) => lines.join("\n"))
.then((lines) => writeFile(filepath, lines, "utf8"));
}
function ensureNoExistingVersionSections (filepath, version, lines) {
lines.filter((line) => line.match(versionSectionRegEx))
.forEach((sectionLine) => {
const [sectionVersion] = sectionLine.match(versionRegEx);
if (sectionVersion === version) throw new Error(`Version '${sectionVersion}' already exists in '${filepath}'`);
});
return lines;
}
function gitCommits () {
return new Promise((resolve,reject) => {
const gitProcess = spawn(
"git",
[
"log",
"--oneline",
"$(git describe --tags --abbrev=0 @^)..@",
"--oneline",
],
{
shell : true,
stdio : [process.stdin, "pipe", "pipe"],
},
);
const stdout = [];
const stderr = [];
gitProcess.stdout && gitProcess.stdout.on("data", (data) => stdout.push(data.toString()));
gitProcess.stderr && gitProcess.stderr.on("data", (data) => stderr.push(data.toString()));
gitProcess.on("close", (code) => {
if (code === 0) resolve(`${stdout.join("").trim()}\n`);
else reject(stderr);
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment