summaryrefslogtreecommitdiff
path: root/cgithub/src
diff options
context:
space:
mode:
Diffstat (limited to 'cgithub/src')
-rw-r--r--cgithub/src/index.ts6
-rw-r--r--cgithub/src/scraper.test.ts14
-rw-r--r--cgithub/src/scraper.ts63
3 files changed, 83 insertions, 0 deletions
diff --git a/cgithub/src/index.ts b/cgithub/src/index.ts
index 4486dff..2f8efef 100644
--- a/cgithub/src/index.ts
+++ b/cgithub/src/index.ts
@@ -9,6 +9,7 @@ import {
getGitHubBlob,
getGitHubCommits,
getGitHubIssues,
+ getGitHubReleases,
getGitHubRepo,
getGitHubTree,
GitHubHTTPError,
@@ -73,6 +74,11 @@ app.get("/:owner/:repo/commits/:branch/:path{.*}?", async (c) => {
return tryRender(c, "commits.eta", getGitHubCommits(owner, repo, branch, path));
});
+app.get("/:owner/:repo/releases", async (c) => {
+ const { owner, repo } = c.req.param();
+ return tryRender(c, "releases.eta", getGitHubReleases(owner, repo));
+});
+
app.all("*", async (c) => {
const path = c.req.path;
c.header("Referrer-Policy", "no-referrer");
diff --git a/cgithub/src/scraper.test.ts b/cgithub/src/scraper.test.ts
index 7cafd9c..8c11317 100644
--- a/cgithub/src/scraper.test.ts
+++ b/cgithub/src/scraper.test.ts
@@ -4,6 +4,7 @@ import {
getGitHubBlob,
getGitHubCommits,
getGitHubIssues,
+ getGitHubReleases,
getGitHubRepo,
getGitHubTree,
GitHubHTTPError,
@@ -159,6 +160,19 @@ describe("GitHub scraper", () => {
assert.ok(data.commitGroups.length > 0);
});
+
+ it("should fetch releases", async () => {
+ const data = await getGitHubReleases("actions", "deploy-pages");
+
+ assert.strictEqual(data.repo.owner, "actions");
+ assert.strictEqual(data.repo.name, "deploy-pages");
+
+ assert.ok(data.releases.length > 0);
+ const firstRelease = data.releases[0];
+ assert.ok(firstRelease.tagName.length > 0);
+ assert.ok(firstRelease.title.match(/^v\d/));
+ assert.ok(firstRelease.publishedAt.length > 0);
+ });
});
describe("rich file types", () => {
diff --git a/cgithub/src/scraper.ts b/cgithub/src/scraper.ts
index 71d0e3e..e52ddac 100644
--- a/cgithub/src/scraper.ts
+++ b/cgithub/src/scraper.ts
@@ -184,6 +184,17 @@ export interface GitHubCommits extends GitHubNav {
commitGroups: CommitGroup[];
}
+interface Release {
+ tagName: string;
+ title: string;
+ publishedAt: string;
+ bodyHtml: string;
+}
+
+export interface GitHubReleases extends GitHubCommon {
+ releases: Release[];
+}
+
async function fetchGitHubPage(path: string): Promise<string> {
// GitHub throttles/blocks requests without realistic browser headers.
// These headers make the request appear as a standard browser visit.
@@ -403,3 +414,55 @@ export async function getGitHubCommits(
commitGroups: payload.commitGroups,
};
}
+
+export async function getGitHubReleases(owner: string, repo: string): Promise<GitHubReleases> {
+ const html = await fetchGitHubPage(`${owner}/${repo}/releases`);
+
+ // Releases page doesn't have embedded JSON, so we parse HTML directly
+ const releases: Release[] = [];
+
+ // Find all h2 tags with version numbers
+ const h2Regex = /<h2[^>]*id="([^"]*)"[^>]*>(v[^<]*)<\/h2>/g;
+
+ for (const h2Match of html.matchAll(h2Regex)) {
+ const id = h2Match[1];
+ const title = h2Match[2].trim();
+
+ // Find the section containing this h2
+ const sectionRegex = new RegExp(
+ `<section[^>]*>\\s*<h2[^>]*id="${id}"[^>]*>[^<]*<\\/h2>([\\s\\S]*?)<\\/section>`,
+ "s",
+ );
+ const sectionMatch = html.match(sectionRegex);
+ if (!sectionMatch) continue;
+
+ const sectionHtml = sectionMatch[1];
+
+ // Extract tag name from link
+ const tagMatch = sectionHtml.match(/<a[^>]*href="[^"]*\/tree\/([^"]+)"[^>]*>/);
+ const tagName = tagMatch?.[1] || title;
+
+ // Extract published date
+ const dateMatch = sectionHtml.match(/<relative-time[^>]*datetime="([^"]*)"[^>]*>/);
+ const publishedAt = dateMatch?.[1] || "";
+
+ // Extract body HTML (markdown content)
+ const bodyMatch = sectionHtml.match(/<div[^>]*class="[^"]*markdown-body[^"]*"[^>]*>([\s\S]*?)<\/div>/);
+ const bodyHtml = bodyMatch?.[1]?.trim() || "";
+
+ releases.push({
+ tagName,
+ title,
+ publishedAt,
+ bodyHtml,
+ });
+ }
+
+ return {
+ repo: {
+ owner,
+ name: repo,
+ },
+ releases,
+ };
+}