zero-gravity: detect tarball layout — galileo 3.0.8 moved binaries to a shared root bin/ #63

Manually merged
claude merged 1 commits from zerog-tarball-layout-tolerant into main 2026-08-12 16:49:39 +00:00
Collaborator

Fixes a build break I introduced. vibe-node #2912 (galileo 3.0.3 → 3.0.8, merged as bc4cecf) broke the de-32 build — job ad580587:

#16 [zero-gravity-galileo-geth-node 7/8] RUN chmod +x /0g/bin/0gchaind
#16 0.287 chmod: cannot access '/0g/bin/0gchaind': No such file or directory

Root cause

The Dockerfile hardcoded galileo -> mv <root>/rpc /0g. That held for 3.0.3, where every profile dir carried its own copy of the binaries. 3.0.8 deduplicated them into a shared <root>/bin/ — which is why the tarball went 168 MB → 69 MB — leaving rpc/ as configs-only. So the move produced configs and no binaries, and the build died two layers later on a chmod.

Verified by downloading and listing all three:

galileo 3.0.3     <root>/{rpc,validator,archive,seed}/bin/  + per-profile configs
galileo 3.0.8     <root>/bin/  SHARED   +  <root>/{rpc,validator,seed,archive}/ configs
aristotle 1.0.6   <root>/bin/  + configs, no profile dirs at all

does <root>/rpc exist is not a sufficient test — my first attempt at this fix used exactly that and still failed, because 3.0.8 does ship rpc/; it just no longer holds bin/. The test run caught it.

The change

Take configs from the profile dir when present, then top the binaries up from the shared root bin/ when the profile dir didn't supply them:

if [ -d "${SRC}/rpc" ]; then cp -a "${SRC}/rpc/." /0g/;   # configs (+ binaries pre-3.0.8)
else                         cp -a "${SRC}/." /0g/;  fi   # aristotle: everything at root

if [ ! -f /0g/bin/0gchaind ] && [ -d "${SRC}/bin" ]; then
    cp -a "${SRC}/bin" /0g/                               # >= 3.0.8: shared root bin/
fi

Then assert both binaries immediately after extraction, naming the chain, the version and the known layouts — so the next upstream reshuffle reports itself at the point of failure instead of as an anonymous chmod error two layers downstream.

Verification — real artifacts, not synthetic

All three now yield an identical /0g, so 3.0.8 reaches parity with 3.0.3:

galileo 3.0.3   profile dir found -> .../rpc
                OK | 0gchaind=76M geth=46M | 0g-home bin geth-config.toml
                     geth-genesis.json kzg-trusted-setup.json rollback_cl.sh
galileo 3.0.8   profile dir found -> .../rpc
                binaries not in the profile dir -> topping up from .../bin
                OK | 0gchaind=76M geth=46M | (identical set)
aristotle 1.0.6 no profile dir -> using tarball root
                OK | 0gchaind=76M geth=46M | (+ archive/validator config variants)

The configs the templates reference — /0g/geth-config.toml and /0g/kzg-trusted-setup.json — are present in every case.

Negative case, a synthetic tarball with binaries in neither location:

0g: FATAL - missing after extraction: bin/0gchaind bin/geth
exit=1

⚠️ Not built as a container image — no docker daemon on the controller. The extraction path is exercised verbatim against the real tarballs; the surrounding layers are unchanged. The real build happens on de-32 after deploy, and job ad580587 can simply be re-run.

What I got wrong

I verified the aristotle 1.0.6 tarball layout end-to-end before merging #3356 — downloaded it, confirmed bin/0gchaind and bin/geth — and said so explicitly. I did not apply the same check to galileo 3.0.8 when merging #2912, and that check would have caught this before it reached a host.

#2912's own verification (a clean ./update.sh) only ever proved the compose renders, never that the image builds. Those are different claims, and I let the first stand in for the second.

Impact is contained: 0g has zero dRPC entries and no rpcs: host assignment, so this is the de-32 bench only — no serving node, no revenue. aristotle 1.0.6 built and exported cleanly in the same job, which independently validates #61 and #3356 on a real build.

Related: #61 (merged) made the download fail loudly; this makes the extraction fail loudly. Same lesson, adjacent step.

**Fixes a build break I introduced.** vibe-node #2912 (galileo `3.0.3 → 3.0.8`, merged as `bc4cecf`) broke the de-32 build — job **`ad580587`**: ``` #16 [zero-gravity-galileo-geth-node 7/8] RUN chmod +x /0g/bin/0gchaind #16 0.287 chmod: cannot access '/0g/bin/0gchaind': No such file or directory ``` ## Root cause The Dockerfile hardcoded `galileo -> mv <root>/rpc /0g`. That held for 3.0.3, where **every profile dir carried its own copy of the binaries**. 3.0.8 **deduplicated them into a shared `<root>/bin/`** — which is why the tarball went **168 MB → 69 MB** — leaving `rpc/` as configs-only. So the move produced configs and no binaries, and the build died two layers later on a `chmod`. Verified by downloading and listing all three: ``` galileo 3.0.3 <root>/{rpc,validator,archive,seed}/bin/ + per-profile configs galileo 3.0.8 <root>/bin/ SHARED + <root>/{rpc,validator,seed,archive}/ configs aristotle 1.0.6 <root>/bin/ + configs, no profile dirs at all ``` **`does <root>/rpc exist` is not a sufficient test** — my first attempt at this fix used exactly that and still failed, because 3.0.8 *does* ship `rpc/`; it just no longer holds `bin/`. The test run caught it. ## The change Take configs from the profile dir when present, then **top the binaries up** from the shared root `bin/` when the profile dir didn't supply them: ```sh if [ -d "${SRC}/rpc" ]; then cp -a "${SRC}/rpc/." /0g/; # configs (+ binaries pre-3.0.8) else cp -a "${SRC}/." /0g/; fi # aristotle: everything at root if [ ! -f /0g/bin/0gchaind ] && [ -d "${SRC}/bin" ]; then cp -a "${SRC}/bin" /0g/ # >= 3.0.8: shared root bin/ fi ``` Then **assert both binaries immediately after extraction**, naming the chain, the version and the known layouts — so the next upstream reshuffle reports itself at the point of failure instead of as an anonymous `chmod` error two layers downstream. ## Verification — real artifacts, not synthetic All three now yield an **identical** `/0g`, so 3.0.8 reaches parity with 3.0.3: ``` galileo 3.0.3 profile dir found -> .../rpc OK | 0gchaind=76M geth=46M | 0g-home bin geth-config.toml geth-genesis.json kzg-trusted-setup.json rollback_cl.sh galileo 3.0.8 profile dir found -> .../rpc binaries not in the profile dir -> topping up from .../bin OK | 0gchaind=76M geth=46M | (identical set) aristotle 1.0.6 no profile dir -> using tarball root OK | 0gchaind=76M geth=46M | (+ archive/validator config variants) ``` The configs the templates reference — `/0g/geth-config.toml` and `/0g/kzg-trusted-setup.json` — are present in every case. Negative case, a synthetic tarball with binaries in neither location: ``` 0g: FATAL - missing after extraction: bin/0gchaind bin/geth exit=1 ``` ⚠️ **Not built as a container image** — no docker daemon on the controller. The extraction path is exercised verbatim against the real tarballs; the surrounding layers are unchanged. The real build happens on de-32 after deploy, and job `ad580587` can simply be re-run. ## What I got wrong I verified the aristotle 1.0.6 tarball layout end-to-end before merging #3356 — downloaded it, confirmed `bin/0gchaind` and `bin/geth` — and said so explicitly. I did **not** apply the same check to galileo 3.0.8 when merging #2912, and that check would have caught this before it reached a host. #2912's own verification (a clean `./update.sh`) only ever proved the compose *renders*, never that the image *builds*. Those are different claims, and I let the first stand in for the second. Impact is contained: 0g has zero dRPC entries and no `rpcs:` host assignment, so this is the de-32 bench only — no serving node, no revenue. **aristotle 1.0.6 built and exported cleanly** in the same job, which independently validates #61 and #3356 on a real build. Related: **#61** (merged) made the *download* fail loudly; this makes the *extraction* fail loudly. Same lesson, adjacent step.
claude added 1 commit 2026-08-12 11:10:21 +00:00
Fixes the de-32 build break introduced by vibe-node #2912 (galileo 3.0.3 -> 3.0.8,
merged as bc4cecf). Job ad580587:

    #16 chmod: cannot access '/0g/bin/0gchaind': No such file or directory

The Dockerfile hardcoded 'galileo -> mv <root>/rpc /0g'. That held for 3.0.3, where
every profile dir carried its own copy of the binaries. 3.0.8 DEDUPLICATED them into
a shared <root>/bin/ (168 MB -> 69 MB), leaving rpc/ as configs-only — so the move
produced configs and no binaries, and the build died two layers later on a chmod.

Note 'does <root>/rpc exist' is not a sufficient test: 3.0.8 still ships rpc/, it just
no longer holds bin/. Verified layouts:

  galileo 3.0.3     <root>/{rpc,validator,archive,seed}/bin/ + configs
  galileo 3.0.8     <root>/bin/ shared + <root>/{rpc,...}/ configs
  aristotle 1.0.6   <root>/bin/ + configs, no profile dirs

So: take configs from the profile dir when present, then top the binaries up from the
shared root bin/ if the profile dir did not supply them.

Also asserts both binaries exist immediately after extraction, naming the chain, the
version and the known layouts — so the next upstream reshuffle reports itself at the
point of failure rather than as an anonymous chmod error.

Verified against the real artifacts — all three now yield an identical /0g
(bin/0gchaind 76M, bin/geth 46M, geth-config.toml, geth-genesis.json,
kzg-trusted-setup.json, 0g-home/, rollback_cl.sh), and a synthetic unknown layout
exits 1 with the diagnostic instead of reaching chmod.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude manually merged commit 6df9f0f17a into main 2026-08-12 16:49:39 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: StakeSquid/ethereum-rpc-docker#63