Skip to content

Commit 5982878

Browse files
committed
use github api to find pull request for commit
1 parent 8651387 commit 5982878

19 files changed

+862
-626
lines changed

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"clean": "rimraf lib",
2727
"fix": "pnpm lint --fix",
2828
"lint": "eslint src --ext ts --format stylish",
29-
"prepack": "pnpm build",
29+
"prepare": "pnpm build",
3030
"prettier": "prettier --write 'src/**/*.ts'",
3131
"test": "vitest",
3232
"test-ci": "pnpm build && pnpm test",
@@ -41,6 +41,7 @@
4141
},
4242
"dependencies": {
4343
"@manypkg/get-packages": "^2.2.0",
44+
"@octokit/rest": "^21.1.0",
4445
"chalk": "^4.0.0",
4546
"cli-highlight": "^2.1.11",
4647
"execa": "^5.0.0",
@@ -51,7 +52,10 @@
5152
"yargs": "^17.1.0"
5253
},
5354
"devDependencies": {
55+
"@types/fs-extra": "^11.0.4",
56+
"@types/hosted-git-info": "^3.0.5",
5457
"@types/node": "22.10.10",
58+
"@types/yargs": "^17.0.33",
5559
"@typescript-eslint/eslint-plugin": "5.62.0",
5660
"@typescript-eslint/parser": "5.62.0",
5761
"eslint": "8.55.0",
@@ -62,7 +66,7 @@
6266
"release-plan": "^0.11.0",
6367
"rimraf": "3.0.2",
6468
"typescript": "5.0.4",
65-
"vitest": "^3.0.4"
69+
"vitest": "^3.0.7"
6670
},
6771
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
6872
"engines": {

pnpm-lock.yaml

+359-221
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__mocks__/@octokit/rest.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { vi } from "vitest";
2+
3+
let responses = {
4+
prs: {} as Record<string, any[]>,
5+
users: {} as Record<string, any>,
6+
};
7+
8+
function throwError(status: number) {
9+
const e = new Error();
10+
(e as any).status = status;
11+
throw e;
12+
}
13+
14+
export const octokit = {
15+
args: [] as any,
16+
repos: {
17+
listPullRequestsAssociatedWithCommit: vi.fn(({ commit_sha }) => ({
18+
data: responses.prs[commit_sha] || throwError(404),
19+
})),
20+
},
21+
users: {
22+
getByUsername: vi.fn(({ username }) => ({ data: responses.users[username] || throwError(404) })),
23+
},
24+
};
25+
export const Octokit = function (...args: any[]) {
26+
octokit.args = args;
27+
return octokit;
28+
};
29+
Octokit.octokit = octokit;
30+
export const __setMockResponses = (res: any) => (responses = res);
31+
export const __resetMockResponses = () => (responses = { prs: {}, users: {} });

src/__snapshots__/changelog.spec.js.snap

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
exports[`Changelog > getCommitInfos > parse commits with different tags 1`] = `
44
[
5+
{
6+
"commitSHA": "a0000006",
7+
"date": "2017-01-01",
8+
"issueNumber": null,
9+
"message": "Merge pull request #3 from my-feature-3",
10+
"packages": [],
11+
"tags": undefined,
12+
},
513
{
614
"commitSHA": "a0000005",
715
"date": "2017-01-01",
@@ -56,7 +64,7 @@ exports[`Changelog > getCommitInfos > parse commits with different tags 1`] = `
5664
},
5765
],
5866
"number": 2,
59-
"title": "This is the commit title for the issue (#2)",
67+
"title": "feat(module) Add new module (#2)",
6068
"user": {
6169
"html_url": "https://github.com/test-user",
6270
"login": "test-user",

src/changelog.spec.js

+52-40
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
2-
2+
import * as MockedOctokit from "./__mocks__/@octokit/rest";
3+
import { Octokit } from "@octokit/rest";
34
import Changelog from "./changelog";
4-
import * as fetch from "./fetch";
55
import * as git from "./git";
66

7+
vi.mock("@octokit/rest");
78
vi.mock("../src/progress-bar");
89
vi.mock("../src/changelog");
910
vi.mock("../src/github-api");
1011
vi.mock("./git");
11-
vi.mock("./fetch");
1212

1313
describe("Changelog", () => {
14+
beforeEach(() => {
15+
Octokit.mockImplementation((...args) => MockedOctokit.Octokit(...args));
16+
});
17+
afterEach(() => {
18+
vi.resetAllMocks();
19+
});
1420
describe("packageFromPath", () => {
1521
const TESTS = [
1622
["", ""],
@@ -89,7 +95,7 @@ describe("Changelog", () => {
8995

9096
describe("getCommitInfos", () => {
9197
beforeEach(() => {
92-
fetch.__resetMockResponses();
98+
MockedOctokit.__resetMockResponses();
9399

94100
git.listCommits.mockImplementation(() => [
95101
{
@@ -135,35 +141,47 @@ describe("Changelog", () => {
135141
git.changedPaths.mockImplementation(() => []);
136142

137143
const usersCache = {
138-
"https://api.github.com/users/test-user": {
144+
"test-user": {
139145
body: {
140146
login: "test-user",
141147
html_url: "https://github.com/test-user",
142148
name: "Test User",
143149
},
144150
},
145151
};
146-
const issuesCache = {
147-
"https://api.github.com/repos/embroider-build/github-changelog/issues/2": {
148-
body: {
152+
const prCache = {
153+
a0000001: [],
154+
a0000002: [],
155+
a0000003: [
156+
{
157+
number: 2,
158+
title: "feat(module) Add new module (#2)",
159+
labels: [{ name: "Type: New Feature" }, { name: "Status: In Progress" }],
160+
user: usersCache["test-user"].body,
161+
},
162+
],
163+
a0000004: [
164+
{
149165
number: 2,
150166
title: "This is the commit title for the issue (#2)",
151167
labels: [{ name: "Type: New Feature" }, { name: "Status: In Progress" }],
152-
user: usersCache["https://api.github.com/users/test-user"].body,
168+
user: usersCache["test-user"].body,
153169
},
154-
},
170+
],
171+
a0000005: [],
172+
a0000006: [],
155173
"https://api.github.com/repos/embroider-build/github-changelog/issues/3": {
156174
body: {
157175
number: 2,
158176
title: "This is the commit title for the issue (#2)",
159177
labels: [{ name: "ignore" }, { name: "Status: In Progress" }],
160-
user: usersCache["https://api.github.com/users/test-user"].body,
178+
user: usersCache["test-user"].body,
161179
},
162180
},
163181
};
164-
fetch.__setMockResponses({
165-
...usersCache,
166-
...issuesCache,
182+
MockedOctokit.__setMockResponses({
183+
users: { ...usersCache },
184+
prs: { ...prCache },
167185
});
168186
});
169187

@@ -181,39 +199,33 @@ describe("Changelog", () => {
181199

182200
describe("getCommitters", () => {
183201
beforeEach(() => {
184-
fetch.__resetMockResponses();
202+
MockedOctokit.__resetMockResponses();
185203

186204
const usersCache = {
187-
"https://api.github.com/users/test-user": {
188-
body: {
189-
login: "test-user",
190-
html_url: "https://github.com/test-user",
191-
name: "Test User",
192-
},
205+
"test-user": {
206+
login: "test-user",
207+
html_url: "https://github.com/test-user",
208+
name: "Test User",
193209
},
194-
"https://api.github.com/users/test-user-1": {
195-
body: {
196-
login: "test-user-1",
197-
html_url: "https://github.com/test-user-1",
198-
name: "Test User 1",
199-
},
210+
"test-user-1": {
211+
login: "test-user-1",
212+
html_url: "https://github.com/test-user-1",
213+
name: "Test User 1",
200214
},
201-
"https://api.github.com/users/test-user-2": {
202-
body: {
203-
login: "test-user-2",
204-
html_url: "https://github.com/test-user-2",
205-
name: "Test User 2",
206-
},
215+
"test-user-2": {
216+
login: "test-user-2",
217+
html_url: "https://github.com/test-user-2",
218+
name: "Test User 2",
207219
},
208-
"https://api.github.com/users/user-bot": {
209-
body: {
210-
login: "user-bot",
211-
html_url: "https://github.com/user-bot",
212-
name: "User Bot",
213-
},
220+
"user-bot": {
221+
login: "user-bot",
222+
html_url: "https://github.com/user-bot",
223+
name: "User Bot",
214224
},
215225
};
216-
fetch.__setMockResponses(usersCache);
226+
MockedOctokit.__setMockResponses({
227+
users: { ...usersCache },
228+
});
217229
});
218230

219231
it("get list of valid commiters", async () => {

src/changelog.ts

+20-19
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
const pMap = require("p-map");
2-
const { resolve, sep } = require("path");
3-
1+
import pMap from "p-map";
42
import progressBar from "./progress-bar";
53
import { Configuration } from "./configuration";
6-
import findPullRequestId from "./find-pull-request-id";
74
import * as Git from "./git";
85
import GithubAPI, { GitHubUserResponse } from "./github-api";
96
import { CommitInfo, Release } from "./interfaces";
107
import MarkdownRenderer from "./markdown-renderer";
118

9+
import { resolve, sep } from "path";
10+
1211
const UNRELEASED_TAG = "___unreleased___";
1312

1413
interface Options {
@@ -66,7 +65,7 @@ export default class Changelog {
6665
const commits = await this.getCommitInfos(from, to);
6766

6867
// Step 6: Group commits by release (local)
69-
let releases = this.groupByRelease(commits);
68+
const releases = this.groupByRelease(commits);
7069

7170
// Step 7: Compile list of committers in release (local + remote)
7271
await this.fillInContributors(releases);
@@ -75,8 +74,7 @@ export default class Changelog {
7574
}
7675

7776
private async getListOfUniquePackages(sha: string): Promise<string[]> {
78-
let changedPaths = await Git.changedPaths(sha);
79-
77+
const changedPaths = await Git.changedPaths(sha);
8078
return changedPaths
8179
.map(path => this.packageFromPath(path))
8280
.filter(Boolean)
@@ -92,7 +90,7 @@ export default class Changelog {
9290
// ember-fastboot
9391
// ember-fastboot-2-fast-2-furious
9492
const foundPackage = this.config.packages.find(p => {
95-
let withSlash = p.path.endsWith(sep) ? p.path : `${p.path}${sep}`;
93+
const withSlash = p.path.endsWith(sep) ? p.path : `${p.path}${sep}`;
9694

9795
return absolutePath.startsWith(withSlash);
9896
});
@@ -130,7 +128,7 @@ export default class Changelog {
130128
for (const commit of commits) {
131129
const issue = commit.githubIssue;
132130
const login = issue && issue.user && issue.user.login;
133-
// If a list of `ignoreCommitters` is provided in the lerna.json config
131+
// If a list of `ignoreCommitters` is provided in the lernaon config
134132
// check if the current committer should be kept or not.
135133
const shouldKeepCommiter = login && !this.ignoreCommitter(login);
136134
if (login && shouldKeepCommiter && !committers[login]) {
@@ -161,27 +159,30 @@ export default class Changelog {
161159
.map(ref => ref.substr(TAG_PREFIX.length));
162160
}
163161

164-
const issueNumber = findPullRequestId(message);
165-
166162
return {
167163
commitSHA: sha,
168164
message,
169165
// Note: Only merge commits or commits referencing an issue / PR
170166
// will be kept in the changelog.
171167
tags: tagsInCommit,
172-
issueNumber,
168+
issueNumber: null,
173169
date,
174170
} as CommitInfo;
175171
});
176172
}
177173

178174
private async downloadIssueData(commitInfos: CommitInfo[]) {
179175
progressBar.init("Downloading issue information…", commitInfos.length);
176+
const PRs = new Set();
180177
await pMap(
181178
commitInfos,
182179
async (commitInfo: CommitInfo) => {
183-
if (commitInfo.issueNumber) {
184-
commitInfo.githubIssue = await this.github.getIssueData(this.config.repo, commitInfo.issueNumber);
180+
const issueData = await this.github.getPullRequest(this.config.repo, commitInfo.commitSHA);
181+
182+
if (issueData && !PRs.has(issueData.number)) {
183+
PRs.add(issueData.number);
184+
commitInfo.issueNumber = issueData.number.toString();
185+
commitInfo.githubIssue = issueData;
185186
}
186187

187188
progressBar.tick();
@@ -195,7 +196,7 @@ export default class Changelog {
195196
// Analyze the commits and group them by tag.
196197
// This is useful to generate multiple release logs in case there are
197198
// multiple release tags.
198-
let releaseMap: { [id: string]: Release } = {};
199+
const releaseMap: { [id: string]: Release } = {};
199200

200201
let currentTags = [UNRELEASED_TAG];
201202
for (const commit of commits) {
@@ -210,11 +211,11 @@ export default class Changelog {
210211
// referencing them.
211212
for (const currentTag of currentTags) {
212213
if (!releaseMap[currentTag]) {
213-
let date = currentTag === UNRELEASED_TAG ? this.getToday() : commit.date;
214+
const date = currentTag === UNRELEASED_TAG ? this.getToday() : commit.date;
214215
releaseMap[currentTag] = { name: currentTag, date, commits: [] };
215216
}
216217

217-
let prUserLogin = commit.githubIssue?.user.login;
218+
const prUserLogin = commit.githubIssue?.user?.login;
218219
if (prUserLogin && !this.ignoreCommitter(prUserLogin)) {
219220
releaseMap[currentTag].commits.push(commit);
220221
}
@@ -236,10 +237,10 @@ export default class Changelog {
236237
const labels = commit.githubIssue.labels.map(label => label.name.toLowerCase());
237238

238239
if (this.config.wildcardLabel) {
239-
// check whether the commit has any of the labels from the learna.json config.
240+
// check whether the commit has any of the labels from the learnaon config.
240241
// If not, label this commit with the provided label
241242

242-
let foundLabel = Object.keys(this.config.labels).some(label => labels.indexOf(label.toLowerCase()) !== -1);
243+
const foundLabel = Object.keys(this.config.labels).some(label => labels.indexOf(label.toLowerCase()) !== -1);
243244

244245
if (!foundLabel) {
245246
labels.push(this.config.wildcardLabel);

0 commit comments

Comments
 (0)