Skip to content
Merged
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
161 changes: 71 additions & 90 deletions scripts/changelog.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict'

/*
Usage:

Expand All @@ -13,107 +14,87 @@ the result to the changelog.
*/
const execSync = require('child_process').execSync
const branch = process.argv[2] || 'origin/latest'
const log = execSync(`git log --reverse --pretty='format:%h %H%d %s (%aN)%n%b%n---%n' ${branch}...`)
const log = execSync(`git log --reverse --pretty='format:%h' ${branch}...`)
.toString()
.split(/\n/)

main()

function shortname (url) {
const matched =
url.match(/https:\/\/github\.com\/([^/]+\/[^/]+)\/(?:pull|issues)\/(\d+)/) ||
url.match(/https:\/\/(npm\.community)\/t\/(?:[^/]+\/)(\d+)/)
if (!matched) {
return false
function printCommit (c) {
console.log(`* [\`${c.hash}\`](${c.url})`)
for (const pr of c.prs) {
console.log(` [#${pr.number}](${pr.url})`)
}
const repo = matched[1]
const id = matched[2]
if (repo !== 'npm/cli') {
return `${repo}#${id}`
} else {
return `#${id}`
console.log(` ${c.message}`)
// no credit for deps commits
if (!c.message.startsWith('deps')) {
for (const user of c.credit) {
console.log(` ([${user.name}](${user.url}))`)
}
}
}

function printCommit (c) {
console.log(`* [\`${c.shortid}\`](https://github.com/npm/cli/commit/${c.fullid})`)
if (c.fixes.length) {
for (const fix of c.fixes) {
const label = shortname(fix)
if (label) {
console.log(` [${label}](${fix})`)
const main = async () => {
const query = `
fragment commitCredit on GitObject {
... on Commit {
messageHeadline
url
authors (first:10) {
nodes {
user {
login
url
}
email
name
}
}
associatedPullRequests (first:10) {
nodes {
number
url
merged
}
}
}
}
} else if (c.prurl) {
const label = shortname(c.prurl)
if (label) {
console.log(` [${label}](${c.prurl})`)
} else {
console.log(` [#](${c.prurl})`)

query {
repository (owner:"npm", name:"cli") {
${log.map((sha) => `_${sha}: object (expression: "${sha}") {
...commitCredit
}`).join('\n')}
}
}
}
const msg = c.message
.replace(/^\s+/gm, '')
.replace(/^[-a-z]+: /, '')
.replace(/^/gm, ' ')
.replace(/^ {2}Reviewed-by: @.*/gm, '')
.replace(/\n$/, '')
// backtickify package@version
.replace(/^(\s*@?[^@\s]+@\d+[.]\d+[.]\d+)\b(\s*\S)/g, '$1:$2')
.replace(/((?:\b|@)[^@\s]+@\d+[.]\d+[.]\d+)\b/g, '`$1`')
// linkify commitids
.replace(/\b([a-f0-9]{7,8})\b/g, '[`$1`](https://github.com/npm/cli/commit/$1)')
console.log(msg)
// don't assign credit for dep updates
if (!/^ {2}`[^`]+@\d+\.\d+\.\d+[^`]*`:?$/m.test(msg)) {
if (c.credit) {
c.credit.forEach(function (credit) {
console.log(` ([@${credit}](https://github.com/${credit}))`)
})
} else {
console.log(` ([@${c.author}](https://github.com/${c.author}))`)
`

const response = execSync(`gh api graphql -f query='${query}'`).toString()
const body = JSON.parse(response)

for (const [hash, data] of Object.entries(body.data.repository)) {
const commit = {
hash: hash.slice(1), // remove leading _
url: data.url,
message: data.messageHeadline.replace(/\(#\d+\)$/, ''),
prs: data.associatedPullRequests.nodes.filter((pull) => pull.merged),
credit: data.authors.nodes.map((author) => {
if (author.user && author.user.login) {
return {
name: `@${author.user.login}`,
url: author.user.url,
}
}
// if the commit used an email that's not associated with a github account
// then the user field will be empty, so we fall back to using the committer's
// name and email as specified by git
return {
name: author.name,
url: `mailto:${author.email}`,
}
}),
}

printCommit(commit)
}
}

function main () {
let commit
log.forEach(function (line) {
line = line.replace(/\r/g, '')
let m
/* eslint no-cond-assign:0 */
if (/^---$/.test(line)) {
printCommit(commit)
} else if (
(m = line.match(/^([a-f0-9]{7,10}) ([a-f0-9]+) (?:[(]([^)]+)[)] )?(.*?) [(](.*?)[)]/))
) {
commit = {
shortid: m[1],
fullid: m[2],
branch: m[3],
message: m[4],
author: m[5],
prurl: null,
fixes: [],
credit: null,
}
} else if ((m = line.match(/^PR-URL: (.*)/))) {
commit.prurl = m[1]
} else if ((m = line.match(/^Credit: @(.*)/))) {
if (!commit.credit) {
commit.credit = []
}
commit.credit.push(m[1])
} else if ((m = line.match(/^(?:Fix(?:es)|Closes?): #?([0-9]+)/))) {
commit.fixes.push(`https://github.com/npm/cli/issues/${m[1]}`)
} else if ((m = line.match(/^(?:Fix(?:es)|Closes?): ([^#]+)#([0-9]*)/))) {
commit.fixes.push(`https://github.com/${m[1]}/issues/${m[2]}`)
} else if ((m = line.match(/^(?:Fix(?:es)|Closes?): (https?:\/\/.*)/))) {
commit.fixes.push(m[1])
} else if ((m = line.match(/^Reviewed-By: @(.*)/))) {
commit.reviewed = m[1]
} else if (/\S/.test(line)) {
commit.message += `\n${line}`
}
})
}
main()