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:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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."
|
||||||
|
|||||||
Reference in New Issue
Block a user