From 9002d849855a838ae50565c007b250539b54f82e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 04:09:24 +0000 Subject: [PATCH] docker: the base-image tag is not the toolchain pin Railway build failed: failed to solve: docker.io/library/rust:1.98.1-slim-bookworm: not found Both Dockerfiles used one `ARG RUST_VERSION=1.98.1` as BOTH the toolchain version and the base-image tag. Measured against the registry: that tag is a 404, while `rust:1.98-slim-bookworm` and `rust:1.98.0-slim-bookworm` are 200, and `static.rust-lang.org/dist/channel-rust-1.98.1.toml` is 200. So 1.98.1 is a real Rust release and the CHANNEL was always valid -- what does not exist is a docker-library IMAGE for that patch. Conflating the two made a valid pin depend on whether someone tagged an image for it. The base now floats on the MINOR tag (`ARG RUST_IMAGE_TAG=1.98-slim-bookworm`), which docker-library always publishes. The exact toolchain comes from rust-toolchain.toml, which is now COPY'd into the builder ahead of the sources with a `rustup show` to materialise it. That closes a separate gap found while fixing this one: the pin file was NEVER copied into either image, so a Railway build's toolchain was whatever the base tag shipped and the pin had no effect there at all -- the exact cross-file drift that file was added to end, still open on the one path nobody checked. Copying it first also means a pin bump invalidates the toolchain layer and nothing below it. Consequence: bumping the toolchain is now a one-line edit in rust-toolchain.toml. `RUST_IMAGE_TAG` moves only when the MINOR does, and never to a patch-level tag -- that is the shape that broke. Diagnostic note recorded in both files: BuildKit resolves every stage's base in parallel, so the last `load metadata` line printed is often the healthy one (`debian:bookworm-slim`, which resolves fine). Read `failed to solve:`. NOT BUILT HERE -- no Docker daemon in this environment (client only). The registry probes above are real; the image build is not. Railway is the gate. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011DN5odWu4eisdevH9bPWyz --- Dockerfile.railway | 51 ++++++++++++++++++++++++++++++++++++++-- Dockerfile.solid.railway | 51 ++++++++++++++++++++++++++++++++++++++-- rust-toolchain.toml | 14 +++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) diff --git a/Dockerfile.railway b/Dockerfile.railway index f0b0a27..a305fd8 100644 --- a/Dockerfile.railway +++ b/Dockerfile.railway @@ -55,11 +55,52 @@ # to move in lockstep with this pin. # --------------------------------------------------------------------------- -ARG RUST_VERSION=1.98.1 +# BASE-IMAGE TAG vs TOOLCHAIN PIN — they are two different things, and only +# one of them has to exist on Docker Hub. +# +# This file previously read `ARG RUST_VERSION=1.98.1` and used it BOTH as the +# toolchain version and as the base-image tag. That build fails outright: +# +# failed to solve: docker.io/library/rust:1.98.1-slim-bookworm: not found +# +# Measured against the registry: `rust:1.98.1-slim-bookworm` is a 404, while +# `rust:1.98-slim-bookworm` and `rust:1.98.0-slim-bookworm` are both 200 — and +# `https://static.rust-lang.org/dist/channel-rust-1.98.1.toml` is a 200 too. +# So 1.98.1 IS a real Rust release; what does not exist is a docker-library +# IMAGE for that patch. The official-images repo does not tag every point +# release and lags the ones it does. A toolchain pin can therefore be valid +# while its same-numbered image tag is absent, and a Dockerfile that conflates +# them inherits an availability constraint it never meant to take on. +# +# (The failure surfaces confusingly: BuildKit resolves every stage's base in +# parallel, so the last line printed is often the HEALTHY one — +# `load metadata for docker.io/library/debian:bookworm-slim`, which resolves +# fine. Read the `failed to solve:` line, not the last `load metadata` line.) +# +# The fix separates the two concerns: +# * the base image floats on the MINOR tag, which docker-library always +# publishes; +# * the exact toolchain comes from `rust-toolchain.toml`, which rustup +# honours automatically and which is already this repo's single source of +# truth for the pin. +# +# That also closes a real gap this file had independently of the 404: +# `rust-toolchain.toml` was NEVER COPIED into the builder, so the image's +# toolchain was whatever the base tag shipped and the pin file had no effect +# here at all — precisely the cross-file drift that file was added to end. +# Copying it before the sources also means a pin bump invalidates the +# toolchain layer and nothing below it. +# +# Consequence worth stating: bumping the pin is now a ONE-line edit in +# `rust-toolchain.toml`. Touch `RUST_IMAGE_TAG` only when the MINOR moves +# (1.98 -> 1.99), and never to a patch-level tag — that is the shape that +# broke. + +ARG RUST_IMAGE_TAG=1.98-slim-bookworm ARG BIN_NAME=a2ui-paint-web # ── builder ──────────────────────────────────────────────────────────────── -FROM rust:${RUST_VERSION}-slim-bookworm AS builder +FROM rust:${RUST_IMAGE_TAG} AS builder ARG BIN_NAME # git: the OGAR / lance-graph dependencies are fetched as git deps by cargo. @@ -75,6 +116,12 @@ WORKDIR /src # Copy the manifest first so the dependency build layer caches independently of # source edits. The lock is deliberately NOT copied — see the note above the # build step. +# The pin file FIRST, on its own layer: rustup reads it and materialises the +# exact channel + components, so the toolchain in this image is the one the +# repo declares rather than whatever the base tag happens to ship. +COPY rust-toolchain.toml ./ +RUN rustup show + COPY Cargo.toml ./ COPY crates ./crates diff --git a/Dockerfile.solid.railway b/Dockerfile.solid.railway index 753b7de..23e7dcb 100644 --- a/Dockerfile.solid.railway +++ b/Dockerfile.solid.railway @@ -36,11 +36,52 @@ # workspace. Do not cite this line as evidence that something requires 1.98.1. # --------------------------------------------------------------------------- -ARG RUST_VERSION=1.98.1 +# BASE-IMAGE TAG vs TOOLCHAIN PIN — they are two different things, and only +# one of them has to exist on Docker Hub. +# +# This file previously read `ARG RUST_VERSION=1.98.1` and used it BOTH as the +# toolchain version and as the base-image tag. That build fails outright: +# +# failed to solve: docker.io/library/rust:1.98.1-slim-bookworm: not found +# +# Measured against the registry: `rust:1.98.1-slim-bookworm` is a 404, while +# `rust:1.98-slim-bookworm` and `rust:1.98.0-slim-bookworm` are both 200 — and +# `https://static.rust-lang.org/dist/channel-rust-1.98.1.toml` is a 200 too. +# So 1.98.1 IS a real Rust release; what does not exist is a docker-library +# IMAGE for that patch. The official-images repo does not tag every point +# release and lags the ones it does. A toolchain pin can therefore be valid +# while its same-numbered image tag is absent, and a Dockerfile that conflates +# them inherits an availability constraint it never meant to take on. +# +# (The failure surfaces confusingly: BuildKit resolves every stage's base in +# parallel, so the last line printed is often the HEALTHY one — +# `load metadata for docker.io/library/debian:bookworm-slim`, which resolves +# fine. Read the `failed to solve:` line, not the last `load metadata` line.) +# +# The fix separates the two concerns: +# * the base image floats on the MINOR tag, which docker-library always +# publishes; +# * the exact toolchain comes from `rust-toolchain.toml`, which rustup +# honours automatically and which is already this repo's single source of +# truth for the pin. +# +# That also closes a real gap this file had independently of the 404: +# `rust-toolchain.toml` was NEVER COPIED into the builder, so the image's +# toolchain was whatever the base tag shipped and the pin file had no effect +# here at all — precisely the cross-file drift that file was added to end. +# Copying it before the sources also means a pin bump invalidates the +# toolchain layer and nothing below it. +# +# Consequence worth stating: bumping the pin is now a ONE-line edit in +# `rust-toolchain.toml`. Touch `RUST_IMAGE_TAG` only when the MINOR moves +# (1.98 -> 1.99), and never to a patch-level tag — that is the shape that +# broke. + +ARG RUST_IMAGE_TAG=1.98-slim-bookworm ARG BIN_NAME=a2ui-solid-web # ── builder ──────────────────────────────────────────────────────────────── -FROM rust:${RUST_VERSION}-slim-bookworm AS builder +FROM rust:${RUST_IMAGE_TAG} AS builder ARG BIN_NAME # git for the OGAR / lance-graph git dependencies pulled by sibling crates in @@ -51,6 +92,12 @@ RUN apt-get update \ WORKDIR /src +# The pin file FIRST, on its own layer: rustup reads it and materialises the +# exact channel + components, so the toolchain in this image is the one the +# repo declares rather than whatever the base tag happens to ship. +COPY rust-toolchain.toml ./ +RUN rustup show + COPY Cargo.toml ./ COPY crates ./crates diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a1a2759..98d71fb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -41,6 +41,20 @@ channel = "1.98.1" # and `cargo fmt --check` were both clean before and # after the fix. # +# 1.98.1 (image fix, no channel change) The two Dockerfiles used this same +# version string as their base-image TAG +# (`rust:1.98.1-slim-bookworm`), and that image does not +# exist: docker-library does not tag every point release. +# Measured — that tag is a 404 while `1.98` / `1.98.0` are +# 200, and `channel-rust-1.98.1.toml` is a 200, so the +# CHANNEL above was always valid and only the IMAGE was +# absent. Both files now float their base on the MINOR tag +# and COPY this file into the builder, so rustup +# materialises the channel there instead of the tag +# deciding it. That also closes a gap this file could not +# previously reach: it was never copied into either image, +# so the pin had no effect on a Railway build at all. +# # Never auto-track `stable` — bump explicitly when a future version is # reviewed and the workspace's fmt/clippy/test gates pass clean. #