max / alloy
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
4 files changed,
+120 insertions,
-29 deletions
| @@ -2643,22 +2643,6 @@ | |||
| 2643 | 2643 | "winnow", | |
| 2644 | 2644 | ] | |
| 2645 | 2645 | ||
| 2646 | - | [[patch.unused]] | |
| 2647 | - | name = "kberg" | |
| 2648 | - | version = "0.1.0" | |
| 2649 | - | ||
| 2650 | - | [[patch.unused]] | |
| 2651 | - | name = "ops-status" | |
| 2652 | - | version = "0.1.0" | |
| 2653 | - | ||
| 2654 | - | [[patch.unused]] | |
| 2655 | - | name = "painhours" | |
| 2656 | - | version = "0.1.0" | |
| 2657 | - | ||
| 2658 | - | [[patch.unused]] | |
| 2659 | - | name = "tagtree" | |
| 2660 | - | version = "0.4.0" | |
| 2661 | - | ||
| 2662 | 2646 | [[patch.unused]] | |
| 2663 | 2647 | name = "quasi-axum" | |
| 2664 | 2648 | version = "0.18.0" | |
| @@ -2691,6 +2675,30 @@ | |||
| 2691 | 2675 | name = "quasi-webview" | |
| 2692 | 2676 | version = "0.18.0" | |
| 2693 | 2677 | ||
| 2678 | + | [[patch.unused]] | |
| 2679 | + | name = "docengine" | |
| 2680 | + | version = "0.7.0" | |
| 2681 | + | ||
| 2682 | + | [[patch.unused]] | |
| 2683 | + | name = "quasi-type" | |
| 2684 | + | version = "0.1.0" | |
| 2685 | + | ||
| 2686 | + | [[patch.unused]] | |
| 2687 | + | name = "kberg" | |
| 2688 | + | version = "0.1.0" | |
| 2689 | + | ||
| 2690 | + | [[patch.unused]] | |
| 2691 | + | name = "ops-status" | |
| 2692 | + | version = "0.1.0" | |
| 2693 | + | ||
| 2694 | + | [[patch.unused]] | |
| 2695 | + | name = "painhours" | |
| 2696 | + | version = "0.1.0" | |
| 2697 | + | ||
| 2698 | + | [[patch.unused]] | |
| 2699 | + | name = "tagtree" | |
| 2700 | + | version = "0.4.0" | |
| 2701 | + | ||
| 2694 | 2702 | [[patch.unused]] | |
| 2695 | 2703 | name = "synckit-client" | |
| 2696 | 2704 | version = "0.8.0" | |
| @@ -2698,7 +2706,3 @@ | |||
| 2698 | 2706 | [[patch.unused]] | |
| 2699 | 2707 | name = "synckit-config" | |
| 2700 | 2708 | version = "0.2.0" | |
| 2701 | - | ||
| 2702 | - | [[patch.unused]] | |
| 2703 | - | name = "docengine" | |
| 2704 | - | version = "0.7.0" |
| @@ -595,7 +595,34 @@ | |||
| 595 | 595 | # separated, and validated against a curated set below rather than | |
| 596 | 596 | # treated as a package list — the builder is not a package manager, and | |
| 597 | 597 | # the gate lives here so it holds even when the TUI is bypassed. | |
| 598 | - | ARG LANGS=rust | |
| 598 | + | # | |
| 599 | + | # THE DEFAULT IS WHAT COMPILES THE SHIPPED STACK, ruled by Max 2026-08-17: | |
| 600 | + | # someone who wants to rebuild a program Alloy ships should not have to | |
| 601 | + | # install a toolchain first. Measured against the client image rather than | |
| 602 | + | # assumed, by reading each shipped binary for a Go build id or a rustc | |
| 603 | + | # path: | |
| 604 | + | # | |
| 605 | + | # Rust hx yazi nu starship zoxide btm dua rg fd bat eza satty, plus | |
| 606 | + | # alloy and shop themselves. 14. | |
| 607 | + | # Go direnv fzf gopass syncthing restic tailscale cliphist. 7. | |
| 608 | + | # C sway mako swaylock, and the hardware-health group. | |
| 609 | + | # | |
| 610 | + | # So `rust,go`. Go is the cheaper half at 95 MiB inbound against rust's | |
| 611 | + | # 176, and it covers the whole continuity tier — syncthing, restic and | |
| 612 | + | # tailscale are the programs most likely to be wanted a version ahead of | |
| 613 | + | # what Fedora carries. | |
| 614 | + | # | |
| 615 | + | # The C tier is deliberately NOT covered and the rule stops short of it. | |
| 616 | + | # `gcc` arrives with rust, so a single-file C program builds, but sway and | |
| 617 | + | # mako want meson, ninja, wayland-protocols and a spread of -devel | |
| 618 | + | # packages that no language toggle models. Naming that here rather than | |
| 619 | + | # pretending the rule is uniform: the three C programs in the stack are | |
| 620 | + | # the ones a user rebuilds by layering a buildroot, not by flipping a | |
| 621 | + | # toggle in the builder. | |
| 622 | + | # | |
| 623 | + | # python and zig stay off for the same reason go is now on: nothing | |
| 624 | + | # shipped is written in either. | |
| 625 | + | ARG LANGS=rust,go | |
| 599 | 626 | ||
| 600 | 627 | # TRIM is the one builder choice about the *base* rather than about Alloy. | |
| 601 | 628 | # fedora-bootc is a general-purpose server base, and three of the things it |
| @@ -410,11 +410,19 @@ | |||
| 410 | 410 | Self { | |
| 411 | 411 | profile: Profile::default(), | |
| 412 | 412 | browser: Browser::default(), | |
| 413 | - | // Rust on by default: the build-host role is a hard requirement | |
| 414 | - | // (sandod refuses to compile anywhere but its configured host), and | |
| 415 | - | // an image that silently stopped being able to build Rust would | |
| 416 | - | // break Sando rather than merely save disk. | |
| 417 | - | langs: BTreeSet::from([Lang::Rust]), | |
| 413 | + | // The default is whatever compiles the stack Alloy ships (Max, | |
| 414 | + | // 2026-08-17): someone rebuilding a program that came with the | |
| 415 | + | // image should not have to install a toolchain to do it. Measured | |
| 416 | + | // off the client image, 14 shipped binaries are Rust (helix, yazi, | |
| 417 | + | // nushell, starship, the whole CLI tier, plus alloy and shop) and 7 | |
| 418 | + | // are Go (syncthing, restic, tailscale, gopass, direnv, fzf, | |
| 419 | + | // cliphist), so both are on and python and zig are not. | |
| 420 | + | // | |
| 421 | + | // Rust also carries the build-host role, which is a hard | |
| 422 | + | // requirement on its own: sandod refuses to compile anywhere but | |
| 423 | + | // its configured host, so an image that silently stopped being able | |
| 424 | + | // to build Rust would break Sando rather than merely save disk. | |
| 425 | + | langs: BTreeSet::from([Lang::Rust, Lang::Go]), | |
| 418 | 426 | hostname: String::new(), | |
| 419 | 427 | pubkey: String::new(), | |
| 420 | 428 | artifact: Artifact::default(), | |
| @@ -1384,11 +1392,13 @@ | |||
| 1384 | 1392 | use super::*; | |
| 1385 | 1393 | ||
| 1386 | 1394 | #[test] | |
| 1387 | - | fn the_default_is_the_desktop_with_rust() { | |
| 1395 | + | fn the_default_is_the_desktop_with_what_compiles_the_stack() { | |
| 1388 | 1396 | let choices = Choices::default(); | |
| 1389 | 1397 | assert_eq!(choices.profile, Profile::Client); | |
| 1390 | 1398 | assert_eq!(choices.browser, Browser::Helium); | |
| 1391 | - | assert!(choices.langs.contains(&Lang::Rust)); | |
| 1399 | + | // Both, and only both: the shipped stack is Rust and Go, and nothing | |
| 1400 | + | // in it is python or zig. See the comment on `Choices::default`. | |
| 1401 | + | assert_eq!(choices.langs, BTreeSet::from([Lang::Rust, Lang::Go])); | |
| 1392 | 1402 | assert_eq!(choices.artifact, Artifact::Iso); | |
| 1393 | 1403 | // Trimmed by default. What it costs is foreign-architecture emulation, | |
| 1394 | 1404 | // which the house rules forbid using in the first place. | |
| @@ -1404,7 +1414,12 @@ | |||
| 1404 | 1414 | let names: Vec<&str> = args.iter().map(|(k, _)| k.as_str()).collect(); | |
| 1405 | 1415 | assert_eq!(names, ["PROFILE", "BROWSER", "LANGS", "TRIM"]); | |
| 1406 | 1416 | assert_eq!(args[0].1, "client"); | |
| 1407 | - | assert_eq!(args[2].1, "rust"); | |
| 1417 | + | // Comma-joined in the enum's declared order, and this is also the | |
| 1418 | + | // literal the Containerfile's own `ARG LANGS` default has to match: | |
| 1419 | + | // the two defaults are one decision written in two files, and a build | |
| 1420 | + | // that bypasses the TUI must get the same stack the TUI would have | |
| 1421 | + | // asked for. | |
| 1422 | + | assert_eq!(args[2].1, "rust,go"); | |
| 1408 | 1423 | assert_eq!(args[3].1, "unused"); | |
| 1409 | 1424 | } | |
| 1410 | 1425 |
| @@ -237,3 +237,48 @@ | |||
| 237 | 237 | "the server prune removes the console palette", | |
| 238 | 238 | ); | |
| 239 | 239 | } | |
| 240 | + | ||
| 241 | + | /// The builder's default language set and the Containerfile's `ARG LANGS` | |
| 242 | + | /// default are one decision written in two files, so they are asserted equal. | |
| 243 | + | /// | |
| 244 | + | /// The rule behind the value (Max, 2026-08-17) is that the default is whatever | |
| 245 | + | /// compiles the stack Alloy ships: a user rebuilding a program that came with | |
| 246 | + | /// the image should not have to install a toolchain first. That makes the | |
| 247 | + | /// value a measurement of the shipped binaries rather than a preference, and | |
| 248 | + | /// it makes drift between the two files a real defect rather than an | |
| 249 | + | /// inconsistency — a build that bypasses the TUI has to get the stack the TUI | |
| 250 | + | /// would have asked for, and `alloy image` printing a command whose `LANGS` | |
| 251 | + | /// matches the ARG it is overriding is how anyone would ever notice. | |
| 252 | + | /// | |
| 253 | + | /// Asserted against the literal in `image.rs` rather than by importing it: the | |
| 254 | + | /// crate is a binary, the test is a text check over the Containerfile in the | |
| 255 | + | /// spirit of the rest of this file, and the point is that the two spellings | |
| 256 | + | /// agree. | |
| 257 | + | #[test] | |
| 258 | + | fn the_langs_default_matches_the_builder() { | |
| 259 | + | let text = containerfile(); | |
| 260 | + | let arg = text | |
| 261 | + | .lines() | |
| 262 | + | .find_map(|line| line.strip_prefix("ARG LANGS=")) | |
| 263 | + | .expect("no ARG LANGS in the Containerfile"); | |
| 264 | + | ||
| 265 | + | let source = | |
| 266 | + | std::fs::read_to_string(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/image.rs")) | |
| 267 | + | .expect("cannot read image.rs"); | |
| 268 | + | let builder = source | |
| 269 | + | .lines() | |
| 270 | + | .find_map(|line| line.trim().strip_prefix("langs: BTreeSet::from([")) | |
| 271 | + | .and_then(|rest| rest.split(']').next()) | |
| 272 | + | .expect("no langs default in image.rs"); | |
| 273 | + | ||
| 274 | + | let from_arg: Vec<&str> = arg.trim().split(',').collect(); | |
| 275 | + | let from_builder: Vec<String> = builder | |
| 276 | + | .split(',') | |
| 277 | + | .filter_map(|entry| entry.trim().strip_prefix("Lang::").map(str::to_lowercase)) | |
| 278 | + | .collect(); | |
| 279 | + | ||
| 280 | + | assert_eq!( | |
| 281 | + | from_arg, from_builder, | |
| 282 | + | "ARG LANGS={arg} and image.rs's default disagree; they are one decision", | |
| 283 | + | ); | |
| 284 | + | } |