Redirect bare /<owner>/<repo>.git URLs to the clean scoreboard
Pasting the clone URL into a browser previously fell through the isGitProtocol check (path ends with .git) and got proxied straight to Forgejo, which happily rendered its own repo page — Forgejo's chrome was bleeding onto tdd.md. Real git operations always have sub-paths (/info/refs?service=..., /git-upload-pack, /objects/...). The plain /<owner>/<repo>.git URL is only ever visited by humans. So: 302 it to /<owner>/<repo> before the protocol check kicks in. Clones, fetches and pushes unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 file changed · +17 −0
src/server.ts
+17
−0
| @@ -1087,6 +1087,23 @@ When you push, the judge replays your commits and posts the verdict at [/agents/ | ||
| 1087 | 1087 | async fetch(req) { |
| 1088 | 1088 | const url = new URL(req.url); |
| 1089 | 1089 | |
| 1090 | + // Bare /<owner>/<repo>.git (no sub-path) is what someone gets when | |
| 1091 | + // they paste the clone URL into a browser. Without intervention our | |
| 1092 | + // proxy hands it to Forgejo, which renders its own repo page — | |
| 1093 | + // Forgejo's chrome leaks onto tdd.md. Redirect to the clean URL | |
| 1094 | + // so the visitor lands on our Bun-native scoreboard instead. Real | |
| 1095 | + // git operations always have sub-paths (/info/refs, /git-upload-pack, | |
| 1096 | + // /objects/...) and continue to be proxied below. | |
| 1097 | + const bareGitUrl = url.pathname.match( | |
| 1098 | + /^\/([A-Za-z0-9][A-Za-z0-9-]*)\/([A-Za-z0-9][A-Za-z0-9._-]*)\.git\/?$/, | |
| 1099 | + ); | |
| 1100 | + if (bareGitUrl) { | |
| 1101 | + return new Response(null, { | |
| 1102 | + status: 302, | |
| 1103 | + headers: { Location: `/${bareGitUrl[1]}/${bareGitUrl[2]}` }, | |
| 1104 | + }); | |
| 1105 | + } | |
| 1106 | + | |
| 1090 | 1107 | // Git smart-HTTP and dumb-HTTP — proxy raw to Forgejo. |
| 1091 | 1108 | if (isGitProtocol(url.pathname, url.searchParams)) { |
| 1092 | 1109 | return proxyToForgejo(req, url.pathname + url.search); |