Skip to content

Commit 0aa7a2e

Browse files
authored
feat: --find-matching-prs for commits without PR-URL (#130)
1 parent 2a87620 commit 0aa7a2e

7 files changed

+102
-10
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ npm i changelog-maker -g
4444

4545
## Usage
4646

47-
**`changelog-maker [--plaintext|p] [--markdown|md] [--sha] [--group|-g] [--reverse] [--commit-url=<url/with/{ref}>] [--start-ref=<ref>] [--end-ref=<ref>] [github-user[, github-project]]`**
47+
**`changelog-maker [--plaintext|p] [--markdown|md] [--sha] [--group|-g] [--reverse] [--find-matching-prs] [--commit-url=<url/with/{ref}>] [--start-ref=<ref>] [--end-ref=<ref>] [github-user[, github-project]]`**
4848

4949
`github-user` and `github-project` should point to the GitHub repository that can be used to find the `PR-URL` data if just an issue number is provided and will also impact how the PR-URL issue numbers are displayed
5050

@@ -62,6 +62,7 @@ npm i changelog-maker -g
6262
* `--start-ref=<ref>`: use the given git `<ref>` as a starting point rather than the _last tag_. The `<ref>` can be anything commit-ish including a commit sha, tag, branch name. If you specify a `--start-ref` argument the commit log will not be pruned so that version commits and `working on <version>` commits are left in the list.
6363
* `--end-ref=<ref>`: use the given git `<ref>` as a end-point rather than the _now_. The `<ref>` can be anything commit-ish including a commit sha, tag, branch name.
6464
* `--filter-release`: exclude Node-style release commits from the list. e.g. "Working on v1.0.0" or "2015-10-21 Version 2.0.0" and also "npm version X" style commits containing _only_ an `x.y.z` semver designator.
65+
* `--find-matching-prs`: use the GitHub API to find the pull requests that match commits that don't have the `PR-URL` metadata in their message text. Without metadata, it may be necessary to also pass the org/user and repo name on the commandline (as the `github-user` and `github-project` arguments as demonstrated above, it may also be necessary to use `--find-matching-prs=true` in this case).
6566
* `--quiet` or `-q`: do not print to `process.stdout`
6667
* `--all` or `-a`: process all commits since beginning, instead of last tag.
6768
* `--help` or `-h`: show usage and help.

auth.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { promisify } from 'util'
2+
import ghauth from 'ghauth'
3+
4+
const authOptions = {
5+
configName: 'changelog-maker',
6+
scopes: ['repo'],
7+
noDeviceFlow: true
8+
}
9+
10+
export async function auth () {
11+
return await promisify(ghauth)(authOptions)
12+
}

collect-commit-labels.js

+2-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
'use strict'
22

3-
import { promisify } from 'util'
4-
import ghauth from 'ghauth'
3+
import { auth } from './auth.js'
54
import ghissues from 'ghissues'
65
import async from 'async'
76

8-
const authOptions = {
9-
configName: 'changelog-maker',
10-
scopes: ['repo'],
11-
noDeviceFlow: true
12-
}
13-
147
export async function collectCommitLabels (list) {
158
const sublist = list.filter((commit) => {
169
return typeof commit.ghIssue === 'number' && commit.ghUser && commit.ghProject
@@ -20,7 +13,7 @@ export async function collectCommitLabels (list) {
2013
return
2114
}
2215

23-
const authData = await promisify(ghauth)(authOptions)
16+
const authData = await auth()
2417

2518
const cache = {}
2619

find-matching-prs.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
'use strict'
2+
3+
import { auth } from './auth.js'
4+
import { graphql } from '@octokit/graphql'
5+
import async from 'async'
6+
7+
// Query to find the first 4 pull requests that include the commit that we're
8+
// concerned about. We'll filter them and take the first one that was MERGED
9+
// as our prUrl.
10+
const query = `
11+
query ($owner: String!, $name: String!, $commit: GitObjectID!) {
12+
repository(owner: $owner, name: $name) {
13+
object(oid: $commit) {
14+
... on Commit {
15+
associatedPullRequests(first: 4) {
16+
... on PullRequestConnection {
17+
edges {
18+
node {
19+
... on PullRequest {
20+
number
21+
url
22+
title
23+
state
24+
}
25+
}
26+
}
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
`
34+
35+
export async function findMatchingPrs (ghId, list) {
36+
// only look up commits that don't have a prUrl from metadata
37+
const sublist = list.filter((commit) => typeof commit.prUrl !== 'string')
38+
if (!sublist.length) {
39+
return
40+
}
41+
42+
const authData = await auth()
43+
const headers = { authorization: `token ${authData.token}` }
44+
const cache = {}
45+
46+
const q = async.queue(async (commit, next) => {
47+
if (commit.ghUser === 'iojs') {
48+
commit.ghUser = 'nodejs' // forcibly rewrite as the GH API doesn't do it for us
49+
}
50+
51+
// cache on commit, so we don't run the same commit twice (is this possible?)
52+
cache[commit.sha] = cache[commit.sha] || (async () => {
53+
try {
54+
const res = await graphql(query, { owner: ghId.user, name: ghId.repo, commit: commit.sha, headers })
55+
if (res.repository?.object?.associatedPullRequests?.edges?.length) {
56+
const pr = res.repository.object.associatedPullRequests.edges.filter((e) => e.node?.state === 'MERGED')[0]
57+
if (pr) {
58+
commit.ghIssue = pr.node.number
59+
commit.prUrl = pr.node.url
60+
}
61+
}
62+
} catch (err) {
63+
console.error(`Error querying GitHub to find pull request for commit: ${err}`)
64+
}
65+
})()
66+
await cache[commit.sha]
67+
next()
68+
}, 15)
69+
70+
q.push(sublist)
71+
await q.drain()
72+
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"keywords": [],
2626
"preferGlobal": true,
2727
"dependencies": {
28+
"@octokit/graphql": "^4.8.0",
2829
"async": "^3.2.2",
2930
"chalk": "^5.0.0",
3031
"commit-stream": "^1.1.0",

process-commits.js

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { toGroups } from './groups.js'
55
import { formatMarkdown } from './format.js'
66
import { supportsColor } from 'chalk'
77
import { collectCommitLabels } from './collect-commit-labels.js'
8+
import { findMatchingPrs } from './find-matching-prs.js'
89

910
function getFormat (argv) {
1011
if (argv.format && Object.values(formatType).includes(argv.format)) {
@@ -33,6 +34,10 @@ export async function processCommits (argv, ghId, list) {
3334
const reverse = argv.reverse
3435
const commitUrl = argv['commit-url'] || 'https://github.com/{ghUser}/{ghRepo}/commit/{ref}'
3536

37+
if (argv['find-matching-prs']) {
38+
await findMatchingPrs(ghId, list)
39+
}
40+
3641
await collectCommitLabels(list)
3742

3843
const format = getFormat(argv)

test.js

+8
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,11 @@ test('test markdown punctuation chars in commit message and author name', (t) =>
124124
`)
125125
t.end()
126126
})
127+
128+
test('test find-matching-prs', (t) => {
129+
t.equal(
130+
exec('--start-ref=a059bc7ca9 --end-ref=a059bc7ca9 --find-matching-prs=true nodejs changelog-maker'),
131+
`* [a059bc7ca9] - chore(deps): remove package-lock.json (#118) (Rod Vagg) https://github.com/nodejs/changelog-maker/pull/118
132+
`)
133+
t.end()
134+
})

0 commit comments

Comments
 (0)