summaryrefslogtreecommitdiff
path: root/cgithub/src
diff options
context:
space:
mode:
Diffstat (limited to 'cgithub/src')
-rw-r--r--cgithub/src/app.ts6
-rw-r--r--cgithub/src/scraper.test.ts28
-rw-r--r--cgithub/src/scraper.ts39
3 files changed, 73 insertions, 0 deletions
diff --git a/cgithub/src/app.ts b/cgithub/src/app.ts
index ca79b1f..e34c7b1 100644
--- a/cgithub/src/app.ts
+++ b/cgithub/src/app.ts
@@ -5,6 +5,7 @@ import {
getGitHubBlob,
getGitHubCommits,
getGitHubIssues,
+ getGitHubLanguages,
getGitHubLatestCommit,
getGitHubOwner,
getGitHubPulls,
@@ -158,6 +159,11 @@ export function createApp(eta: Eta) {
);
});
+ app.get("/api/:owner/:repo/languages", async (c) => {
+ const { owner, repo } = c.req.param();
+ return tryRenderFragment(c, "_languages.eta", getGitHubLanguages(owner, repo));
+ });
+
app.get("/:owner/:repo/releases/tag/:tag", async (c) => {
const { owner, repo, tag } = c.req.param();
return tryRender(c, "release.eta", getGitHubRelease(owner, repo, tag));
diff --git a/cgithub/src/scraper.test.ts b/cgithub/src/scraper.test.ts
index 56f14b0..5b5e78b 100644
--- a/cgithub/src/scraper.test.ts
+++ b/cgithub/src/scraper.test.ts
@@ -4,6 +4,7 @@ import {
getGitHubBlob,
getGitHubCommits,
getGitHubIssues,
+ getGitHubLanguages,
getGitHubLatestCommit,
getGitHubPulls,
getGitHubRefs,
@@ -219,6 +220,33 @@ describe("GitHub scraper", () => {
assert.ok(data.commit.authors.length > 0);
});
+ it("should fetch languages", async () => {
+ const data = await getGitHubLanguages("actions", "deploy-pages");
+
+ assert.strictEqual(data.repo.owner, "actions");
+ assert.strictEqual(data.repo.name, "deploy-pages");
+
+ assert.deepStrictEqual(data.languages, [
+ { name: "JavaScript", percentage: 100, color: "#f1e05a" },
+ ]);
+ });
+
+ it("should fetch languages for a multi-language repository", async () => {
+ const data = await getGitHubLanguages("rust-lang", "rust");
+
+ const rust = data.languages.find((language) => language.name === "Rust");
+ assert.ok(rust);
+ assert.ok(rust.percentage > 50);
+ assert.strictEqual(rust.color, "#dea584");
+
+ // The aggregate bucket comes back without a name.
+ const other = data.languages.find((language) => language.name === "Other");
+ assert.ok(other);
+
+ const total = data.languages.reduce((sum, language) => sum + language.percentage, 0);
+ assert.ok(Math.abs(total - 100) < 1);
+ });
+
it("should fetch branches", async () => {
const data = await getGitHubRefs("actions", "deploy-pages", "branches");
diff --git a/cgithub/src/scraper.ts b/cgithub/src/scraper.ts
index 39ad2cd..223839f 100644
--- a/cgithub/src/scraper.ts
+++ b/cgithub/src/scraper.ts
@@ -285,6 +285,17 @@ export interface GitHubLatestCommit extends GitHubCommon {
commit: LatestCommit;
}
+export interface Language {
+ // "Other" for the aggregate bucket GitHub reports without a name.
+ name: string;
+ percentage: number;
+ color: string | null;
+}
+
+export interface GitHubLanguages extends GitHubCommon {
+ languages: Language[];
+}
+
export interface Ref {
name: string;
isDefault?: boolean;
@@ -722,6 +733,34 @@ export async function getGitHubLatestCommit(
};
}
+interface SidebarPayload {
+ languages?: {
+ languages: {
+ // Absent for the "Other" bucket.
+ name?: string;
+ percentage: number;
+ color?: string;
+ }[];
+ };
+}
+
+// The repository sidebar (languages, contributors, ...) is not rendered into
+// the repo page at all; GitHub's own UI lazily fetches it as JSON from here.
+export async function getGitHubLanguages(owner: string, repo: string): Promise<GitHubLanguages> {
+ const payload = await fetchGitHubJson<SidebarPayload>(`${owner}/${repo}/_sidebar`);
+
+ const languages: Language[] = [];
+ for (const entry of payload.languages?.languages ?? []) {
+ languages.push({
+ name: entry.name ?? "Other",
+ percentage: entry.percentage,
+ color: entry.color ?? null,
+ });
+ }
+
+ return { repo: { owner, name: repo }, languages };
+}
+
interface BranchRefEntry {
name: string;
isDefault: boolean;