docs: record root cause and live verification of Bug #2
Bug #2 (ls-remote vs fetch disagreeing on the VPS) turned out to be two separate things, neither a bug in the fetch fix itself: a stale, unrelated system-wide git-remote-rad binary being silently used in ad-hoc manual testing (PATH had no ~/.radicle/bin), plus a genuine cross-device rename bug in rad-lfs-transfer's download path (fixed separately, ac4a27c) that only surfaced once the real pipeline actually ran with the fetch fix for the first time. Verified live: triggered blog-deploy.service for real, full success end-to-end, SHA-256-verified.
This commit is contained in:
parent
a97484c92f
commit
945da6f7f8
|
|
@ -2,8 +2,18 @@
|
||||||
|
|
||||||
**RESOLVED** (2026-07-14). Fix: `refs/notes/rad-lfs` is now exposed per-peer
|
**RESOLVED** (2026-07-14). Fix: `refs/notes/rad-lfs` is now exposed per-peer
|
||||||
(`refs/notes/rad-lfs/<peer-id>`) instead of expecting one canonical bare ref, matching how
|
(`refs/notes/rad-lfs/<peer-id>`) instead of expecting one canonical bare ref, matching how
|
||||||
patch refs already work — see "Fix actually shipped" at the bottom for the full writeup and
|
patch refs already work — see "Fix actually shipped" for the full writeup and verification.
|
||||||
verification. Kept the original report below unedited for context.
|
|
||||||
|
**Follow-up "Bug #2" also RESOLVED** (2026-07-14, same day) — turned out to be two separate
|
||||||
|
things, neither a bug in the fix above: a stale, unrelated system-wide `git-remote-rad`
|
||||||
|
install being silently used in manual ad-hoc testing (not a real pipeline issue), plus one
|
||||||
|
genuinely new bug (a cross-device `rename()` failure on LFS download) found only once the
|
||||||
|
real pipeline actually ran with the fix. See "Root cause of Bug #2, actually found" at the
|
||||||
|
very bottom for the full story — verified live against the production deploy pipeline on
|
||||||
|
mbator.pl, which completed successfully end-to-end.
|
||||||
|
|
||||||
|
Kept the original report and the mid-investigation "Follow-up" section below unedited, for
|
||||||
|
context on how the debugging actually went.
|
||||||
|
|
||||||
Found while deploying `rad-lfs-ipfs` to mbator.pl for the blog pipeline (2026-07-14).
|
Found while deploying `rad-lfs-ipfs` to mbator.pl for the blog pipeline (2026-07-14).
|
||||||
Blocks the whole LFS-over-IPFS flow end-to-end, not just a cold-start edge case.
|
Blocks the whole LFS-over-IPFS flow end-to-end, not just a cold-start edge case.
|
||||||
|
|
@ -114,6 +124,65 @@ Re-run `git fetch rad` in `/home/blog/repo` on the VPS (as `blog`) — if the fi
|
||||||
the notes ref, that fetch (and the LFS smudge it triggers) is the last remaining piece to
|
the notes ref, that fetch (and the LFS smudge it triggers) is the last remaining piece to
|
||||||
confirm the whole IPFS content-fetch path end-to-end.
|
confirm the whole IPFS content-fetch path end-to-end.
|
||||||
|
|
||||||
|
## Follow-up: fix deployed, uncovered a second, more specific bug (2026-07-14, same day)
|
||||||
|
|
||||||
|
Rebuilt both the local (`~/Blog/Blog`) and VPS (`/home/blog/repo`) checkouts against
|
||||||
|
`ff762ef8` (the fix + the interactive-passphrase commit) and re-ran `rad lfs init` to pick
|
||||||
|
up the migrated wildcard refspec (`+refs/notes/rad-lfs/*:refs/notes/rad-lfs/*`; the old
|
||||||
|
exact-match entry needed manual removal once — `git update-ref -d refs/notes/rad-lfs` — on
|
||||||
|
the author's machine where a stale *local* plain note ref pre-existed and D/F-conflicted
|
||||||
|
with the incoming namespaced one; the `remove_refspec_if_present` migration in `init.rs`
|
||||||
|
correctly stripped the *refspec config*, but doesn't (and structurally can't, from
|
||||||
|
`init.rs`) also clean up a ref that already exists on disk from before).
|
||||||
|
|
||||||
|
**On the author's machine**: `git fetch rad` now genuinely works — pulls
|
||||||
|
`refs/notes/rad-lfs/<peer>` for real, exactly the repro case from the original bug.
|
||||||
|
Confirmed fixed there.
|
||||||
|
|
||||||
|
**On the VPS (`blog` user, second peer/node)**: same `rad lfs init` migration, but the
|
||||||
|
actual fetch behaves inconsistently:
|
||||||
|
|
||||||
|
- `git ls-remote rad` correctly lists the note:
|
||||||
|
`c4538c2d... refs/notes/rad-lfs/z6MkkphWkrBU3Buhh4ZN8Gtt9KLzn2DUsx3hZhhXirK7XAyn`
|
||||||
|
- `git fetch rad` (relying on the configured wildcard refspec) silently skips it — only
|
||||||
|
reports `master` as `[up to date]`, no attempt at the notes ref, no error.
|
||||||
|
- An **explicit** fetch of that exact literal ref name (copy-pasted straight from the
|
||||||
|
`ls-remote` output above) still fails outright:
|
||||||
|
```
|
||||||
|
$ git fetch rad refs/notes/rad-lfs/z6MkkphWkrBU3Buhh4ZN8Gtt9KLzn2DUsx3hZhhXirK7XAyn:refs/notes/rad-lfs/z6MkkphWkrBU3Buhh4ZN8Gtt9KLzn2DUsx3hZhhXirK7XAyn
|
||||||
|
fatal: couldn't find remote ref refs/notes/rad-lfs/z6MkkphWkrBU3Buhh4ZN8Gtt9KLzn2DUsx3hZhhXirK7XAyn
|
||||||
|
```
|
||||||
|
|
||||||
|
So `git-remote-rad`'s ref listing is inconsistent *between its own `list` output and what
|
||||||
|
`fetch` actually resolves against*, for the identical ref, in the identical process
|
||||||
|
invocation style — `ls-remote` and `fetch` are supposed to both go through the remote
|
||||||
|
helper's `list` verb (that's the whole point of `for_fetch()` being one shared function),
|
||||||
|
so seeing them disagree points at something conditional in how `list` is invoked (fetch
|
||||||
|
vs. ls-remote may pass different capabilities/args to the helper, e.g. a `list for-push`
|
||||||
|
vs. plain `list`, or some caching), rather than `for_fetch()`'s ref-selection logic itself
|
||||||
|
(which is presumably fine, given `ls-remote` gets it right).
|
||||||
|
|
||||||
|
Confirmed the underlying data is genuinely present and correct the whole time (verified
|
||||||
|
directly in `blog`'s node storage, bypassing git-remote-rad entirely):
|
||||||
|
```
|
||||||
|
$ git -C /home/blog/.radicle/storage/z2Z16c9C5BaqSQPfbVrGPGG68wAx3 for-each-ref | grep lfs
|
||||||
|
c4538c2d... commit refs/namespaces/z6MkkphW.../refs/notes/rad-lfs
|
||||||
|
```
|
||||||
|
and the note content itself has the right CID and correctly lists `blog`'s own node DID
|
||||||
|
(`z6Mkpqi56U6...`) as an authorized decrypt recipient. So this is purely a
|
||||||
|
`git-remote-rad` transport/listing bug, not a storage/replication/encryption issue.
|
||||||
|
|
||||||
|
**Not yet root-caused** — flagging with the concrete repro above rather than guessing
|
||||||
|
further at the Rust internals blind. Two machines, two outcomes, same binary version, same
|
||||||
|
refspec, same underlying data: something about the *fetch* invocation path differs from
|
||||||
|
the *ls-remote* invocation path in a way that changes what the helper's `list` returns or
|
||||||
|
how the client matches against it. Worth checking: does `list.rs` (or whatever calls
|
||||||
|
`for_fetch()`) get invoked with different arguments for `list` vs. `list for-push` vs. a
|
||||||
|
fetch-triggered `list`, and does the *local* `remote.<rad>.fetch` refspec (not just the
|
||||||
|
server-side listing) factor into which lines the helper actually returns — i.e. is there a
|
||||||
|
second, client-driven filtering step somewhere that behaves differently between the two
|
||||||
|
call sites?
|
||||||
|
|
||||||
## Fix actually shipped
|
## Fix actually shipped
|
||||||
|
|
||||||
Went with the "simplest" option from the list above, since it's also the semantically
|
Went with the "simplest" option from the list above, since it's also the semantically
|
||||||
|
|
@ -189,3 +258,86 @@ problem in practice. Worth a real fix at some point (e.g. `rad lfs init` also en
|
||||||
explicit `+refs/heads/*:refs/remotes/rad/*`-equivalent push default is present, or documenting
|
explicit `+refs/heads/*:refs/remotes/rad/*`-equivalent push default is present, or documenting
|
||||||
"always `git push rad <branch>` explicitly" as a hard requirement), but out of scope for this
|
"always `git push rad <branch>` explicitly" as a hard requirement), but out of scope for this
|
||||||
specific fetch-bug fix.
|
specific fetch-bug fix.
|
||||||
|
|
||||||
|
## Root cause of Bug #2, actually found (2026-07-14, same day, later)
|
||||||
|
|
||||||
|
Got shell access to the VPS (`ssh mbator.pl`, passwordless sudo in a real tty) and reproduced
|
||||||
|
directly rather than guessing further at the Rust internals. It was **two separate things**,
|
||||||
|
not one — neither of them a bug in the fetch fix above.
|
||||||
|
|
||||||
|
### Part 1: not a code bug at all — a stale, unrelated binary
|
||||||
|
|
||||||
|
`sudo -u blog bash -c '...'` (a bare, non-login shell — the same style of ad-hoc command used
|
||||||
|
for all the manual reproduction so far) resolves `PATH` to the plain system default
|
||||||
|
(`/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`), which has **no**
|
||||||
|
`~/.radicle/bin` in it. `which rad` in that context returns `/usr/local/bin/rad` — version
|
||||||
|
`1.8.0`, a completely unrelated, root-owned, March-2026 system-wide install, nothing to do
|
||||||
|
with this fork. Confirmed directly:
|
||||||
|
|
||||||
|
- With `PATH=/home/blog/.radicle/bin:$PATH` explicitly set: `git ls-remote rad` *and*
|
||||||
|
`git fetch rad` both correctly see and fetch `refs/notes/rad-lfs/<peer>` — full
|
||||||
|
`GIT_TRANSPORT_HELPER_DEBUG=1` trace shows `list` correctly advertising it and git correctly
|
||||||
|
issuing `fetch <oid> <ref>` for it, ending in `[new ref] refs/notes/rad-lfs/<peer>`.
|
||||||
|
- With the bare/default `PATH` (resolving to `/usr/local/bin/git-remote-rad`, the unrelated old
|
||||||
|
binary): `git fetch rad` produces **zero output at all** — exactly the silent-skip symptom
|
||||||
|
originally reported, byte for byte.
|
||||||
|
|
||||||
|
So `list`/`for_fetch()` never actually disagreed between `ls-remote` and `fetch` for the same
|
||||||
|
binary — the two commands were, at some point during manual testing, silently invoking two
|
||||||
|
*different* binaries. The real automated pipeline was never actually at risk from this: all
|
||||||
|
three blog-owned systemd units (`radicle-node-blog`, `blog-radicle-watch`, `blog-deploy`)
|
||||||
|
already have `Environment=...PATH=/home/blog/.radicle/bin:...` correctly set. Confirmed via
|
||||||
|
`systemctl cat` on all three. This was purely a manual-testing artifact.
|
||||||
|
|
||||||
|
### Part 2: a real, new bug — but only surfaces once you get past Part 1
|
||||||
|
|
||||||
|
`blog-deploy.service`'s last run before the fork rebuild had failed with the *original*
|
||||||
|
Bug #1 error (timestamped *before* the 16:10–16:14 UTC rebuild/restart) — meaning the real
|
||||||
|
pipeline, with its correct `PATH`, hadn't actually run even once against the fetch fix yet.
|
||||||
|
Triggering it manually (`sudo systemctl start blog-deploy.service`) surfaced a genuinely new
|
||||||
|
failure:
|
||||||
|
|
||||||
|
```
|
||||||
|
Downloading assets/_infra-test/lfs-ipfs-smoketest.jpg (692 B)
|
||||||
|
Error downloading object: ...: failed to copy downloaded file: cannot replace
|
||||||
|
".../ .git/lfs/objects/c5/e3/c5e3b0c1..." with "/tmp/rad-lfs-c5e3b0c1...":
|
||||||
|
rename /tmp/rad-lfs-c5e3b0c1... .../.git/lfs/objects/c5/e3/c5e3b0c1...: invalid cross-device link
|
||||||
|
```
|
||||||
|
|
||||||
|
The download and decryption had already succeeded by this point (692 B, matching exactly) —
|
||||||
|
this was purely the final handoff to git-lfs failing. Root cause:
|
||||||
|
`radicle-lfs-transfer/src/main.rs`'s `download_inner` reported a path under
|
||||||
|
`std::env::temp_dir()` (`/tmp`) back to git-lfs as the "complete" event's path. Git-lfs then
|
||||||
|
does an *atomic* `rename()` from that path into `.git/lfs/objects/...` — it does not fall back
|
||||||
|
to a copy if that fails. On this VPS, `/tmp` and the repository are on different
|
||||||
|
filesystems/mounts, so the rename hits `EXDEV` ("invalid cross-device link") and git-lfs just
|
||||||
|
errors out rather than retrying with a copy.
|
||||||
|
|
||||||
|
Fixed in `radicle-lfs-transfer` (commit `ac4a27c`): resolve `.git/lfs/tmp` (via
|
||||||
|
`git rev-parse --git-dir`, so this works from a subdirectory too, not just the repo root) and
|
||||||
|
download there instead, mirroring git-lfs's own existing convention for its own internal temp
|
||||||
|
downloads — guaranteed same-filesystem by construction, not by hoping `/tmp` happens to line
|
||||||
|
up. This does reintroduce a `git` binary runtime dependency for `rad-lfs-transfer` (previously
|
||||||
|
dropped once it stopped needing to read/write notes directly) — an acceptable tradeoff, since
|
||||||
|
anything using `rad`/git-lfs at all already requires git to be installed anyway. Submodule
|
||||||
|
pointer bumped in this repo at `a97484c9`.
|
||||||
|
|
||||||
|
### Verified live, for real, against production
|
||||||
|
|
||||||
|
Deployed the fix to the VPS (`blog` has no Forgejo SSH credentials, so copied the fixed
|
||||||
|
`main.rs` directly rather than provisioning new access; rebuilt in place with
|
||||||
|
`~/.cargo/bin`/`~/.radicle/bin` on `PATH`), then actually triggered `blog-deploy.service`:
|
||||||
|
|
||||||
|
```
|
||||||
|
● blog-deploy.service ... Active: inactive (dead) ...
|
||||||
|
Process: ExecStart=/home/blog/repo/deploy/deploy.sh (code=exited, status=0/SUCCESS)
|
||||||
|
[blog-deploy] deploying to /srv/mbator.pl...
|
||||||
|
[blog-deploy] deploy complete: 3f650d2d7e9accfba3468a885fc63984e18ceadb
|
||||||
|
```
|
||||||
|
|
||||||
|
Full success, end to end: `git fetch rad` pulled the notes ref for real, `git lfs pull`
|
||||||
|
downloaded and decrypted the private object, the site built, and it deployed. Confirmed the
|
||||||
|
checked-out asset's SHA-256 matches the original exactly
|
||||||
|
(`c5e3b0c1eb2d6e2f3ef98fe74e70fe431986dc2a401b1eae961e175b5a2164ca`). It doesn't appear under
|
||||||
|
`/srv/mbator.pl` because it's deliberately unreferenced by any post — expected, by the smoke
|
||||||
|
test's own design, not a bug.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue