authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-05 00:19:46-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-05 15:36:02-07:00
log8e3b7453999661e41945f9c6078d71c763ef2ec1
treebd5789de236d04553d1311344ee0b9de48579fa9
parentd7e04000d859a9030b8475d00cf8b411b7343956
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

Handle every Git CGI error status

Cover method and content-type errors alongside permission and backend failures, and record the cloned-repository push verification. Assisted-by: gpt-6.1-sol

3 files changed, 8 insertions(+), 2 deletions(-)

service/shale/prepare.py+1-1
......@@ -1,5 +1,5 @@
11#!/usr/bin/env python3
2"""Keep Shale identities attached to their existing accounts when the issuer moves."""
2"""Prepare owned Git repositories and preserve Shale account bindings."""
33import json
44import os
55from pathlib import Path
tools/router.py+1-1
......@@ -87,7 +87,7 @@ def shale_git_routes(upstreams):
8787 lines = [" @shale_git path */info/refs */git-upload-pack */git-receive-pack",
8888 " handle @shale_git {", f" reverse_proxy {upstreams} {{"]
8989 # Shale forwards CGI Status as a header instead of the HTTP status.
90 for status in (403, 404, 500):
90 for status in (400, 403, 404, 405, 415, 500):
9191 lines += [f' @cgi_{status} header Status "{status} *"',
9292 f" handle_response @cgi_{status} {{",
9393 " header {", " -Status", " defer", " }",
tools/shale-migration.md+6
......@@ -67,3 +67,9 @@ Zenith's Shale app directory contains a small SQLite database and 419 MB of owne
6767For the production copy, stop Zenith's Shale container and the Snow Globe Shale job, set `STUDIO_DEPLOY_HOST` and `STUDIO_DEPLOY_PORT` for the new host, then run `bash tools/import-shale.sh shale`. The importer checks both jobs remain stopped, snapshots the destination dataset, verifies all three copied directories, and leaves Snow Globe stopped. Start the new job after the copy, check the SQLite state and a known login through the new Keycloak client, then switch the public route. The destination snapshot printed by the importer remains available for recovery.
6868
6969After a same-machine OS replacement, set `STUDIO_LEGACY_HANDOFF` to the [offline handoff](legacy-handoff.md) directory as well. The importer then reads the retained Shale directory from the new host's mounted old apps dataset, with no old Docker dependency.
70
71## Imported repository HTTP pushes
72
73The Forgejo importer originally omitted `http.receivepack=true`, which Shale sets when it creates an owned repository. Shale checks its push ACL before invoking `git-http-backend`, but does not set `REMOTE_USER`; Git's default therefore rejected authorized pushes to imported repositories. The importer now writes the setting, and Shale's prepare step repairs missing settings without replacing an explicit disable. Production `config` remains owner-only for pushes.
74
75Shale also forwards CGI `Status` as an ordinary header on HTTP 200. The Git-specific Caddy handler translates Git's error statuses into HTTP statuses and removes the CGI header. A disposable production database/repository copy verified anonymous and invalid credentials return 401, authenticated non-owners return 403, owner discovery advertises `main=37665e69e1aa954f0dec06b1cafa1612f44dc907`, and an actual `jj git push --bookmark main` advances the clone to `b8e734ce6aabd94a7e1aacfc989467662ca27dbe`. Disabled receive-pack returns actual HTTP 403; invalid method/content type return 400/415 without a `Status` header. No production token was added.