Skip to content

Commit

Permalink
Simplify paginated octokit queries (#28)
Browse files Browse the repository at this point in the history
- **Simplify listJobsForWorkflowRun**
- **Simplify listWorkflowRunArtifacts**
- **update changelog**
  • Loading branch information
corentinmusard authored Dec 30, 2024
1 parent 5edaab2 commit ea4acdb
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 90 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Simplify paginated octokit queries
- Use global tracer instead of passing it around
- tests: Add a replay client
- Migrate to ESM
Expand Down
39 changes: 12 additions & 27 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49115,19 +49115,12 @@ async function listWorkflowRunArtifacts(context, octokit, runId) {
}
const artifactNameRegex = /\{(?<jobName>.*)\}\{(?<stepName>.*)\}/;
async function getWorkflowRunArtifactMap(context, octokit, runId) {
const artifactsList = [];
const pageSize = 100;
for (let page = 1, hasNext = true; hasNext; page++) {
const listArtifactsResponse = await octokit.rest.actions.listWorkflowRunArtifacts({
...context.repo,
run_id: runId,
page,
per_page: pageSize,
});
artifactsList.push(...listArtifactsResponse.data.artifacts);
hasNext = artifactsList.length < listArtifactsResponse.data.total_count;
}
const artifactsLookup = await artifactsList.reduce(async (resultP, artifact) => {
const artifacts = await octokit.paginate(octokit.rest.actions.listWorkflowRunArtifacts, {
...context.repo,
run_id: runId,
per_page: 100,
});
const artifactsLookup = await artifacts.reduce(async (resultP, artifact) => {
const result = await resultP;
const match = artifact.name.match(artifactNameRegex);
const next = { ...result };
Expand Down Expand Up @@ -49202,20 +49195,12 @@ async function getSelfArtifactMap() {
return artifactsMap;
}
async function listJobsForWorkflowRun(context, octokit, runId) {
const jobs = [];
const pageSize = 100;
for (let page = 1, hasNext = true; hasNext; page++) {
const listJobsForWorkflowRunResponse = await octokit.rest.actions.listJobsForWorkflowRun({
...context.repo,
run_id: runId,
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
page,
per_page: pageSize,
});
jobs.push(...listJobsForWorkflowRunResponse.data.jobs);
hasNext = jobs.length < listJobsForWorkflowRunResponse.data.total_count;
}
return jobs;
return await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, {
...context.repo,
run_id: runId,
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
per_page: 100,
});
}
async function getWorkflowRunJobs(context, octokit, runId) {
const getWorkflowRunResponse = await octokit.rest.actions.getWorkflowRun({
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/__assets__/run.rec

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/github/__assets__/getWorkflowRunJobs.rec

Large diffs are not rendered by default.

26 changes: 10 additions & 16 deletions src/github/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import * as path from "node:path";
import * as url from "node:url";
import type { Context } from "@actions/github/lib/context";
import { jest } from "@jest/globals";
import type { components } from "@octokit/openapi-types";
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
import fetchMock from "jest-fetch-mock";
import { mock, mockDeep } from "jest-mock-extended";
import type { Octokit } from "./github";
import { type WorkflowArtifact, type WorkflowArtifactDownload, listWorkflowRunArtifacts } from "./github";
import { type WorkflowArtifactDownload, listWorkflowRunArtifacts } from "./github";

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

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

type ListWorkflowRunArtifactsResponse = RestEndpointMethodTypes["actions"]["listWorkflowRunArtifacts"]["response"];
type Artifacts = components["schemas"]["artifact"][];
type DownloadArtifactResponse = RestEndpointMethodTypes["actions"]["downloadArtifact"]["response"];

describe("listWorkflowRunArtifacts", () => {
Expand All @@ -26,25 +27,18 @@ describe("listWorkflowRunArtifacts", () => {
beforeAll(async () => {
mockContext = mockDeep<Context>();
mockOctokit = mockDeep<Octokit>();
const mockListWorkflowRunArtifacts = mockOctokit.rest.actions.listWorkflowRunArtifacts as jest.MockedFunction<
typeof mockOctokit.rest.actions.listWorkflowRunArtifacts
>;
const mockPaginate = mockOctokit.paginate as jest.MockedFunction<typeof mockOctokit.paginate>;
const mockDownloadArtifact = mockOctokit.rest.actions.downloadArtifact as jest.MockedFunction<
typeof mockOctokit.rest.actions.downloadArtifact
>;

mockListWorkflowRunArtifacts.mockResolvedValue(
mock<ListWorkflowRunArtifactsResponse>({
data: {
total_count: 1,
artifacts: [
mock<WorkflowArtifact>({
id: 1,
name: "{lint-and-test}{run tests}",
}),
],
mockPaginate.mockResolvedValue(
mock<Artifacts>([
{
id: 1,
name: "{lint-and-test}{run tests}",
},
}),
]),
);
mockDownloadArtifact.mockResolvedValue(mock<DownloadArtifactResponse>({ url: "localhost" }));
const filePath = path.join(__dirname, "__assets__", "{lint-and-test}{run tests}.zip");
Expand Down
51 changes: 13 additions & 38 deletions src/github/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ type ListJobsForWorkflowRunType = RestEndpointMethodTypes["actions"]["listJobsFo
type WorkflowRunJob = ListJobsForWorkflowRunType["data"]["jobs"][number];
type WorkflowRun = RestEndpointMethodTypes["actions"]["getWorkflowRun"]["response"]["data"];

interface WorkflowArtifact {
id: number;
name: string;
}

type WorkflowArtifactMap = {
[job: string]: {
[step: string]: WorkflowArtifactDownload;
Expand Down Expand Up @@ -55,21 +50,13 @@ async function listWorkflowRunArtifacts(
const artifactNameRegex = /\{(?<jobName>.*)\}\{(?<stepName>.*)\}/;

async function getWorkflowRunArtifactMap(context: Context, octokit: Octokit, runId: number) {
const artifactsList: WorkflowArtifact[] = [];
const pageSize = 100;

for (let page = 1, hasNext = true; hasNext; page++) {
const listArtifactsResponse = await octokit.rest.actions.listWorkflowRunArtifacts({
...context.repo,
run_id: runId,
page,
per_page: pageSize,
});
artifactsList.push(...listArtifactsResponse.data.artifacts);
hasNext = artifactsList.length < listArtifactsResponse.data.total_count;
}
const artifacts = await octokit.paginate(octokit.rest.actions.listWorkflowRunArtifacts, {
...context.repo,
run_id: runId,
per_page: 100,
});

const artifactsLookup: WorkflowArtifactMap = await artifactsList.reduce(async (resultP, artifact) => {
const artifactsLookup: WorkflowArtifactMap = await artifacts.reduce(async (resultP, artifact) => {
const result = await resultP;
const match = artifact.name.match(artifactNameRegex);
const next: WorkflowArtifactMap = { ...result };
Expand Down Expand Up @@ -150,24 +137,13 @@ async function getSelfArtifactMap() {
return artifactsMap;
}

async function listJobsForWorkflowRun(context: Context, octokit: Octokit, runId: number): Promise<WorkflowRunJob[]> {
const jobs: WorkflowRunJob[] = [];
const pageSize = 100;

for (let page = 1, hasNext = true; hasNext; page++) {
const listJobsForWorkflowRunResponse = await octokit.rest.actions.listJobsForWorkflowRun({
...context.repo,
run_id: runId,
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
page,
per_page: pageSize,
});

jobs.push(...listJobsForWorkflowRunResponse.data.jobs);
hasNext = jobs.length < listJobsForWorkflowRunResponse.data.total_count;
}

return jobs;
async function listJobsForWorkflowRun(context: Context, octokit: Octokit, runId: number) {
return await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, {
...context.repo,
run_id: runId,
filter: "latest", // risk of missing a run if re-run happens between Action trigger and this query
per_page: 100,
});
}

type WorkflowRunJobs = {
Expand Down Expand Up @@ -216,7 +192,6 @@ export {
getPRLabels,
getPRsLabels,
type Octokit,
type WorkflowArtifact,
type WorkflowArtifactDownload,
type WorkflowArtifactLookup,
type WorkflowRunJob,
Expand Down

0 comments on commit ea4acdb

Please sign in to comment.