From 84638314a4a264282dae67670a7877c663cea465 Mon Sep 17 00:00:00 2001 From: ruv Date: Thu, 14 May 2026 13:38:58 -0400 Subject: [PATCH] fix(docker): bump rust 1.85 -> 1.90 + enforce LF on shell scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two real bugs found while pushing the v0.8.0 image to Docker Hub: ## Rust 1.85 -> 1.90 `hnsw_rs 0.3.4` (transitive via wifi-densepose-ruvector -> ruvector-attn-mincut -> hnsw_rs) calls `nbp.is_multiple_of(500_000)`. `is_multiple_of` on unsigned integers was stabilised in Rust 1.87 (rust-lang/rust#128101 — RFC 3565). On 1.85 the compile fails with: error[E0658]: use of unstable library feature `unsigned_is_multiple_of` --> hnsw_rs-0.3.4/src/hnswio.rs:736:20 Pinned to 1.90 for reproducibility — a comment in the Dockerfile flags the 1.87 MSRV requirement so a future downgrade can't quietly break it. ## .gitattributes — force LF on shell scripts + Dockerfile Without a `.gitattributes`, git's default `core.autocrlf=true` on Windows converts shell scripts to CRLF on checkout. `COPY`ing `docker/docker-entrypoint.sh` into a Linux image then preserves CRLF. The shebang line `#!/bin/sh\r\n` causes `exec /app/docker-entrypoint.sh` to fail with: exec /app/docker-entrypoint.sh: no such file or directory The kernel tries to look up an interpreter literally named `/bin/sh\r`, which doesn't exist. Container exits immediately. The first v0.8.0 image push (digest sha256:7957…44fa) suffered exactly this; the re-pushed image (digest sha256:e9f4…d38315) was built on a renormalised tree. The .gitattributes rule forces LF for: - *.sh / *.bash - Dockerfile* - docker/* (covers docker-entrypoint.sh + docker-compose.yml) - scripts/* - `verify` (the proof-replay wrapper — same root cause as if it had landed CRLF in someone's clone) Binary file globs (*.bin, *.wasm, *.rvf, *.pcap, etc.) explicitly marked binary so text-normalisation never touches them. ## CHANGELOG — drop the false `--introspection` flag claim The CHANGELOG entry for v0.8.0 said the introspection endpoints were "off by default, enabled via `--introspection`". That isn't true: `sensing-server --help` has no such flag. The routes are mounted unconditionally in `main.rs`. The per-frame `update()` p99 of 0.041 ms (~24× under D4's 1 ms budget) makes always-on viable; the "off by default" framing came from an earlier draft of ADR-099 that the implementation outgrew. Corrected. ## Verification End-to-end smoke test of the pushed image: docker run -d -p 13000:3000 -e CSI_SOURCE=simulated -e SENSING_BIND_ADDR=0.0.0.0 ruvnet/wifi-densepose:v0.8.0 /health -> {"status":"ok","source":"simulated",...} /api/v1/info -> {"backend":"rust","features":{"ruvector":true,"signal_processing":true,...}} /api/v1/introspection/snapshot -> {"regime":"unknown", "regime_changed":false,"top_k_similarity":[]} (ADR-099 shape exact) /ui/observatory.html -> HTTP 200, 15 KB Published manifest digests: ruvnet/wifi-densepose:v0.8.0 -> sha256:e9f4c5af…d38315 ruvnet/wifi-densepose:latest -> sha256:e9f4c5af…d38315 Co-Authored-By: claude-flow --- .gitattributes | 35 +++++++++++++++++++++++++++++++++++ CHANGELOG.md | 3 ++- docker/Dockerfile.rust | 6 +++++- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..ecfa67fc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,35 @@ +# Line-ending policy. +# +# `* text=auto` lets git normalise text files to LF in the repository and convert +# to the platform's native line endings on checkout. That default is fine for +# .md / .rs / .toml / .py — broken for shell scripts and Dockerfiles, where +# CRLF on the shebang line causes Linux exec to look for an interpreter named +# `/bin/sh\r` (or similar) and fail with "no such file or directory". +# +# Force LF for anything that ends up executed inside a Linux container or a +# POSIX shell. This is what prevented the v0.8.0 image from booting at first +# build until the entrypoint was renormalised. +* text=auto +*.sh text eol=lf +*.bash text eol=lf +verify text eol=lf +Dockerfile* text eol=lf +docker/* text eol=lf +scripts/* text eol=lf + +# Binary blobs that should never be touched by text-normalisation. +*.bin binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.zip binary +*.tar binary +*.tgz binary +*.gz binary +*.wasm binary +*.rvf binary +*.task binary +*.csi.jsonl binary +*.pcap binary diff --git a/CHANGELOG.md b/CHANGELOG.md index 197b6f7c..7fcc3fc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 regime classification) and `temporal-compare` (DTW pattern matching) as a **parallel tap** alongside RuView's existing event pipeline — no replacement, no behaviour change to the existing `/ws/sensing` fan-out or `wifi-densepose-signal` - DSP. Two new endpoints (off by default, enabled via `--introspection`): + DSP. Two new endpoints (always mounted — the tap is cheap enough at 0.041 ms p99 + per-frame `update()` to ship hot by default): - `GET /ws/introspection` — newline-delimited JSON snapshots streamed at the CSI frame rate. Each snapshot carries `frame_count`, `regime` (Idle / Periodic / Transient / Chaotic / Unknown), `lyapunov_exponent`, `attractor_dim`, diff --git a/docker/Dockerfile.rust b/docker/Dockerfile.rust index 018e8dad..cb046265 100644 --- a/docker/Dockerfile.rust +++ b/docker/Dockerfile.rust @@ -3,7 +3,11 @@ # Multi-stage build for minimal final image # Stage 1: Build -FROM rust:1.85-bookworm AS builder +# Rust 1.87+ is required: `hnsw_rs 0.3.4` (transitive via wifi-densepose-ruvector -> +# ruvector-attn-mincut) uses `u*::is_multiple_of`, stabilised in 1.87. Pinning to a +# recent stable (1.90) for reproducibility — bump cautiously since reproducible +# builds rely on this. +FROM rust:1.90-bookworm AS builder WORKDIR /build