ということは多々あると思う。そういったものを実現する類のツールはいろいろあるし、実際に自分がメンテナしてるHexoでもrelease drafterを導入して自動でリリースノートを作成している。ところが、やんごとなき理由によりリリースノートが部分的に吹っ飛んでしまい、特定の期間のマージ済みPRを抽出してリストにしたいという要件が発生してしまった。

※念のため書いておくとrelease-drafterのせいというよりはrelease-drafterに設定したバージョニングの設定が悪かったのだと思うが詳細は割愛する

やりかた


まず、始めに手でやろうかと思ったけれども、100件近くあったので諦めた。どうするか考えた結果GitHub API v4を使うことにした。というのもv3 (REST)だと関連データを取得するのが手間そうだったからだ。

取得したデータは適当にソートして、Breaking ChangesFeaturesといった分類はその後に手動ですることにした。ここは手動。ここも自動的に出来ればカッコいいけど、分類する判定方法(ラベルで判定とかタイトルで判定とか)が思ったよりややこしくなりそうだったので諦めた。1000件とかなら頑張ってコード書いたかもしれないけど、たかが100件なら手でやった方が速い。

コード


というわけで以下コード。queryrepoのところを対象のリポジトリに変更して、マージされた日を設定するくらい。この辺りのクエリは公式ドキュメントを参考にすればいい。

Searching issues and pull requests

GitHubのv4をNode.jsから実行するのは下記を参考にした。ありがとうございます。

GitHub GraphQL API v4 を JavaScript から利用する

注意点としてはtypeを指定しないといけないというのと、typePRは存在しないという点くらい。件数は一度に100件までしか取得できないのでそれ以上が必要な場合はページネーションするか、期間を区切ったりしないといけない。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// axiosはインストールしといてください
const axios = require("axios");

const httpClient = axios.create({
baseURL: 'https://api.github.com/',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
// アクセストークンは環境変数から
'Authorization': `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`
}
})

const query = `
query {
search(query: "repo:REPOSITOPRY_OWNER/REPO merged:2019-12-22..2020-05-17 is:merged", type:ISSUE, first:100) {
edges {
node {
... on PullRequest {
title,
number,
author {
login
},
labels(first:5) {
edges {
node {
name
}
}
},
mergedAt
}
}
}
}
}`

httpClient.post('graphql', { query }).then(response => {
const changes = [];
response.data.data.search.edges.forEach(x => {
const node = x.node;
const labels = [];
node.labels.edges.forEach(l => { labels.push(l.node.name) });
changes.push({
'labels': `${labels.sort((x, y) => x - y)}`,
'title': `${node.title}`,
'author': `@${node.author.login}`,
'number': `[#${node.number}]`
});
});

changes.sort((x, y) => x.title - y.title).forEach(c => {
// ラベルで分類する必要があったので、ラベルを出力したりしてるけどそういうのやってないなら不要
console.log(`${c.labels}`);
console.log(`${c.title} ${c.author} ${c.number}`);
});
}).catch(error => {
console.log(error.response);
});

これを実行するとこんな感じで出力される。後はこれを手動で良い感じにこねこねする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
dependencies
chore(deps-dev): bump eslint from 6.8.0 to 7.0.0 @dependabot-preview [#4301]

chore: incompatible with Node 14 @john [#4285]

chore(release-drafter): wrap PR reference in square bracket @john [#4284]
dependencies
chore(deps-dev): bump lint-staged from 9.5.0 to 10.2.0 @dependabot-preview [#4283]

fix: access error code from error object directly @due [#4280]

refactor: drop hexo-util#HashStream @due [#4279]
dependencies
chore(deps): bump hexo-fs from 2.0.0 to 3.0.1 @dependabot-preview [#4277]
dependencies
chore(deps): bump hexo-util from 1.9.0 to 2.0.0 @dependabot-preview [#4276]

test: coverage improvements @due [#4270]

test(box): fix test cases for macOS @anonymouse [#4269]

test(load_plugins): make sure file is created @due [#4265]

fix(#4251): load_plugin with extra line EOF @due [#4256]
Breaking Change
chore/ci: drop Node.js 8 and add Node.js 14 @due [#4255]

最近はこの時間はベッドに入っている(寝ているかどうかは別)ので、この時間までやっているのは久しぶり感がある。おわり。