From d729a7ae2fb4e5c2d7fe26e43763609460b4148d Mon Sep 17 00:00:00 2001 From: Miles Ward Date: Tue, 26 May 2026 00:53:30 -0400 Subject: [PATCH] v0.4.62: ship the v0.4.61 cache fix as a buildable image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.4.61 source landed in main with the cache fix and the PXE-logo compositor, plus an aspirational Dockerfile stage that rebuilds iPXE from source with IMAGE_PNG enabled. The Dockerfile stage hits intermittent `cc1: internal compiler error: Segmentation fault` when cross-emulating x86_64 gcc under QEMU on arm64 build hosts, which is what the build host I was using does. No v0.4.61 image was ever published as a result. v0.4.62 walks back the iPXE-from-source change and ships a working image with the same cache fix and the same compositor code in place. The iPXE rebuild is queued for a follow-up release, to be built and validated on the actual x86_64 Unraid hardware where the QEMU instability doesn't apply. What's in v0.4.62 vs v0.4.6: - Asset URL versioning: index.html now appends `?v=` to every asset URL (app.css, app.js, logo.svg). Combined with `Cache-Control: no-cache, must-revalidate` on the asset handlers, upgrades land in operators' browsers without a hard refresh. This is the fix for "I pulled v0.4.6 but the UI still looks like v0.4.5". - New PXE-logo compositor in iso-store::pxe_logo: decodes any raster the operator uploads, scales-to-fit into a 600×200 bounding box, pastes it centered at the top of a 1024×768 PNG canvas, and serves the result at GET /branding/pxe-logo. Wired into render_menu's `console --picture` directive; takes effect when the shipped iPXE binaries grow PNG support. - ASCII OpenPXE wordmark in render_menu retained for v0.4.62 — works on the boot.ipxe.org pre-builds we currently ship. Quality: - 142 tests passing. - cargo clippy --workspace --all-targets clean. - No image dependency change since v0.4.61 (the `image = "0.25"` dep added in v0.4.61 stays — it backs the compositor). Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 16 +++--- Cargo.toml | 2 +- crates/http-api/src/ipxe_script.rs | 44 ++++++++------- deploy/docker/Dockerfile | 86 +++++------------------------- 4 files changed, 46 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8ce326..2574e6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,7 +1140,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openpxe" -version = "0.4.61" +version = "0.4.62" dependencies = [ "anyhow", "axum", @@ -1162,7 +1162,7 @@ dependencies = [ [[package]] name = "openpxe-core" -version = "0.4.61" +version = "0.4.62" dependencies = [ "anyhow", "bcrypt", @@ -1181,7 +1181,7 @@ dependencies = [ [[package]] name = "openpxe-dhcp-proxy" -version = "0.4.61" +version = "0.4.62" dependencies = [ "anyhow", "bytes", @@ -1195,7 +1195,7 @@ dependencies = [ [[package]] name = "openpxe-http-api" -version = "0.4.61" +version = "0.4.62" dependencies = [ "anyhow", "axum", @@ -1226,7 +1226,7 @@ dependencies = [ [[package]] name = "openpxe-ipxe-assets" -version = "0.4.61" +version = "0.4.62" dependencies = [ "openpxe-core", "rust-embed", @@ -1236,7 +1236,7 @@ dependencies = [ [[package]] name = "openpxe-iso-store" -version = "0.4.61" +version = "0.4.62" dependencies = [ "anyhow", "bcrypt", @@ -1260,7 +1260,7 @@ dependencies = [ [[package]] name = "openpxe-tftp" -version = "0.4.61" +version = "0.4.62" dependencies = [ "anyhow", "bytes", @@ -1274,7 +1274,7 @@ dependencies = [ [[package]] name = "openpxe-webui" -version = "0.4.61" +version = "0.4.62" [[package]] name = "parking_lot" diff --git a/Cargo.toml b/Cargo.toml index d72c72c..ccdf2b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ ] [workspace.package] -version = "0.4.61" +version = "0.4.62" edition = "2021" rust-version = "1.95" license = "MIT OR Apache-2.0" diff --git a/crates/http-api/src/ipxe_script.rs b/crates/http-api/src/ipxe_script.rs index ab16992..7795d2b 100644 --- a/crates/http-api/src/ipxe_script.rs +++ b/crates/http-api/src/ipxe_script.rs @@ -77,15 +77,20 @@ pub fn render_menu(isos: &[IsoMeta], settings: &Settings, base_url: &str) -> Str ); let _ = writeln!(s, ":menu"); let _ = writeln!(s, "menu OpenPXE - network boot menu"); - // v0.4.61: we used to draw an ASCII OpenPXE wordmark here. Now - // that the bundled iPXE is built with `IMAGE_PNG`, the - // `console --picture` line at the top of this script paints the - // operator's actual logo (composed server-side into a 1024×768 - // canvas with the logo centered at the top) — the ASCII banner - // became visual noise *on top* of the real image. Old iPXE - // builds without PNG fall through the `|| console` clause and - // simply show the menu without a logo, which is the correct - // graceful-degradation outcome. + // ASCII OpenPXE wordmark. Works on every iPXE build, including + // the boot.ipxe.org pre-builds we ship (which omit `IMAGE_PNG`, + // so `console --picture` paints nothing). When the queued iPXE + // source-build lands and the operator's uploaded raster actually + // paints via `console --picture`, this banner can be retired in + // favour of the real image. The compositor at + // /branding/pxe-logo is already wired and waiting. + let _ = writeln!(s, "item --gap"); + let _ = writeln!(s, "item --gap -- ___ ___ __ __ ___"); + let _ = writeln!(s, "item --gap -- / _ \\ _ __ ___ _ _ | _ \\ \\/ / | __|"); + let _ = writeln!(s, "item --gap -- | (_) | '_ \\/ -_) ' \\ | _/ \\ / | _|"); + let _ = writeln!(s, "item --gap -- \\___/| .__/\\___|_||_| |_| /_/\\_\\ |___|"); + let _ = writeln!(s, "item --gap -- |_|"); + let _ = writeln!(s, "item --gap"); let _ = writeln!( s, "item --gap -- ------------------------- Default -------------------------" @@ -626,12 +631,13 @@ mod password_tests { #[test] fn top_menu_has_polished_branding_and_arch_footer() { - // v0.4.6 polish + v0.4.61 image upgrade: the menu emits a - // `console --picture` line that the bundled iPXE (built with - // `IMAGE_PNG`) honours, plus an arch-resolved footer carrying - // the current OpenPXE version. The ASCII wordmark that used - // to live here was dropped in v0.4.61 — it duplicated the now- - // working image. + // v0.4.6 polish + v0.4.62 stability fixes: the menu emits a + // `console --picture` line that PNG-capable iPXE builds will + // honour (queued for a follow-up release once we can rebuild + // iPXE from source on native x86_64 hardware), an ASCII + // OpenPXE wordmark that works on every iPXE build (including + // the boot.ipxe.org pre-builds we currently ship), and a + // single-line footer carrying the OpenPXE version + arch. let settings = Settings::default(); let s = render_menu(&[], &settings, "http://10.0.0.5"); assert!( @@ -641,11 +647,11 @@ mod password_tests { // Picture-or-text-console must be a single statement so older // iPXE parsers don't choke on the chain. assert!(s.contains("|| console"), "missing graceful fallback:\n{s}"); - // No ASCII wordmark — once the real PNG paints, the ASCII - // banner would duplicate the operator's logo visually. + // ASCII wordmark — paints on every iPXE build regardless of + // PNG support. assert!( - !s.contains("___ ___ __ __ ___"), - "ASCII banner shouldn't be emitted in v0.4.61+:\n{s}" + s.contains("___ ___ __ __ ___"), + "ASCII banner missing first row:\n{s}" ); // Footer with version + arch interpolation. The version comes // from CARGO_PKG_VERSION at compile time. diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile index 54fb223..32344ff 100644 --- a/deploy/docker/Dockerfile +++ b/deploy/docker/Dockerfile @@ -16,79 +16,21 @@ ARG RUST_VERSION=1.95 -########## fetch wimboot (and a sanity-check fetch of upstream iPXE) ########## -# v0.4.61: we no longer ship the boot.ipxe.org iPXE binaries directly; -# instead we build iPXE from source with IMAGE_PNG enabled (see the -# ipxe-build stage below). The fetch stage still pulls wimboot (a -# pre-signed binary from ipxe/wimboot's GitHub release) since that's -# unrelated to the PNG concern. +########## fetch iPXE binaries + wimboot ########## +# v0.4.62: kept on the boot.ipxe.org pre-builds for the moment. We +# want PNG support (so `console --picture` paints the operator's logo +# on the PXE menu) but the obvious path — adding a new `ipxe-build` +# stage that compiles iPXE from source with `IMAGE_PNG` enabled — +# runs into a QEMU/gcc instability when cross-emulating x86_64 on +# arm64 build hosts (intermittent `cc1` segfaults). The compositor +# at /branding/pxe-logo is already wired so when the iPXE rebuild +# lands (on native x86_64 hardware), no other code change is needed. FROM debian:12-slim AS fetch RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \ && rm -rf /var/lib/apt/lists/* WORKDIR /src -RUN mkdir -p assets/ipxe && \ - curl --fail --silent --show-error --location \ - -o assets/ipxe/wimboot \ - https://github.com/ipxe/wimboot/releases/latest/download/wimboot \ - || echo "wimboot fetch failed; Windows toggle will stay disabled" - -########## build iPXE from source with IMAGE_PNG enabled ########## -# This stage replaces the old "grab pre-built binaries from -# boot.ipxe.org" path. The shipped binaries there are built with the -# default config which omits `IMAGE_PNG`, so the `console --picture` -# call in render_menu silently no-ops — operator logos never paint. -# Building from source lets us flip the one flag we need. -# -# Cross-compilation: x86_64 + i386 use the native toolchain that ships -# in the rust:bookworm base; arm64 uses gcc-aarch64-linux-gnu. The four -# output binaries match the names openpxe-ipxe-assets expects in -# assets/ipxe/. -FROM rust:${RUST_VERSION}-bookworm AS ipxe-build -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - git build-essential liblzma-dev mtools genisoimage syslinux \ - gcc-aarch64-linux-gnu \ - && rm -rf /var/lib/apt/lists/* -WORKDIR /build -# Pin to a recent iPXE master tip via shallow clone. iPXE doesn't tag -# releases; pinning the SHA in source would be a periodic chore. The -# tradeoff is that "rebuild the container" silently picks up upstream -# patches — for a boot loader this is the right side of the -# pin-vs-fresh tradeoff (we want CVE fixes ASAP and the PXE chain is -# the trusted base). -RUN git clone --depth=1 https://github.com/ipxe/ipxe.git ipxe -WORKDIR /build/ipxe/src -# Feature flags landed via the `config/local/` override files iPXE's -# config system reads after `config/general.h`. We enable just the -# image format + framebuffer console plumbing — everything else stays -# at the upstream default. `keep-debug` is off; `parserrors` is off; we -# pin a small set of useful tweaks. -RUN mkdir -p config/local \ - && printf '%s\n' \ - '#define IMAGE_PNG' \ - '#define CONSOLE_FRAMEBUFFER' \ - '#define CONSOLE_VESAFB' \ - '#define DOWNLOAD_PROTO_HTTPS' \ - '#define NSLOOKUP_CMD' \ - '#define NTP_CMD' \ - > config/local/general.h -# Each arch builds to its own `bin-*` directory. We copy the four -# output binaries into /out/ with the names openpxe-ipxe-assets -# expects. Stripping the binaries saves ~30% — they go into the rust -# binary via include_bytes! so the savings ripple through the final -# image. -RUN mkdir -p /out && \ - make -j"$(nproc)" bin/undionly.kpxe && \ - cp bin/undionly.kpxe /out/undionly.kpxe && \ - make -j"$(nproc)" bin-x86_64-efi/snponly.efi && \ - cp bin-x86_64-efi/snponly.efi /out/snponly.efi && \ - make -j"$(nproc)" bin-x86_64-efi/ipxe.efi && \ - cp bin-x86_64-efi/ipxe.efi /out/ipxe.efi && \ - make -j"$(nproc)" bin-i386-efi/snponly.efi && \ - cp bin-i386-efi/snponly.efi /out/snponly-i386.efi && \ - make -j"$(nproc)" CROSS_COMPILE=aarch64-linux-gnu- bin-arm64-efi/snponly.efi && \ - cp bin-arm64-efi/snponly.efi /out/snponly-arm64.efi && \ - ls -lh /out/ +COPY scripts/fetch-ipxe.sh scripts/fetch-ipxe.sh +RUN mkdir -p assets/ipxe && bash scripts/fetch-ipxe.sh ########## build openpxe ########## FROM rust:${RUST_VERSION}-bookworm AS build @@ -122,11 +64,7 @@ RUN apt-get update \ # `cargo build`, which is slow and can exhaust small Colima/CI disks. COPY Cargo.toml Cargo.lock ./ COPY crates/ crates/ -# v0.4.61: iPXE binaries come from our own source-built stage with -# IMAGE_PNG enabled. wimboot still comes from the fetch stage (it's -# from ipxe/wimboot's GitHub release, separately signed). -COPY --from=ipxe-build /out/ /src/assets/ipxe/ -COPY --from=fetch /src/assets/ipxe/wimboot /src/assets/ipxe/wimboot +COPY --from=fetch /src/assets/ipxe /src/assets/ipxe # Cache cargo registry + target across builds. The mtime touch is # belt-and-suspenders: cargo occasionally misses mtime-only changes on