From e2a1e280f6dfbb4f11ad541dec9541cdbf760ab1 Mon Sep 17 00:00:00 2001 From: Dmitriy Mozgovoy Date: Mon, 9 Jan 2023 15:24:02 +0200 Subject: [PATCH] chore(ci): improved contributors & PRs sections generator; (#5453) --- bin/contributors.js | 128 +++++++++++++++++---- bin/helpers/colorize.js | 14 +++ bin/injectContributorsList.js | 49 +++++--- package-lock.json | 209 ++++++++++++++++++---------------- package.json | 1 + templates/contributors.hbs | 4 +- templates/prs.hbs | 13 +++ 7 files changed, 279 insertions(+), 139 deletions(-) create mode 100644 bin/helpers/colorize.js create mode 100644 templates/prs.hbs diff --git a/bin/contributors.js b/bin/contributors.js index ac2bd5ed12..149c1c565a 100644 --- a/bin/contributors.js +++ b/bin/contributors.js @@ -3,6 +3,7 @@ import util from "util"; import cp from "child_process"; import Handlebars from "handlebars"; import fs from "fs/promises"; +import {colorize} from "./helpers/colorize.js"; const exec = util.promisify(cp.exec); @@ -20,6 +21,8 @@ const getUserFromCommit = ((commitCache) => async (sha) => { return commitCache[sha]; } + console.log(colorize()`fetch github commit info (${sha})`); + const {data} = await axios.get(`https://api.github.com/repos/axios/axios/commits/${sha}`); return commitCache[sha] = { @@ -32,6 +35,20 @@ const getUserFromCommit = ((commitCache) => async (sha) => { } })({}); +const getIssueById = ((cache) => async (id) => { + if(cache[id] !== undefined) { + return cache[id]; + } + + try { + const {data} = await axios.get(`https://api.github.com/repos/axios/axios/issues/${id}`); + + return cache[id] = data; + } catch (err) { + return null; + } +})({}); + const getUserInfo = ((userCache) => async (userEntry) => { const {email, commits} = userEntry; @@ -39,11 +56,11 @@ const getUserInfo = ((userCache) => async (userEntry) => { return userCache[email]; } - console.log(`fetch github user info [${userEntry.name}]`); + console.log(colorize()`fetch github user info [${userEntry.name}]`); return userCache[email] = { ...userEntry, - ...await getUserFromCommit(commits[0]) + ...await getUserFromCommit(commits[0].hash) } })({}); @@ -76,16 +93,24 @@ const deduplicate = (authors) => { return combined; } -const getReleaseInfo = async (version, useGithub) => { - version = 'v' + version.replace(/^v/, ''); +const getReleaseInfo = ((releaseCache) => async (tag) => { + if(releaseCache[tag] !== undefined) { + return releaseCache[tag]; + } + + const isUnreleasedTag = !tag; - const releases = JSON.parse((await exec( + const version = 'v' + tag.replace(/^v/, ''); + + const command = isUnreleasedTag ? + `npx auto-changelog --unreleased-only --stdout --commit-limit false --template json` : `npx auto-changelog ${ - version ? '--starting-version ' + version + ' --ending-version ' + version: '' - } --stdout --commit-limit false --template json`)).stdout - ); + version ? '--starting-version ' + version + ' --ending-version ' + version : '' + } --stdout --commit-limit false --template json`; + + const release = JSON.parse((await exec(command)).stdout)[0]; - for(const release of releases) { + if(release) { const authors = {}; const commits = [ @@ -94,15 +119,30 @@ const getReleaseInfo = async (version, useGithub) => { ...release.merges.map(fix => fix.commit) ].filter(Boolean); - for(const {hash, author, email, insertions, deletions} of commits) { + const commitMergeMap = {}; + + for(const merge of release.merges) { + commitMergeMap[merge.commit.hash] = merge.id; + } + + for (const {hash, author, email, insertions, deletions} of commits) { const entry = authors[email] = (authors[email] || { name: author, + prs: [], email, commits: [], insertions: 0, deletions: 0 }); - entry.commits.push(hash); + entry.commits.push({hash}); + + let pr; + + if((pr = commitMergeMap[hash])) { + entry.prs.push(pr); + } + + console.log(colorize()`Found commit [${hash}]`); entry.displayName = entry.name || author || entry.login; @@ -111,22 +151,63 @@ const getReleaseInfo = async (version, useGithub) => { entry.insertions += insertions; entry.deletions += deletions; entry.points = entry.insertions + entry.deletions; - entry.isBot = entry.type === "Bot" } - for(const [email, author] of Object.entries(authors)) { - authors[email] = await getUserInfo(author); + for (const [email, author] of Object.entries(authors)) { + const entry = authors[email] = await getUserInfo(author); + + entry.isBot = entry.type === "Bot"; } - release.authors = Object.values(deduplicate(authors)).sort((a, b) => b.points - a.points); + release.authors = Object.values(deduplicate(authors)) + .sort((a, b) => b.points - a.points); + release.allCommits = commits; } - return releases; + releaseCache[tag] = release; + + return release; +})({}); + +const renderContributorsList = async (tag, template) => { + const release = await getReleaseInfo(tag); + + const compile = Handlebars.compile(String(await fs.readFile(template))) + + const content = compile(release); + + return removeExtraLineBreaks(cleanTemplate(content)); } -const renderContributorsList = async (version, useGithub = false, template) => { - const release = (await getReleaseInfo(version, useGithub))[0]; +const renderPRsList = async (tag, template, {comments_threshold= 5, awesome_threshold= 5, label = 'add_to_changelog'} = {}) => { + const release = await getReleaseInfo(tag); + + const prs = {}; + + for(const merge of release.merges) { + const pr = await getIssueById(merge.id); + + if (pr && pr.labels.find(({name})=> name === label)) { + const {reactions, body} = pr; + prs[pr.number] = pr; + pr.isHot = pr.comments > comments_threshold; + const points = reactions['+1'] + + reactions['hooray'] + reactions['rocket'] + reactions['heart'] + reactions['laugh'] - reactions['-1']; + + pr.isAwesome = points > awesome_threshold; + + let match; + + pr.messages = []; + + if (body && (match = /```+changelog(.+)?```/gms.exec(body)) && match[1]) { + pr.messages.push(match[1]); + } + } + } + + release.prs = Object.values(prs); const compile = Handlebars.compile(String(await fs.readFile(template))) @@ -135,6 +216,15 @@ const renderContributorsList = async (version, useGithub = false, template) => { return removeExtraLineBreaks(cleanTemplate(content)); } +const getTagRef = async (tag) => { + try { + return (await exec(`git show-ref --tags "refs/tags/${tag}"`)).stdout.split(' ')[0]; + } catch(e) { + } +} + export { - renderContributorsList + renderContributorsList, + renderPRsList, + getTagRef } diff --git a/bin/helpers/colorize.js b/bin/helpers/colorize.js new file mode 100644 index 0000000000..193a0a4914 --- /dev/null +++ b/bin/helpers/colorize.js @@ -0,0 +1,14 @@ +import chalk from 'chalk'; + +export const colorize = (...colors)=> { + if(!colors.length) { + colors = ['green', 'magenta', 'cyan', 'blue', 'yellow', 'red']; + } + + const colorsCount = colors.length; + + return (strings, ...values) => { + const {length} = values; + return strings.map((str, i) => i < length ? str + chalk[colors[i%colorsCount]].bold(values[i]) : str).join(''); + } +} diff --git a/bin/injectContributorsList.js b/bin/injectContributorsList.js index d1666d61fa..92accf7da6 100644 --- a/bin/injectContributorsList.js +++ b/bin/injectContributorsList.js @@ -1,27 +1,31 @@ import fs from 'fs/promises'; import path from 'path'; -import {renderContributorsList} from './contributors.js'; +import {renderContributorsList, getTagRef, renderPRsList} from './contributors.js'; import asyncReplace from 'string-replace-async'; import {fileURLToPath} from "url"; +import {colorize} from "./helpers/colorize.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const injectContributors = async (infile, injector) => { - console.log(`Checking contributors sections in ${infile}`); +const CONTRIBUTORS_TEMPLATE = path.resolve(__dirname, '../templates/contributors.hbs'); +const PRS_TEMPLATE = path.resolve(__dirname, '../templates/prs.hbs'); + +const injectSection = async (name, contributorsRE, injector, infile = '../CHANGELOG.md') => { + console.log(colorize()`Checking ${name} sections in ${infile}`); infile = path.resolve(__dirname, infile); const content = String(await fs.readFile(infile)); const headerRE = /^#+\s+\[([-_\d.\w]+)].+?$/mig; - const contributorsRE = /^\s*### Contributors/mi; let tag; let index = 0; + let isFirstTag = true; const newContent = await asyncReplace(content, headerRE, async (match, nextTag, offset) => { const releaseContent = content.slice(index, offset); - const hasContributorsSection = contributorsRE.test(releaseContent); + const hasSection = contributorsRE.test(releaseContent); const currentTag = tag; @@ -29,20 +33,25 @@ const injectContributors = async (infile, injector) => { index = offset + match.length; if(currentTag) { - if (hasContributorsSection) { - console.log(`[${currentTag}]: ✓ OK`); + if (hasSection) { + console.log(colorize()`[${currentTag}]: ✓ OK`); } else { - console.log(`[${currentTag}]: ❌ MISSED`); - console.log(`Generating contributors list...`); + const target = isFirstTag && (!await getTagRef(currentTag)) ? '' : currentTag; + + console.log(colorize()`[${currentTag}]: ❌ MISSED` + (!target ? ' (UNRELEASED)' : '')); + + isFirstTag = false; - const section = await injector(currentTag); + console.log(`Generating section...`); - console.log(`\nRENDERED CONTRIBUTORS LIST [${currentTag}]:`); + const section = await injector(target); + + console.log(colorize()`\nRENDERED SECTION [${name}] for [${currentTag}]:`); console.log('-------------BEGIN--------------\n'); console.log(section); console.log('--------------END---------------\n'); - return section + match; + return section + '\n' + match; } } @@ -52,8 +61,14 @@ const injectContributors = async (infile, injector) => { await fs.writeFile(infile, newContent); } - -await injectContributors( - '../CHANGELOG.md', - (tag) => renderContributorsList(tag, true, path.resolve(__dirname, '../templates/contributors.hbs') - )); +await injectSection( + 'PRs', + /^\s*### PRs/mi, + (tag) => !tag && renderPRsList(tag, PRS_TEMPLATE, {awesome_threshold: 5, comments_threshold: 7}), +); + +await injectSection( + 'contributors', + /^\s*### Contributors/mi, + (tag) => renderContributorsList(tag, CONTRIBUTORS_TEMPLATE) +); diff --git a/package-lock.json b/package-lock.json index 6f39184b70..3fc8f47598 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "abortcontroller-polyfill": "^1.7.3", "auto-changelog": "^2.4.0", "body-parser": "^1.20.0", + "chalk": "^5.2.0", "coveralls": "^3.1.1", "cross-env": "^7.0.3", "dev-null": "^0.1.1", @@ -3961,7 +3962,7 @@ "node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4615,6 +4616,22 @@ "js-tokens": "^3.0.2" } }, + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", @@ -5228,18 +5245,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/boxen/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -5836,19 +5841,15 @@ } }, "node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/change-case": { @@ -12335,7 +12336,7 @@ "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "dependencies": { "ansi-regex": "^2.0.0" @@ -12905,18 +12906,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/inquirer/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -15012,6 +15001,22 @@ "node": ">=0.12" } }, + "node_modules/maxmin/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/maxmin/node_modules/gzip-size": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", @@ -16884,18 +16889,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/ora/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/ora/node_modules/is-unicode-supported": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", @@ -19493,6 +19486,22 @@ "maxmin": "^2.1.0" } }, + "node_modules/rollup-plugin-bundle-size/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -21126,7 +21135,7 @@ "node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "engines": { "node": ">=0.8.0" @@ -22215,18 +22224,6 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/update-notifier/node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -26964,7 +26961,7 @@ "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "ansi-wrap": { @@ -27508,6 +27505,21 @@ "chalk": "^1.1.3", "esutils": "^2.0.2", "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } } }, "babel-generator": { @@ -28009,12 +28021,6 @@ "integrity": "sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==", "dev": true }, - "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true - }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -28485,17 +28491,10 @@ } }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true }, "change-case": { "version": "4.1.2", @@ -33776,7 +33775,7 @@ "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -34201,12 +34200,6 @@ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true }, - "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true - }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -35827,6 +35820,19 @@ "pretty-bytes": "^3.0.0" }, "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, "gzip-size": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", @@ -37333,12 +37339,6 @@ "ieee754": "^1.2.1" } }, - "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true - }, "is-unicode-supported": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", @@ -39327,6 +39327,21 @@ "requires": { "chalk": "^1.1.3", "maxmin": "^2.1.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } } }, "rollup-plugin-terser": { @@ -40663,7 +40678,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true }, "supports-preserve-symlinks-flag": { @@ -41499,12 +41514,6 @@ "xdg-basedir": "^5.1.0" }, "dependencies": { - "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true - }, "import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", diff --git a/package.json b/package.json index a6fc90c920..d59317d66d 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "abortcontroller-polyfill": "^1.7.3", "auto-changelog": "^2.4.0", "body-parser": "^1.20.0", + "chalk": "^5.2.0", "coveralls": "^3.1.1", "cross-env": "^7.0.3", "dev-null": "^0.1.1", diff --git a/templates/contributors.hbs b/templates/contributors.hbs index d000445f6f..c95fc5cbc4 100644 --- a/templates/contributors.hbs +++ b/templates/contributors.hbs @@ -1,15 +1,13 @@ {{#if authors}} - ### Contributors to this release {{#each authors}} {{#unless isBot}} {{#if login}} - - {{#if avatar_url}}![avatar]({{avatar_url_sm}}){{/if}} [{{displayName}}]({{html_url}}) + - {{#if avatar_url}}![avatar]({{avatar_url_sm}}){{/if}} [{{displayName}}]({{html_url}} "+{{insertions}}/-{{deletions}} ({{#each prs}}#{{this}} {{/each}})") {{else}} - {{displayName}} {{/if}} {{/unless}} {{/each}} - {{/if}} diff --git a/templates/prs.hbs b/templates/prs.hbs new file mode 100644 index 0000000000..9713a9ce30 --- /dev/null +++ b/templates/prs.hbs @@ -0,0 +1,13 @@ +{{#if prs}} +### PRs +{{#each prs}} + - {{title}} ( [#{{number}}]({{pull_request.url}}) ){{#if isHot}} 🔥{{/if}}{{#if isHot}} 🚀{{/if}} + {{#if messages}} + {{#each messages}} + ``` + {{this}} + ``` + {{/each}} + {{/if}} +{{/each}} +{{/if}}