diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f9b46f4b6c4214..be4ef95cd37455 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,20 +66,7 @@ Regularly, a new release of [mdn-browser-compat-data](https://www.npmjs.com/pack This step will trigger Travis to publish to npm automatically (see our [.travis.yml file](https://github.com/mdn/browser-compat-data/blob/master/.travis.yml)). 6. Check [Travis CI](https://travis-ci.org/mdn/browser-compat-data) again for the v0.0.43 build and also check [mdn-browser-compat-data on npm](https://www.npmjs.com/package/mdn-browser-compat-data) to see if `0.0.43` shows up correctly once Travis has finished its work. 7. Notify the [#mdndev](irc://irc.mozilla.org/mdndev) IRC channel on irc.mozilla.org about the new release and coordinate with jwhitlock or rjohnson a deployment of the new package to the MDN site. - 8. Create a [new release on GitHub](https://github.com/mdn/browser-compat-data/releases) and write down release notes. Mention notable changes if non-data updates happened (schema, test, or infrastructure changes). Also record current statistics such as total number of contributors and stargazers, as well as the total number of features by running this node command in the main dir: - `node -p "bcd=require('.');i=0;JSON.parse(JSON.stringify(bcd),function(k,v){if(k==='__compat'){i++;}return i;})"` - - Release notes template: - ``` -Notable changes: -- [TK text] - -Statistics: -- [TK number] contributors have changed [TK number] files with [TK number] additions and [TK number] deletions in [TK number] commits [TK link to diff between version tags] -- [TK number] total contributors -- [TK number] total stargazers -- [TK number] total features -``` + 8. Create a new [release on GitHub](https://github.com/mdn/browser-compat-data/releases) by running `npm run release-notes -- v0.0.43`). ## Licensing diff --git a/package.json b/package.json index cc6c56fdf6947c..a9057789e9a564 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "scripts": { "confluence": "node ./node_modules/mdn-confluence/main/generate.es6.js --output-dir=. --bcd-module=./index.js", "lint": "node test/lint", + "release-notes": "node test/release-notes", "render": "node test/render", "show-errors": "npm test 1> /dev/null", "test": "npm run lint" diff --git a/test/release-notes.js b/test/release-notes.js new file mode 100644 index 00000000000000..1398dca1529532 --- /dev/null +++ b/test/release-notes.js @@ -0,0 +1,144 @@ +const { execSync } = require('child_process'); +const http = require('https'); +const readline = require('readline'); + +const bcd = require('..'); + +const { argv } = require('yargs') + .command('$0 ', 'Initiate a release of this package on GitHub', (yargs) => { + yargs.positional('version-tag', { + describe: 'the version tag to generate release notes for', + type: 'string', + }); + }); + +const getJSON = (url) => new Promise((resolve, reject) => http.get(url, { headers: { 'User-Agent': 'bcd-release-script' } }, (response) => { + let body = ''; + response.on('data', (data) => { + body += data; + }); + response.on('error', error => reject(error)); + response.on('end', () => { + resolve(JSON.parse(body)); + }); +})); + +const question = (query) => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + return new Promise(resolve => rl.question(query, resolve)) + .then((response) => { + rl.close(); + return response; + }); +} + +const confirm = (str) => !['n', 'no'].includes(str.toLowerCase()); + +const prompt = async (questions) => { + const results = {}; + for (const q of questions) { + const options = q.type === confirm ? '(Y/n) ' : ''; + results[q.name] = await question(`${q.message} ${options}`).then(q.type); + } + return results; +}; + +const stargazers = () => getJSON('https://api.github.com/repos/mdn/browser-compat-data').then(json => json.stargazers_count); + +const stats = (version, previousVersion) => { + // Get just the diff stats summary + const diff = execSync(`git diff --shortstat ${previousVersion}...${version}`, { encoding: 'utf8' }) + .trim(); + // Extract the numbers from a line like this: + // 50 files changed, 1988 insertions(+), 2056 deletions(-) + const [, changed, insertions, deletions] = diff.match(/(\d+) files* changed, (\d+) insertions*\(\+\), (\d+) deletions*/); + + // Get the number of commits + const commits = execSync(`git rev-list --count ${previousVersion}...${version}`, { encoding: 'utf8' }) + .trim(); + + return { + commits, + changed, + insertions, + deletions, + }; +}; + +const contributors = (version, previousVersion) => prompt([ + { + name: 'releaseContributors', + type: Number, + message: `Find "contributors" at https://github.com/mdn/browser-compat-data/compare/${previousVersion}...${version}\nHow many people have contributed to this release?`, + }, + { + name: 'totalContributors', + type: Number, + message: 'Find "contributors" at https://github.com/mdn/browser-compat-data/\nHow many people have contributed to browser-compat-data overall?', + }, +]); + +const notableChanges = async () => { + const { result } = await prompt([ + { + name: 'result', + message: 'Does this release contain any schema, test, or infrastructure changes?', + type: confirm, + }, + ]); + + if (!result) { + return 'None'; + } + return 'REPLACE ME WITH ACTUAL RELEASE NOTES'; +}; + +const countFeatures = () => { + let count = 0; + JSON.parse(JSON.stringify(bcd), (k) => { + if (k === '__compat' ) { + count++; + } + return count; + }); + return count; +}; + +const makeURL = (version, body) => { + const baseURL = 'https://github.com/mdn/browser-compat-data/releases/new'; + + // Adhering to RFC 3986 makes the full link clickable in Terminal.app + const encodedBody = encodeURIComponent(body).replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16)}`); + + return `${baseURL}?title=${version}&tag=${version}&prerelease=true&body=${encodedBody}`; +}; + +const main = async () => { + const version = argv.versionTag; + const previousVersion = execSync(`git describe --abbrev=0 ${version}^`, { encoding: 'utf8' }).trim(); + + const { commits, changed, insertions, deletions } = stats(version, previousVersion); + + const { releaseContributors, totalContributors } = await contributors(version, previousVersion); + const changeMessage = await notableChanges(); + const stars = await stargazers(); + const features = countFeatures(); + + const body = `\ +**Notable changes** +- ${changeMessage} + +**Statistics** +- ${features} total features +- ${releaseContributors} contributors have changed ${changed} files with ${insertions} additions and ${deletions} deletions in ${commits} commits (https://github.com/mdn/browser-compat-data/compare/${previousVersion}...${version}) +- ${totalContributors} total contributors +- ${stars} total stargazers`; + + console.log('\n\x1b[1mOpen this URL in a browser:\x1b[0m'); + console.log(makeURL(version, body)); +}; + +main();