Skip to content

Commit a409c3b

Browse files
committed
Use Octokit for API calls without a token, too
An Octokit client created without `github.getOctokit` doesn't require a Github token to make public API requests, but may be rate-limited like the unauthenticated curl calls.
1 parent 53636dd commit a409c3b

4 files changed

Lines changed: 39 additions & 128 deletions

File tree

__tests__/releases.test.ts

Lines changed: 28 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { beforeEach, describe, expect, jest, test } from "@jest/globals";
22
import { Octokit, ReleaseData, Releases } from "../src/releases";
3-
import { CommandExecutor } from "../src/exec";
3+
import { RequestError } from "@octokit/request-error";
44

55
const dummyReleaseData: ReleaseData = {
66
url: "",
@@ -49,7 +49,6 @@ const dummyReleaseData_v1_24_0 = { ...dummyReleaseData, name: 'v1.24.0' };
4949
const dummyReleaseData_v1_28_0 = { ...dummyReleaseData, name: 'v1.28.0' };
5050
const allReleases = [dummyReleaseData, dummyReleaseData_1_7, dummyReleaseData_v1_24_0, dummyReleaseData_v1_28_0];
5151

52-
const executor = jest.fn<CommandExecutor>();
5352
type Paginate = Octokit['paginate'];
5453
type ListReleases = Octokit['rest']['repos']['listReleases'];
5554
type GetLatestRelease = Octokit['rest']['repos']['getLatestRelease'];
@@ -70,26 +69,6 @@ beforeEach(() => {
7069
jest.clearAllMocks();
7170
})
7271

73-
function expectLastCurlCallForUrl(url: string) {
74-
expect(executor).lastCalledWith('curl', ['-sL', url], { ignoreReturnCode: false });
75-
}
76-
77-
function mockApiReturn(stdOut: string) {
78-
executor.mockReturnValueOnce(Promise.resolve({
79-
exitCode: 0,
80-
stdOut,
81-
stdErr: '',
82-
}));
83-
}
84-
85-
function mockApiReturnReleases() {
86-
mockApiReturn(JSON.stringify(allReleases));
87-
}
88-
89-
function mockApiReturnRelease(releaseData: ReleaseData) {
90-
mockApiReturn(JSON.stringify(releaseData));
91-
}
92-
9372
function mockOctokitReturnReleases() {
9473
mockPaginate.mockReturnValueOnce(Promise.resolve(allReleases));
9574
}
@@ -103,108 +82,69 @@ function mockOctokitReturnRelease(releaseData: ReleaseData) {
10382
}));
10483
}
10584

106-
const URL_BASE = "https://api.github.com/repos/google/google-java-format/releases";
107-
const URL_TAIL = "?per_page=100";
108-
10985
describe('get all release data', () => {
110-
test('get all release data with API', async () => {
111-
mockApiReturnReleases();
112-
const releases = new Releases(executor);
113-
const results = await releases.getAllReleaseData();
114-
expect(results).toEqual(allReleases);
115-
// IMPORTANT: should not have a trailing slash
116-
expectLastCurlCallForUrl(URL_BASE + URL_TAIL);
117-
});
118-
119-
test('get all release data with API and call to API fails', async () => {
120-
const error = new Error("Command 'curl ...' failed with exit code 1");
121-
executor.mockReturnValueOnce(Promise.reject(error));
122-
const releases = new Releases(executor);
123-
expect(() => releases.getAllReleaseData())
124-
.rejects
125-
.toThrow(error)
126-
// IMPORTANT: should not have a trailing slash
127-
expectLastCurlCallForUrl(URL_BASE + URL_TAIL);
128-
});
129-
13086
test('get all release data with octokit', async () => {
13187
mockOctokitReturnReleases();
132-
const releases = new Releases(executor, octokit);
88+
const releases = new Releases(octokit);
13389
const results = await releases.getAllReleaseData();
13490
expect(results).toEqual(allReleases);
13591
expect(mockPaginate).toHaveBeenCalledWith(octokit.rest.repos.listReleases, expect.anything());
13692
expect(mockGetLatestRelease).not.toBeCalled();
137-
expect(executor).not.toBeCalled();
13893
});
139-
});
140-
141-
describe('get latest release data', () => {
142-
const casesJavaVersions: [number, ReleaseData][] = [[8, dummyReleaseData_1_7], [11, dummyReleaseData_v1_24_0], [17, dummyReleaseData_v1_28_0]];
143-
144-
describe('get latest release data with API', () => {
145-
const releases = new Releases(executor);
14694

147-
test.each(casesJavaVersions)('when java version is %i, then return release %o', async (javaVersion, expectedRelease) => {
148-
mockApiReturnReleases();
149-
const result = await releases.getLatestReleaseData(javaVersion);
150-
expect(result).toEqual(expectedRelease);
151-
// IMPORTANT: should not have a trailing slash
152-
expectLastCurlCallForUrl(URL_BASE + URL_TAIL);
153-
});
154-
155-
test('when java version is 21, then return release latest', async () => {
156-
mockApiReturnRelease(dummyReleaseData);
157-
const result = await releases.getLatestReleaseData(21);
158-
expect(result).toEqual(dummyReleaseData);
159-
expectLastCurlCallForUrl(URL_BASE + "/latest" + URL_TAIL);
95+
test('get all release data with octokit and call fails', async () => {
96+
const error = new RequestError("Not Found", 500, {
97+
request: {
98+
method: "GET",
99+
url: "/repos/google/google-java-format/releases",
100+
headers: {},
101+
},
102+
response: {
103+
status: 500,
104+
url: "/repos/google/google-java-format/releases",
105+
headers: {},
106+
data: { message: "Error" },
107+
},
160108
});
109+
mockPaginate.mockRejectedValue(error);
110+
const releases = new Releases(octokit);
111+
await expect(releases.getAllReleaseData()).rejects.toThrow(error);
112+
expect(mockPaginate).toHaveBeenCalledWith(octokit.rest.repos.listReleases, expect.anything());
161113
});
114+
});
162115

163-
test('get latest release data with API and call to API fails', async () => {
164-
const error = new Error("Command 'curl ...' failed with exit code 1");
165-
executor.mockReturnValueOnce(Promise.reject(error));
166-
const releases = new Releases(executor);
167-
expect(() => releases.getLatestReleaseData(21))
168-
.rejects
169-
.toThrow(error);
170-
expectLastCurlCallForUrl(URL_BASE + "/latest" + URL_TAIL);
171-
});
116+
describe('get latest release data', () => {
117+
const casesJavaVersions: [number, ReleaseData][] = [[8, dummyReleaseData_1_7], [11, dummyReleaseData_v1_24_0], [17, dummyReleaseData_v1_28_0]];
172118

173119
describe('get latest release data with octokit', () => {
174-
const releases = new Releases(executor, octokit);
120+
const releases = new Releases(octokit);
175121

176122
test.each(casesJavaVersions)('when java version is %i, then return release %o', async (javaVersion, expectedRelease) => {
177123
mockOctokitReturnReleases();
178124
const result = await releases.getLatestReleaseData(javaVersion);
179125
expect(result).toEqual(expectedRelease);
180-
expect(executor).not.toBeCalled();
181126
});
182127

183128
test('when java version is 21, then return latest release', async () => {
184129
mockOctokitReturnRelease(dummyReleaseData);
185130
const result = await releases.getLatestReleaseData(21);
186131
expect(result).toEqual(dummyReleaseData);
187-
expect(executor).not.toBeCalled();
188132
});
189133
});
190134
});
191135

192136
describe('get release by name', () => {
193137
test('get release by name (existing)', async () => {
194-
mockApiReturnReleases();
195-
const releases = new Releases(executor);
138+
mockOctokitReturnReleases();
139+
const releases = new Releases(octokit);
196140
const result = await releases.getReleaseDataByName('dummy-release-data');
197141
expect(result).toEqual(dummyReleaseData);
198-
// IMPORTANT: should not have a trailing slash
199-
expectLastCurlCallForUrl(URL_BASE + URL_TAIL);
200142
});
201143

202144
test('get release by name (non-existing)', async () => {
203-
mockApiReturnReleases();
204-
const releases = new Releases(executor);
145+
mockOctokitReturnReleases();
146+
const releases = new Releases(octokit);
205147
const result = await releases.getReleaseDataByName('non-existing-data');
206148
expect(result).toBeUndefined();
207-
// IMPORTANT: should not have a trailing slash
208-
expectLastCurlCallForUrl(URL_BASE + URL_TAIL);
209149
});
210-
});
150+
});

dist/index.js

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32561,6 +32561,7 @@ const glob = __importStar(__nccwpck_require__(8090));
3256132561
const path = __importStar(__nccwpck_require__(1017));
3256232562
const git_1 = __nccwpck_require__(6350);
3256332563
const releases_1 = __nccwpck_require__(4325);
32564+
const utils_1 = __nccwpck_require__(3030);
3256432565
function getInput(inputAlternativeNames) {
3256532566
if (inputAlternativeNames.length === 0)
3256632567
throw new Error("inputAlternativeNames is empty");
@@ -32583,8 +32584,8 @@ class Main {
3258332584
this.execute = executor;
3258432585
this.gitOperations = new git_1.GitOperations(executor);
3258532586
this.githubToken = getInput(['githubToken', 'github-token']);
32586-
const octokit = this.githubToken ? github.getOctokit(this.githubToken) : undefined;
32587-
this.releases = new releases_1.Releases(executor, octokit);
32587+
const octokit = this.githubToken ? github.getOctokit(this.githubToken) : new utils_1.GitHub({});
32588+
this.releases = new releases_1.Releases(octokit);
3258832589
this.executablePath = executablePath;
3258932590
}
3259032591
async getJavaVersion() {
@@ -32705,22 +32706,11 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
3270532706
exports.Releases = void 0;
3270632707
const const_1 = __nccwpck_require__(8032);
3270732708
class Releases {
32708-
static apiReleases = `https://api.github.com/repos/${const_1.repositoryOwner}/${const_1.repositoryName}/releases`;
32709-
execute;
3271032709
octokit;
32711-
constructor(execute, octokit) {
32712-
this.execute = execute;
32710+
constructor(octokit) {
3271332711
this.octokit = octokit;
3271432712
}
32715-
async callReleasesApi(pathParameter) {
32716-
const url = `${Releases.apiReleases}${pathParameter || ''}?per_page=100`;
32717-
const response = await this.execute('curl', ['-sL', url], { ignoreReturnCode: false });
32718-
return JSON.parse(response.stdOut);
32719-
}
3272032713
async getAllReleaseData() {
32721-
if (!this.octokit) {
32722-
return this.callReleasesApi();
32723-
}
3272432714
const params = { owner: const_1.repositoryOwner, repo: const_1.repositoryName, per_page: 100 };
3272532715
const allReleases = await this.octokit.paginate(this.octokit.rest.repos.listReleases, params);
3272632716
return allReleases;
@@ -32738,9 +32728,6 @@ class Releases {
3273832728
// Versions after v1.28.0 require JDK 21+
3273932729
return (await this.getReleaseDataByName('v1.28.0'));
3274032730
}
32741-
if (!this.octokit) {
32742-
return this.callReleasesApi('/latest');
32743-
}
3274432731
const params = { owner: const_1.repositoryOwner, repo: const_1.repositoryName };
3274532732
const response = await this.octokit.rest.repos.getLatestRelease(params);
3274632733
return response.data;

src/main.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as path from 'path';
55
import { CommandExecutor, CommandExecutorOptions, CommandExecutorResult } from './exec';
66
import { GitOperations } from './git';
77
import { ReleaseData, Releases } from './releases';
8+
import { GitHub } from '@actions/github/lib/utils';
89

910
export function getInput(inputAlternativeNames: string[]) {
1011
if (inputAlternativeNames.length === 0) throw new Error("inputAlternativeNames is empty");
@@ -28,8 +29,8 @@ export class Main {
2829
this.execute = executor;
2930
this.gitOperations = new GitOperations(executor);
3031
this.githubToken = getInput(['githubToken', 'github-token']);
31-
const octokit = this.githubToken ? github.getOctokit(this.githubToken) : undefined;
32-
this.releases = new Releases(executor, octokit);
32+
const octokit = this.githubToken ? github.getOctokit(this.githubToken) : new GitHub({});
33+
this.releases = new Releases(octokit);
3334
this.executablePath = executablePath;
3435
}
3536

src/releases.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,20 @@
11
import * as github from '@actions/github';
22
import { CommandExecutor } from './exec';
33
import { repositoryName as GJF_REPO_NAME, repositoryOwner as GJF_REPO_OWNER } from './const';
4-
import { RestEndpointMethodTypes } from '@octokit/plugin-rest-endpoint-methods'
4+
import { RestEndpointMethodTypes } from '@octokit/plugin-rest-endpoint-methods';
55

66
type ReleaseDataArray = RestEndpointMethodTypes['repos']['listReleases']['response']['data'];
77
export type ReleaseData = ReleaseDataArray[0];
88
export type Octokit = ReturnType<typeof github.getOctokit>;
99

1010
export class Releases {
11-
private static readonly apiReleases = `https://api.github.com/repos/${GJF_REPO_OWNER}/${GJF_REPO_NAME}/releases`;
12-
private readonly execute: CommandExecutor;
13-
private readonly octokit: Octokit | undefined;
11+
private readonly octokit: Octokit;
1412

15-
constructor(execute: CommandExecutor, octokit?: Octokit) {
16-
this.execute = execute;
13+
constructor(octokit: Octokit) {
1714
this.octokit = octokit;
1815
}
1916

20-
private async callReleasesApi(): Promise<ReleaseData[]>;
21-
private async callReleasesApi(pathParameter: string | number): Promise<ReleaseData>;
22-
private async callReleasesApi(pathParameter?: number): Promise<ReleaseData | ReleaseData[]>;
23-
private async callReleasesApi(pathParameter?: string | number): Promise<ReleaseData | ReleaseData[]> {
24-
const url = `${Releases.apiReleases}${pathParameter || ''}?per_page=100`;
25-
const response = await this.execute('curl', ['-sL', url], { ignoreReturnCode: false });
26-
return JSON.parse(response.stdOut);
27-
}
28-
2917
async getAllReleaseData(): Promise<ReleaseData[]> {
30-
if (!this.octokit) {
31-
return this.callReleasesApi();
32-
}
3318
const params = { owner: GJF_REPO_OWNER, repo: GJF_REPO_NAME, per_page: 100 };
3419
const allReleases = await this.octokit.paginate(this.octokit.rest.repos.listReleases, params);
3520
return allReleases;
@@ -48,9 +33,7 @@ export class Releases {
4833
// Versions after v1.28.0 require JDK 21+
4934
return (await this.getReleaseDataByName('v1.28.0'))!;
5035
}
51-
if (!this.octokit) {
52-
return this.callReleasesApi('/latest');
53-
}
36+
5437
const params = { owner: GJF_REPO_OWNER, repo: GJF_REPO_NAME };
5538
const response = await this.octokit.rest.repos.getLatestRelease(params);
5639
return response.data;

0 commit comments

Comments
 (0)