Fix sidecar: advance refs/heads/main after each commit

post-receive uses `git checkout -f <SHA>` which detaches HEAD in the bare
repo. Without this fix, sidecar commits advance the detached HEAD but not
refs/heads/main and are orphaned on the next push.

Also commits CLAUDE.md docs update and the retry-loop sync-vps.sh.
This commit is contained in:
brutsalvadi
2026-05-08 08:57:49 +02:00
parent e9dceafc27
commit a85a2eeb6d
3 changed files with 53 additions and 2 deletions
+29 -1
View File
@@ -12,8 +12,11 @@ pointing to `../pages` and `../blog` (outside the submodule). No symlinks.
``` ```
bincio_wiki/ bincio_wiki/
pages/ wiki content (*.md files the community edits) pages/ wiki content (*.md files the community edits)
_docs/ software documentation (shown as separate section) _docs/ documentation shown as a separate section
blog/ blog/stories content (*.md files) blog/ blog/stories content (*.md files)
config/ bincio-specific configuration (outside site/ submodule)
sections.json wiki sections and subsections (imported by Astro pages + editor)
i.bonsai.md wikibonsai semantic tree (loaded by the index collection)
assets/ user-uploaded images (gitignored; rsync'd to VPS separately) assets/ user-uploaded images (gitignored; rsync'd to VPS separately)
site/ Astro 6 app (git submodule → brutsalvadi/astro-bloomz) site/ Astro 6 app (git submodule → brutsalvadi/astro-bloomz)
edit/ FastAPI edit server (Python, port 8001) edit/ FastAPI edit server (Python, port 8001)
@@ -114,6 +117,31 @@ Debian 12 VPS. nginx serves the built site from `/var/www/bincio/wiki/`.
FastAPI proxied from `localhost:8001` via nginx `location /api/ { proxy_pass }`. FastAPI proxied from `localhost:8001` via nginx `location /api/ { proxy_pass }`.
See `docs/vps.md` for full nginx config and deployment steps. See `docs/vps.md` for full nginx config and deployment steps.
## Edit concurrency — future work
Current state: last-write-wins. No conflict detection.
Planned approach (not yet implemented):
1. **Git commits with attribution** — on every save, `git add <file>` +
`git commit -m "handle: edited page-name" --author="handle <handle@bincio.wiki>"`.
Requires an `asyncio.Lock` in `server.py` to serialize git operations.
2. **Optimistic locking via base-hash check** — when the client loads a page
for editing, the server returns the current git commit hash alongside the
content. On save, the client sends that hash back. If `HEAD` has moved for
that file since then, the save is rejected with a 409 and a "pagina
modificata, ricarica prima di salvare" message. No lock to release, no
keepalive needed — the user can abandon the editor freely.
3. **3-way merge (ideal)** — instead of rejecting on conflict, use
`git merge-file` with base = content at `baseHash`, ours = current HEAD,
theirs = user's new content. Different lines → automatic merge, one commit.
Same lines → 409 conflict error. Builds on step 2; adds it when needed.
No pessimistic locking needed. Optimistic locking with 3-way merge handles
concurrent edits gracefully without timers or "are you still there?" prompts.
## Git conventions ## Git conventions
- No `Co-Authored-By: Claude` trailers in commits - No `Co-Authored-By: Claude` trailers in commits
+10
View File
@@ -133,6 +133,16 @@ async def _git_commit(file_path: Path, handle: str, verb: str) -> None:
stderr=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL,
) )
await commit.wait() await commit.wait()
# post-receive uses `git checkout -f <SHA>` which detaches HEAD, so
# commits would advance the detached HEAD but not refs/heads/main and
# would be lost on the next push. Explicitly keep main up to date.
update_ref = await asyncio.create_subprocess_exec(
"git", "update-ref", "refs/heads/main", "HEAD",
cwd=str(_ROOT), env=env,
stdout=asyncio.subprocess.DEVNULL,
stderr=asyncio.subprocess.DEVNULL,
)
await update_ref.wait()
except Exception as e: except Exception as e:
print(f"[git] commit failed for {rel}: {e}", flush=True) print(f"[git] commit failed for {rel}: {e}", flush=True)
+14 -1
View File
@@ -1,5 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Pull web-edit commits from VPS, then push everything back. # Pull web-edit commits from VPS, then push everything back.
# Retries the container push (up to 3x) if a VPS web-edit landed between
# our pull and push — receive.denyNonFastForwards is enabled on the bare repo
# so those races are rejected rather than silently dropped.
# Usage: bash scripts/sync-vps.sh # Usage: bash scripts/sync-vps.sh
set -e set -e
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
@@ -11,6 +14,16 @@ echo "==> Pushing site submodule to VPS..."
(cd site && git push vps main) (cd site && git push vps main)
echo "==> Pushing container repo to VPS..." echo "==> Pushing container repo to VPS..."
git push vps main for attempt in 1 2 3; do
if git push vps main; then
break
elif [ $attempt -lt 3 ]; then
echo " Push rejected — VPS has new commits, pulling and retrying (attempt $attempt)..."
git pull --rebase --autostash vps main
else
echo " Push failed after 3 attempts — run sync-vps.sh again manually."
exit 1
fi
done
echo "==> Done." echo "==> Done."