Skip to content

Commit ea4acdb

Browse files
Simplify paginated octokit queries (#28)
- **Simplify listJobsForWorkflowRun** - **Simplify listWorkflowRunArtifacts** - **update changelog**
1 parent 5edaab2 commit ea4acdb

File tree

7 files changed

+45
-90
lines changed

7 files changed

+45
-90
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12+
- Simplify paginated octokit queries
1213
- Use global tracer instead of passing it around
1314
- tests: Add a replay client
1415
- Migrate to ESM

dist/index.js

+12-27
Original file line numberDiff line numberDiff line change
@@ -49115,19 +49115,12 @@ async function listWorkflowRunArtifacts(context, octokit, runId) {
4911549115
}
4911649116
const artifactNameRegex = /\{(?<jobName>.*)\}\{(?<stepName>.*)\}/;
4911749117
async function getWorkflowRunArtifactMap(context, octokit, runId) {
49118-
const artifactsList = [];
49119-
const pageSize = 100;
49120-
for (let page = 1, hasNext = true; hasNext; page++) {
49121-
const listArtifactsResponse = await octokit.rest.actions.listWorkflowRunArtifacts({
49122-
...context.repo,
49123-
run_id: runId,
49124-
page,
49125-
per_page: pageSize,
49126-
});
49127-
artifactsList.push(...listArtifactsResponse.data.artifacts);
49128-
hasNext = artifactsList.length < listArtifactsResponse.data.total_count;
49129-
}
49130-
const artifactsLookup = await artifactsList.reduce(async (resultP, artifact) => {
49118+
const artifacts = await octokit.paginate(octokit.rest.actions.listWorkflowRunArtifacts, {
49119+
...context.repo,
49120+
run_id: runId,
49121+
per_page: 100,
49122+
});
49123+
const artifactsLookup = await artifacts.reduce(async (resultP, artifact) => {
4913149124
const result = await resultP;
4913249125
const match = artifact.name.match(artifactNameRegex);
4913349126
const next = { ...result };
@@ -49202,20 +49195,12 @@ async function getSelfArtifactMap() {
4920249195
return artifactsMap;
4920349196
}
4920449197
async function listJobsForWorkflowRun(context, octokit, runId) {
49205-
const jobs = [];
49206-
const pageSize = 100;
49207-
for (let page = 1, hasNext = true; hasNext; page++) {
49208-
const listJobsForWorkflowRunResponse = await octokit.rest.actions.listJobsForWorkflowRun({
49209-
...context.repo,
49210-
run_id: runId,
49211-
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
49212-
page,
49213-
per_page: pageSize,
49214-
});
49215-
jobs.push(...listJobsForWorkflowRunResponse.data.jobs);
49216-
hasNext = jobs.length < listJobsForWorkflowRunResponse.data.total_count;
49217-
}
49218-
return jobs;
49198+
return await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, {
49199+
...context.repo,
49200+
run_id: runId,
49201+
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
49202+
per_page: 100,
49203+
});
4921949204
}
4922049205
async function getWorkflowRunJobs(context, octokit, runId) {
4922149206
const getWorkflowRunResponse = await octokit.rest.actions.getWorkflowRun({

dist/index.js.map

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

src/__assets__/run.rec

+4-4
Large diffs are not rendered by default.

src/github/__assets__/getWorkflowRunJobs.rec

+4-4
Large diffs are not rendered by default.

src/github/github.test.ts

+10-16
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@ import * as path from "node:path";
33
import * as url from "node:url";
44
import type { Context } from "@actions/github/lib/context";
55
import { jest } from "@jest/globals";
6+
import type { components } from "@octokit/openapi-types";
67
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
78
import fetchMock from "jest-fetch-mock";
89
import { mock, mockDeep } from "jest-mock-extended";
910
import type { Octokit } from "./github";
10-
import { type WorkflowArtifact, type WorkflowArtifactDownload, listWorkflowRunArtifacts } from "./github";
11+
import { type WorkflowArtifactDownload, listWorkflowRunArtifacts } from "./github";
1112

1213
const __filename = url.fileURLToPath(import.meta.url);
1314
const __dirname = path.dirname(__filename);
1415

1516
jest.mock("@actions/github");
1617
jest.mock("@actions/core");
1718

18-
type ListWorkflowRunArtifactsResponse = RestEndpointMethodTypes["actions"]["listWorkflowRunArtifacts"]["response"];
19+
type Artifacts = components["schemas"]["artifact"][];
1920
type DownloadArtifactResponse = RestEndpointMethodTypes["actions"]["downloadArtifact"]["response"];
2021

2122
describe("listWorkflowRunArtifacts", () => {
@@ -26,25 +27,18 @@ describe("listWorkflowRunArtifacts", () => {
2627
beforeAll(async () => {
2728
mockContext = mockDeep<Context>();
2829
mockOctokit = mockDeep<Octokit>();
29-
const mockListWorkflowRunArtifacts = mockOctokit.rest.actions.listWorkflowRunArtifacts as jest.MockedFunction<
30-
typeof mockOctokit.rest.actions.listWorkflowRunArtifacts
31-
>;
30+
const mockPaginate = mockOctokit.paginate as jest.MockedFunction<typeof mockOctokit.paginate>;
3231
const mockDownloadArtifact = mockOctokit.rest.actions.downloadArtifact as jest.MockedFunction<
3332
typeof mockOctokit.rest.actions.downloadArtifact
3433
>;
3534

36-
mockListWorkflowRunArtifacts.mockResolvedValue(
37-
mock<ListWorkflowRunArtifactsResponse>({
38-
data: {
39-
total_count: 1,
40-
artifacts: [
41-
mock<WorkflowArtifact>({
42-
id: 1,
43-
name: "{lint-and-test}{run tests}",
44-
}),
45-
],
35+
mockPaginate.mockResolvedValue(
36+
mock<Artifacts>([
37+
{
38+
id: 1,
39+
name: "{lint-and-test}{run tests}",
4640
},
47-
}),
41+
]),
4842
);
4943
mockDownloadArtifact.mockResolvedValue(mock<DownloadArtifactResponse>({ url: "localhost" }));
5044
const filePath = path.join(__dirname, "__assets__", "{lint-and-test}{run tests}.zip");

src/github/github.ts

+13-38
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ type ListJobsForWorkflowRunType = RestEndpointMethodTypes["actions"]["listJobsFo
1212
type WorkflowRunJob = ListJobsForWorkflowRunType["data"]["jobs"][number];
1313
type WorkflowRun = RestEndpointMethodTypes["actions"]["getWorkflowRun"]["response"]["data"];
1414

15-
interface WorkflowArtifact {
16-
id: number;
17-
name: string;
18-
}
19-
2015
type WorkflowArtifactMap = {
2116
[job: string]: {
2217
[step: string]: WorkflowArtifactDownload;
@@ -55,21 +50,13 @@ async function listWorkflowRunArtifacts(
5550
const artifactNameRegex = /\{(?<jobName>.*)\}\{(?<stepName>.*)\}/;
5651

5752
async function getWorkflowRunArtifactMap(context: Context, octokit: Octokit, runId: number) {
58-
const artifactsList: WorkflowArtifact[] = [];
59-
const pageSize = 100;
60-
61-
for (let page = 1, hasNext = true; hasNext; page++) {
62-
const listArtifactsResponse = await octokit.rest.actions.listWorkflowRunArtifacts({
63-
...context.repo,
64-
run_id: runId,
65-
page,
66-
per_page: pageSize,
67-
});
68-
artifactsList.push(...listArtifactsResponse.data.artifacts);
69-
hasNext = artifactsList.length < listArtifactsResponse.data.total_count;
70-
}
53+
const artifacts = await octokit.paginate(octokit.rest.actions.listWorkflowRunArtifacts, {
54+
...context.repo,
55+
run_id: runId,
56+
per_page: 100,
57+
});
7158

72-
const artifactsLookup: WorkflowArtifactMap = await artifactsList.reduce(async (resultP, artifact) => {
59+
const artifactsLookup: WorkflowArtifactMap = await artifacts.reduce(async (resultP, artifact) => {
7360
const result = await resultP;
7461
const match = artifact.name.match(artifactNameRegex);
7562
const next: WorkflowArtifactMap = { ...result };
@@ -150,24 +137,13 @@ async function getSelfArtifactMap() {
150137
return artifactsMap;
151138
}
152139

153-
async function listJobsForWorkflowRun(context: Context, octokit: Octokit, runId: number): Promise<WorkflowRunJob[]> {
154-
const jobs: WorkflowRunJob[] = [];
155-
const pageSize = 100;
156-
157-
for (let page = 1, hasNext = true; hasNext; page++) {
158-
const listJobsForWorkflowRunResponse = await octokit.rest.actions.listJobsForWorkflowRun({
159-
...context.repo,
160-
run_id: runId,
161-
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
162-
page,
163-
per_page: pageSize,
164-
});
165-
166-
jobs.push(...listJobsForWorkflowRunResponse.data.jobs);
167-
hasNext = jobs.length < listJobsForWorkflowRunResponse.data.total_count;
168-
}
169-
170-
return jobs;
140+
async function listJobsForWorkflowRun(context: Context, octokit: Octokit, runId: number) {
141+
return await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, {
142+
...context.repo,
143+
run_id: runId,
144+
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
145+
per_page: 100,
146+
});
171147
}
172148

173149
type WorkflowRunJobs = {
@@ -216,7 +192,6 @@ export {
216192
getPRLabels,
217193
getPRsLabels,
218194
type Octokit,
219-
type WorkflowArtifact,
220195
type WorkflowArtifactDownload,
221196
type WorkflowArtifactLookup,
222197
type WorkflowRunJob,

0 commit comments

Comments
 (0)