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 @@...@@ -1,5 +1,5 @@
1#!/usr/bin/env python31#!/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."""
3import json3import json
4import os4import os
5from pathlib import Path5from pathlib import Path
tools/router.py+1-1
...@@ -87,7 +87,7 @@ def shale_git_routes(upstreams):...@@ -87,7 +87,7 @@ def shale_git_routes(upstreams):
87 lines = [" @shale_git path */info/refs */git-upload-pack */git-receive-pack",87 lines = [" @shale_git path */info/refs */git-upload-pack */git-receive-pack",
88 " handle @shale_git {", f" reverse_proxy {upstreams} {{"]88 " handle @shale_git {", f" reverse_proxy {upstreams} {{"]
89 # Shale forwards CGI Status as a header instead of the HTTP status.89 # 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):
91 lines += [f' @cgi_{status} header Status "{status} *"',91 lines += [f' @cgi_{status} header Status "{status} *"',
92 f" handle_response @cgi_{status} {{",92 f" handle_response @cgi_{status} {{",
93 " header {", " -Status", " defer", " }",93 " 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...@@ -67,3 +67,9 @@ Zenith's Shale app directory contains a small SQLite database and 419 MB of owne
67For 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.67For 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
69After 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.69After 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.