Skip to content

Commit

Permalink
Add open PRs to dashboard
Browse files Browse the repository at this point in the history
It will be useful to know how many open PRs a repo has. In future we can
make this even more useful but showing a column for 'Open Renovate PRs'.

In fetchAllRepos we are doing a `Promise.all` on the secondary requests
for data so the requests can run concurrently.
The seeding script takes a long time to run at the moment but with
change their shouldn't be too much of an extra penalty for further
concurrent API requests.
  • Loading branch information
rich committed Sep 12, 2024
1 parent cfd16ac commit 4a30fc3
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 20 deletions.
12 changes: 12 additions & 0 deletions dataScripts/allOwners.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getReposFromJson } from "../utils.js";

const allOwners = async () => {
const { repos } = await getReposFromJson("./data/repos.json");

repos
.map((repo) => {
console.log(repo.topics);
})
.flat();
};
await allOwners();
20 changes: 16 additions & 4 deletions dataScripts/fetchAllRepos.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@ import { OctokitApp } from "../octokitApp.js";
import { writeFile, mkdir } from "fs/promises";
import { mapRepoFromApiForStorage } from "../utils.js";
import path from "path";
import { getDependenciesForRepo } from "../renovate/dependencyDashboard.js";
import {
getDependenciesForRepo,
getOpenPRsForRepo,
} from "../renovate/dependencyDashboard.js";

const fetchAllRepos = async () => {
const repos = [];

await OctokitApp.app.eachRepository(async ({ repository, octokit }) => {
if (repository.archived) return;

repository.dependencies = await getDependenciesForRepo({
repository,
octokit,
await Promise.all([
await getDependenciesForRepo({
repository,
octokit,
}),
await getOpenPRsForRepo({
repository,
octokit,
}),
]).then(([dependencies, openPrsCount]) => {
repository.dependencies = dependencies;
repository.openPrsCount = openPrsCount;
});

repos.push(mapRepoFromApiForStorage(repository));
Expand Down
2 changes: 2 additions & 0 deletions index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<th class="py-2 pl-2" scope="col">Language</th>
<th class="py-2 pl-2" scope="col">Topics</th>
<th class="py-2 pl-2" scope="col">Open issues count</th>
<th class="py-2 pl-2" scope="col">Open PRs count</th>
<th class="py-2 pl-2 last:pr-2" scope="col">Last updated</th>
<th class="py-2 pl-2 last:pr-2" scope="col">Dependencies</th>
</tr>
Expand All @@ -47,6 +48,7 @@
<td class="py-2 pl-2">{{ repo.language }}</td>
<td class="py-2 pl-2">{{ repo.topics | join(", ")}}</td>
<td class="py-2 pl-2">{{ repo.openIssues }}</td>
<td class="py-2 pl-2">{{ repo.openPrsCount }}</td>
<td class="py-2 pl-2 last:pr-2">{{ repo.updatedAt }}</td>

{% if repo.dependencies.length %}
Expand Down
27 changes: 18 additions & 9 deletions renovate/dependencyDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ const LINE_SEPARATOR_REGEX = /\r?\n|\r|\n/g;
// Group 2: ^7.20.2
const DEPENDENCY_NAME_AND_VERSION_REGEX = /`(\S+?) (.*)`/;

const issueIsRenovateDependencyDashboard = (issue) => issue.user.login === "renovate[bot]" && issue.pull_request === undefined;
const issueIsRenovateDependencyDashboard = (issue) =>
issue.user.login === "renovate[bot]" && issue.pull_request === undefined;

const parseDependenciesFromDashboard = (issue) => issue
.body
.split(LINE_SEPARATOR_REGEX)
.map(parseDependencyFromLine)
.filter((dependency) => dependency !== null);
const parseDependenciesFromDashboard = (issue) =>
issue.body
.split(LINE_SEPARATOR_REGEX)
.map(parseDependencyFromLine)
.filter((dependency) => dependency !== null);

const parseDependencyFromLine = (line) => {
const match = line.match(DEPENDENCY_NAME_AND_VERSION_REGEX);
Expand All @@ -41,18 +42,26 @@ const parseDependencyFromLine = (line) => {
}

return new Dependency(match[1], match[2]);
}
};

export const handleIssuesApiResponse = (response) => {
const dependencyDashboardIssue = response.data.find(issueIsRenovateDependencyDashboard);
const dependencyDashboardIssue = response.data.find(
issueIsRenovateDependencyDashboard
);

if (!dependencyDashboardIssue) {
return [];
}

return parseDependenciesFromDashboard(dependencyDashboardIssue);
}
};

export const handlePrsApiResponse = ({ data }) => data?.length || 0;

export const getDependenciesForRepo = ({ octokit, repository }) => {
return octokit.request(repository.issues_url).then(handleIssuesApiResponse);
};

export const getOpenPRsForRepo = ({ octokit, repository }) => {
return octokit.request(repository.pulls_url).then(handlePrsApiResponse);
};
28 changes: 21 additions & 7 deletions renovate/dependencyDashboard.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { describe, it } from "node:test";
import expect from "node:assert";
import { Dependency, handleIssuesApiResponse } from "./dependencyDashboard.js";
import {
Dependency,
handleIssuesApiResponse,
handlePrsApiResponse,
} from "./dependencyDashboard.js";

describe("handleIssuesApiResponse", () => {
it("should extract dependency version information from the Renovate Dependency Dashboard if it exists", async () => {
Expand All @@ -24,9 +28,9 @@ describe("handleIssuesApiResponse", () => {
user: {
login: "renovate[bot]",
},
body: "# Dependency Dashboard\nList of dependencies:\n- `libquux v4.1.1.rc4`\n- `@xyzzy/utils \"~> 22.04 Questing Quokka\"`\n\nHere's some more:\n- `baz-framework ^0.1`",
}
]
body: '# Dependency Dashboard\nList of dependencies:\n- `libquux v4.1.1.rc4`\n- `@xyzzy/utils "~> 22.04 Questing Quokka"`\n\nHere\'s some more:\n- `baz-framework ^0.1`',
},
],
};

const expectedDependencies = [
Expand All @@ -37,7 +41,7 @@ describe("handleIssuesApiResponse", () => {

expect.deepEqual(
handleIssuesApiResponse(issuesApiResponse),
expectedDependencies,
expectedDependencies
);
});

Expand All @@ -58,14 +62,24 @@ describe("handleIssuesApiResponse", () => {
pull_request: {},
body: "Configure Renovate",
},
]
],
};

const expectedDependencies = [];

expect.deepEqual(
handleIssuesApiResponse(issuesApiResponse),
expectedDependencies,
expectedDependencies
);
});

describe("handlePrsApiResponse", () => {
it("returns the length of the array containing PRs", () => {
expect.equal(handlePrsApiResponse({ data: [1, 2, 3] }), 3);
});

it("returns 0 if there are no open PRs", () => {
expect.equal(handlePrsApiResponse({ data: undefined }), 0);
});
});
});
1 change: 1 addition & 0 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ export const mapRepoFromApiForStorage = (repo) => ({
topics: repo.topics,
openIssues: repo.open_issues,
dependencies: repo.dependencies,
openPrsCount: repo.openPrsCount,
});
2 changes: 2 additions & 0 deletions utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ describe("mapRepoFromStorageToUi", () => {
pull: false,
},
dependencies: repoDependencies,
openPrsCount: 0,
};

const repoToSave = {
Expand All @@ -270,6 +271,7 @@ describe("mapRepoFromStorageToUi", () => {
topics: ["delivery-plus", "internal", "tech-ops"],
openIssues: 2,
dependencies: repoDependencies,
openPrsCount: 0,
};

expect.deepEqual(mapRepoFromApiForStorage(apiRepo), repoToSave);
Expand Down

0 comments on commit 4a30fc3

Please sign in to comment.