git.lucas.co / gitsite
generator for git.lucas.co
git clone https://git.lucas.co/gitsite.git

commit3a49ee23163b13baa6439031307388275a6d3800
parente3d1fbf59e
authorLucas Galante <lsgalante12@gmail.com>
date2026-08-10 22:02
Fix Cloudflare deploy: pin production branch, real 404s, no loose objects

- deploy.sh passes --branch=main so deploys go to production
- 404.html disables Pages SPA fallback, which served index.html with a
  200 for missing paths and broke git's loose-object probing
- repack clone-mode mirrors every build so only packfiles ship
- _headers marks git transport files no-transform

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

 .wrangler/cache/pages.json            |  4 ++++
 .wrangler/cache/wrangler-account.json |  6 ++++++
 build.sh                              |  2 ++
 deploy.sh                             |  2 +-
 generate.py                           | 21 +++++++++++++++++++++
 5 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/.wrangler/cache/pages.json b/.wrangler/cache/pages.json
new file mode 100644
index 0000000..83f0c24
--- /dev/null
+++ b/.wrangler/cache/pages.json
@@ -0,0 +1,4 @@
+{
+  "account_id": "16cc27b259a59a98845182903fee02e4",
+  "project_name": "git-lucas-co"
+}
\ No newline at end of file
diff --git a/.wrangler/cache/wrangler-account.json b/.wrangler/cache/wrangler-account.json
new file mode 100644
index 0000000..54da4d5
--- /dev/null
+++ b/.wrangler/cache/wrangler-account.json
@@ -0,0 +1,6 @@
+{
+  "account": {
+    "id": "16cc27b259a59a98845182903fee02e4",
+    "name": "Lsgalante12@gmail.com's Account"
+  }
+}
\ No newline at end of file
diff --git a/build.sh b/build.sh
index 43f86fd..6152124 100755
--- a/build.sh
+++ b/build.sh
@@ -25,6 +25,8 @@ python3 "$BASE/generate.py"
 grep -v '^#' "$BASE/repos.conf" | grep -v '^$' | while IFS='|' read -r name path desc mode; do
     if [ "$mode" = "clone" ]; then
         m="$CACHE/mirrors/$name.git"
+        # no loose objects: Cloudflare mangles them in transit; packs survive
+        git -C "$m" repack -a -d -q --max-pack-size=20m
         git -C "$m" update-server-info
         cp -al "$m" "$CACHE/out/$name.git"
     fi
diff --git a/deploy.sh b/deploy.sh
index 9a5a490..4620906 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -6,4 +6,4 @@ set -e
 OUT="$HOME/.cache/gitsite/out"
 [ -d "$OUT" ] || { echo "no build output; run ./build.sh first" >&2; exit 1; }
 
-npx wrangler pages deploy "$OUT" --project-name=git-lucas-co
+npx wrangler pages deploy "$OUT" --project-name=git-lucas-co --branch=main --commit-dirty=true
diff --git a/generate.py b/generate.py
index 179a4ff..dc328fd 100755
--- a/generate.py
+++ b/generate.py
@@ -218,6 +218,25 @@ def gen_refs(repo, mirror, repo_dir):
                rel(repo_dir, OUT))
 
 
+def gen_404():
+    # a real 404.html switches Pages out of SPA-fallback mode; without it,
+    # unknown paths (e.g. git probing loose objects) get index.html with a 200
+    body = (f'<div class="crumbs"><a href="index.html">{SITE_TITLE}</a></div>\n'
+            '<hr>\n<div class="notice">not found</div>\n')
+    write_page(OUT / "404.html", f"{SITE_TITLE}: not found", body, "")
+
+
+def gen_headers(repos):
+    # keep Cloudflare from recompressing/transforming git transport files
+    rules = ""
+    for repo in repos:
+        if repo["clone"]:
+            rules += (f'/{repo["name"]}.git/*\n'
+                      "  Cache-Control: no-transform\n"
+                      "  Content-Type: application/octet-stream\n")
+    (OUT / "_headers").write_text(rules)
+
+
 def gen_index(repos):
     body = (f'<div class="crumbs">{SITE_TITLE}</div>\n'
             f'<div class="desc"><a href="{HOME_URL}">Lucas Galante</a>\'s projects</div>\n<hr>\n')
@@ -258,6 +277,8 @@ def main():
         gen_refs(repo, mirror, repo_dir)
         print(f'{repo["name"]}: {len(commits)} commits, {len(entries)} files')
 
+    gen_404()
+    gen_headers(repos)
     gen_index(repos)
     print(f"wrote {OUT}")