fix release fetch and GitHub release URL routing (#324)

Co-authored-by: Maxwell <145994855+ping-maxwell@users.noreply.github.com>
This commit is contained in:
Jake
2026-03-21 16:32:01 +10:00
committed by GitHub
co-authored by Maxwell
parent 64161af89c
commit 7acd6c4d08
9 changed files with 118 additions and 14 deletions
@@ -0,0 +1,17 @@
import { notFound, redirect } from "next/navigation";
import { getLatestRepoRelease } from "@/lib/github";
export default async function LatestReleaseRedirectPage({
params,
}: {
params: Promise<{ owner: string; repo: string }>;
}) {
const { owner, repo } = await params;
const release = await getLatestRepoRelease(owner, repo);
if (!release?.tag_name) {
notFound();
}
redirect(`/${owner}/${repo}/releases/${encodeURIComponent(release.tag_name)}`);
}
@@ -7,6 +7,8 @@ export async function GET(
},
) {
const { owner, repo, tag, filename } = await context.params;
// this route basically redirects users to the real github asset URL, if the extension is on it can loop unless that URL is allowed listed in extension rules
const githubUrl = `https://github.com/${owner}/${repo}/releases/download/${encodeURIComponent(tag)}/${filename.join("/")}`;
return NextResponse.redirect(githubUrl, { status: 302 });
}
@@ -0,0 +1,10 @@
import { redirect } from "next/navigation";
export default async function GitHubStyleReleaseTagPage({
params,
}: {
params: Promise<{ owner: string; repo: string; tag: string }>;
}) {
const { owner, repo, tag } = await params;
redirect(`/${owner}/${repo}/releases/${encodeURIComponent(tag)}`);
}
+25 -1
View File
@@ -129,6 +129,9 @@ export function toInternalUrl(htmlUrl: string): string {
if (type === "blob" && path) return `${base}/blob/${path}`;
if (type === "commits") return `${base}/commits`;
if (type === "commit" && path) return `${base}/commits/${path}`;
if (type === "releases") return `${base}/releases`;
if (type === "release" && path) return `${base}/releases/${encodeURIComponent(path)}`;
if (type === "release_latest") return `${base}/releases/_latest`;
if (type === "repo") return base;
return htmlUrl;
@@ -202,7 +205,17 @@ type ParsedGitHubUrl =
| {
owner: string;
repo: string;
type: "repo" | "pull" | "issue" | "tree" | "blob" | "commits" | "commit";
type:
| "repo"
| "pull"
| "issue"
| "tree"
| "blob"
| "commits"
| "commit"
| "releases"
| "release"
| "release_latest";
number?: number;
path?: string;
}
@@ -286,6 +299,17 @@ export function parseGitHubUrl(htmlUrl: string): ParsedGitHubUrl | null {
tag: rest[2],
filename: rest.slice(3).join("/"),
};
if (rest[0] === "releases" && rest.length === 1)
return { owner, repo, type: "releases" };
if (rest[0] === "releases" && rest[1] === "tag" && rest[2])
return {
owner,
repo,
type: "release",
path: decodeURIComponent(rest.slice(2).join("/")),
};
if (rest[0] === "releases" && rest[1] === "latest")
return { owner, repo, type: "release_latest" };
if (rest[0] === "actions") {
// /actions/runs/:runId or /actions/runs/:runId/jobs/:jobId
if (rest[1] === "runs" && rest[2]) {
+25 -13
View File
@@ -2850,7 +2850,7 @@ export async function getRepoReleases(owner: string, repo: string) {
const authCtx = await getGitHubAuthContext();
const cacheKey = buildRepoReleasesCacheKey(owner, repo);
return readLocalFirstGitData({
const releases = await readLocalFirstGitData({
authCtx,
cacheKey,
cacheType: "repo_releases",
@@ -2859,6 +2859,18 @@ export async function getRepoReleases(owner: string, repo: string) {
jobPayload: { owner, repo },
fetchRemote: (octokit) => fetchRepoReleasesFromGitHub(octokit, owner, repo),
});
if (releases.length > 0 || !authCtx?.octokit) return releases;
try {
const fresh = await fetchRepoReleasesFromGitHub(authCtx.octokit, owner, repo);
upsertGithubCacheEntry(authCtx.userId, cacheKey, "repo_releases", fresh).catch(
() => {},
);
return fresh;
} catch {
return releases;
}
}
export async function getRepoReleasesPage(owner: string, repo: string, page: number) {
@@ -2892,18 +2904,6 @@ export async function getRepoReleaseByTag(owner: string, repo: string, tag: stri
const authCtx = await getGitHubAuthContext();
if (!authCtx?.octokit) return null;
if (tag === "latest") {
try {
const { data } = await authCtx.octokit.repos.getLatestRelease({
owner,
repo,
});
return data;
} catch {
return null;
}
}
try {
const { data } = await authCtx.octokit.repos.getReleaseByTag({ owner, repo, tag });
return data;
@@ -2912,6 +2912,18 @@ export async function getRepoReleaseByTag(owner: string, repo: string, tag: stri
}
}
export async function getLatestRepoRelease(owner: string, repo: string) {
const authCtx = await getGitHubAuthContext();
if (!authCtx?.octokit) return null;
try {
const { data } = await authCtx.octokit.repos.getLatestRelease({ owner, repo });
return data;
} catch {
return null;
}
}
export async function getFileContent(owner: string, repo: string, path: string, ref?: string) {
const authCtx = await getGitHubAuthContext();
const normalizedRef = normalizeRef(ref);
+11
View File
@@ -173,6 +173,17 @@ function buildRules(host) {
resourceTypes: ["main_frame"],
},
},
// --- Allow release asset download URLs to prevent redirect loops ---
{
id: 251,
priority: 5,
action: { type: "allow" },
condition: {
regexFilter:
"^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
resourceTypes: ["main_frame"],
},
},
// --- Specific path rewrites ---
{
+9
View File
@@ -101,6 +101,15 @@
"resourceTypes": ["main_frame"]
}
},
{
"id": 37,
"priority": 5,
"action": { "type": "allow" },
"condition": {
"regexFilter": "^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
"resourceTypes": ["main_frame"]
}
},
{
"id": 12,
"priority": 5,
+10
View File
@@ -132,6 +132,16 @@ function buildRules(host) {
resourceTypes: ["main_frame"],
},
},
{
id: 251,
priority: 5,
action: { type: "allow" },
condition: {
regexFilter:
"^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
resourceTypes: ["main_frame"],
},
},
{
id: 101,
+9
View File
@@ -101,6 +101,15 @@
"resourceTypes": ["main_frame"]
}
},
{
"id": 37,
"priority": 5,
"action": { "type": "allow" },
"condition": {
"regexFilter": "^https://github\\.com/[^/]+/[^/]+/releases/download/.+",
"resourceTypes": ["main_frame"]
}
},
{
"id": 12,
"priority": 5,