From 2058987226e84bf881ba7e411ab2b5a32b8a8012 Mon Sep 17 00:00:00 2001 From: japabu Date: Sat, 10 Oct 2026 14:46:31 +0200 Subject: [PATCH 1/5] toyfetch: what the system says of itself, on ToyOS through a sysinfo fork `/system/bin/toyfetch` prints the OS and build, kernel, arch, CPU model and count, memory, uptime, shell and terminal beside the system's logo. It is an app: the same source builds and runs on macOS, Linux and Windows through upstream sysinfo's backends, and on ToyOS through ToyOSOrg/sysinfo's `toyos-0.39.6`, whose ToyOS backend reads the ambient `SYS_SYSINFO` header, CPUID and `/system/etc/os-release`. It holds nothing: no roster and no compositor connector, so what it shows is what every process may read. The logo, a spinning top, is chosen by os-release's `ID`, never by `cfg`. `toyfetch_reports` boots tests/testcases with three CPUs and holds the lines to the image's os-release, to QEMU's `-smp` and to `free`'s total. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- Cargo.lock | 63 +++++++ Cargo.toml | 2 + system.toml | 10 +- tests/testcases/system.toml | 3 + tests/toyos.rs | 68 ++++++++ userland/toyfetch/Cargo.toml | 9 + userland/toyfetch/src/main.rs | 46 +++++ userland/toyfetch/src/render.rs | 292 ++++++++++++++++++++++++++++++++ 8 files changed, 490 insertions(+), 3 deletions(-) create mode 100644 userland/toyfetch/Cargo.toml create mode 100644 userland/toyfetch/src/main.rs create mode 100644 userland/toyfetch/src/render.rs diff --git a/Cargo.lock b/Cargo.lock index 390e805ffa8..4b5fd706163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3376,6 +3376,15 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6" +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -3696,6 +3705,16 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-io-surface" version = "0.3.2" @@ -5340,6 +5359,21 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "sysinfo" +version = "0.39.6" +source = "git+https://github.com/ToyOSOrg/sysinfo?branch=toyos-0.39.6#52adf173726ed6f0b9b7d6bbf38ccee476368dd5" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "toyos", + "toyos-abi", + "windows", +] + [[package]] name = "tar" version = "0.4.46" @@ -5600,6 +5634,13 @@ dependencies = [ "toyos-window", ] +[[package]] +name = "toyfetch" +version = "0.1.0" +dependencies = [ + "sysinfo", +] + [[package]] name = "toyos" version = "0.18.0" @@ -6664,6 +6705,22 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -6673,6 +6730,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.62.2" diff --git a/Cargo.toml b/Cargo.toml index 90b25618f5a..ec43e9e8eec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,6 +118,7 @@ members = [ "userland/terminal", "userland/test-runner", "userland/toybox", + "userland/toyfetch", "userland/toyos-font", "userland/toyos-pci-claim", "userland/toyos-window", @@ -274,6 +275,7 @@ cpal = { git = "https://github.com/ToyOSOrg/cpal", branch = "toyos-0.18.0" } softbuffer = { git = "https://github.com/ToyOSOrg/softbuffer", branch = "toyos-0.4.8" } winit = { git = "https://github.com/ToyOSOrg/winit", branch = "toyos-0.30.13" } ring = { git = "https://github.com/ToyOSOrg/ring", branch = "toyos-0.17.14" } +sysinfo = { git = "https://github.com/ToyOSOrg/sysinfo", branch = "toyos-0.39.6" } # The one profile every guest binary is built with. # Optimised, because an unoptimised guest mismeasures everything under TCG; diff --git a/system.toml b/system.toml index ba018d9f89c..faf117187c2 100644 --- a/system.toml +++ b/system.toml @@ -92,7 +92,7 @@ devices = ["pci:1af4:1041"] [programs.sshserver] service = true receives = ["netstack"] -starts = ["toybox", "shell", "terminal", "doom", "snake", "calc", "editor", "paint", "files", "host", "inspect", "pkg", "proctest", "sshserver", "swap", "update", "/apps"] +starts = ["toybox", "shell", "terminal", "doom", "snake", "calc", "editor", "paint", "files", "host", "inspect", "pkg", "proctest", "sshserver", "swap", "toyfetch", "update", "/apps"] login = true # `swap` is the authority to replace a running service's binary; the build @@ -143,7 +143,7 @@ starts = ["shell"] [programs.shell] receives = ["surface"] -starts = ["toybox", "shell", "terminal", "doom", "snake", "calc", "editor", "paint", "files", "host", "inspect", "pkg", "proctest", "sshserver", "swap", "update", "/apps"] +starts = ["toybox", "shell", "terminal", "doom", "snake", "calc", "editor", "paint", "files", "host", "inspect", "pkg", "proctest", "sshserver", "swap", "toyfetch", "update", "/apps"] [programs.doom] receives = ["compositor", "soundserver"] @@ -156,6 +156,10 @@ receives = ["compositor", "filepicker"] [programs.calc] receives = ["compositor"] +# toyfetch reads only what every process may: the machine's ambient header, +# CPUID and `/system/etc/os-release`. +[programs.toyfetch] + [programs.editor] receives = ["compositor", "filepicker"] @@ -178,7 +182,7 @@ receives = ["compositor", "filepicker"] [programs.toybox] receives = ["compositor", "soundserver", "surface", "power"] syscap = ["roster"] -starts = ["toybox", "shell", "terminal", "doom", "snake", "calc", "editor", "paint", "files", "host", "inspect", "pkg", "proctest", "sshserver", "swap", "update", "/apps"] +starts = ["toybox", "shell", "terminal", "doom", "snake", "calc", "editor", "paint", "files", "host", "inspect", "pkg", "proctest", "sshserver", "swap", "toyfetch", "update", "/apps"] # Symlinks created on ROOT at build time. # Keys are ROOT-relative; targets are the guest paths they resolve to. diff --git a/tests/testcases/system.toml b/tests/testcases/system.toml index fe24cfb7756..265b722366e 100644 --- a/tests/testcases/system.toml +++ b/tests/testcases/system.toml @@ -60,6 +60,9 @@ receives = ["soundserver"] # `disk_backtrace` names its child's crash record with it. [programs.symbolize] +# `toyfetch_reports` runs it, holding nothing, as the image's row does. +[programs.toyfetch] + # One xHCI controller driven from userland: `usbd_spare` claims the controller # the kernel was told to leave alone (`xhci-leave=`, `pci:1b36:000d` on the # guest and the Type-C controller on the T14), and starts and ends this itself, diff --git a/tests/toyos.rs b/tests/toyos.rs index 001638fa132..7e97a2044f0 100644 --- a/tests/toyos.rs +++ b/tests/toyos.rs @@ -419,6 +419,11 @@ const MACHINE_TESTS: &[&str] = &[ // kernel's driver reading a report a device delivered over xHCI has no host // build, and the T14 binds no USB keyboard. "usb_keyboard_rollover", + // What `toyfetch` reads through the `sysinfo` fork's ToyOS backend: the + // backend calls `SYS_SYSINFO` and reads ROOT, which no host has, and the + // CPU count is held to the `-smp` this boot gave QEMU, a count the + // kernel did not make. + "toyfetch_reports", ]; /// **The metal profile**: which registrations run on the ThinkPad T14, what @@ -3711,6 +3716,7 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> { "update_writes_the_idle_slot_through_the_block_service" => update_writes_the_idle_slot_through_the_block_service(), "usbd_drives_the_spare" => usbd_drives_the_spare(test_config), "usb_keyboard_rollover" => usb_keyboard_rollover(test_config), + "toyfetch_reports" => toyfetch_reports(test_config), other => Err(format!("unknown machine test {other}")), } } @@ -3767,6 +3773,68 @@ fn job_said(qemu: &mut QemuInstance, command: &str) -> Result { Ok(result.stdout) } +/// `toyfetch` names the build the image's os-release does, the CPUs this boot +/// gave QEMU, and the memory total `free` reads off the same ambient header. +fn toyfetch_reports(test_config: &Path) -> Result<(), String> { + const CPUS: u32 = 3; + let mut qemu = QemuInstance::boot_with_options(test_config, &[], &[], BootOptions { smp: CPUS, ..Default::default() }); + let (_, bytes) = toyos_build::image::root_file_on(qemu.boot_image(), toyos_osrelease::PATH)?; + let release = toyos_osrelease::parse(&bytes).map_err(|why| format!("the image's {}: {why:?}", toyos_osrelease::PATH))?; + // `free`'s second line is the total, used and free mebibytes. + let free = job_said(&mut qemu, "free")?; + let total: u64 = free + .lines() + .find_map(|line| line.trim().strip_suffix('M')?.split_whitespace().next()?.strip_suffix('M')?.parse().ok()) + .ok_or_else(|| format!("`free` said no total:\n{free}"))?; + + // The console carries an escape as the four characters `\x1b`. + let said = job_said(&mut qemu, "toyfetch")?.replace('\x1b', "\\x1b"); + // The text a terminal shows: every SGR sequence taken out. + let mut shown = String::new(); + let mut rest = said.as_str(); + while let Some(at) = rest.find("\\x1b[") { + shown.push_str(&rest[..at]); + let end = rest[at..].find('m').ok_or_else(|| format!("an SGR sequence that never ends:\n{said}"))?; + rest = &rest[at + end + 1..]; + } + shown.push_str(rest); + let field = |label: &str| -> Result { + let at = format!("{label}: "); + let mut values = shown.lines().filter_map(|line| Some(line[line.find(&at)? + at.len()..].to_string())); + match (values.next(), values.next()) { + (Some(value), None) => Ok(value), + _ => Err(format!("toyfetch said no single {label} line:\n{shown}")), + } + }; + + let dirty = match release.tree { + toyos_osrelease::Tree::Clean => "", + toyos_osrelease::Tree::Dirty => " (dirty)", + }; + let wanted = [ + ("OS", format!("{} {}{dirty}", toyos_osrelease::NAME, release.short())), + ("Kernel", format!("{} {}", toyos_osrelease::NAME, release.short())), + ("Arch", release.arch.machine().to_string()), + ]; + for (label, value) in wanted { + if field(label)? != value { + return Err(format!("toyfetch's {label} is not {value:?}:\n{shown}")); + } + } + // x86-64 reads its model off CPUID; QEMU names one. + let cpu = field("CPU")?; + if !cpu.ends_with(&format!(" ({CPUS})")) || cpu.starts_with("model not reported") { + return Err(format!("toyfetch's CPU is {cpu:?}, on a boot QEMU gave {CPUS} CPUs:\n{shown}")); + } + let memory = field("Memory")?; + if !memory.ends_with(&format!(" / {total} MiB")) { + return Err(format!("toyfetch's Memory is {memory:?}, and `free` says {total}M in all:\n{shown}")); + } + field("Uptime")?; + eprintln!(" [toyfetch] OS {}, CPU {cpu}, Memory {memory}", field("OS")?); + Ok(()) +} + /// A machine with one NVMe disk and no USB controller boots the image off /// that disk, and what one boot wrote to `/home` and said into `/log` the next /// boot reads back: a file under a name of this run's, line for line, and a diff --git a/userland/toyfetch/Cargo.toml b/userland/toyfetch/Cargo.toml new file mode 100644 index 00000000000..4a68c3196aa --- /dev/null +++ b/userland/toyfetch/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "toyfetch" +description = "Prints what the system it runs on says of itself — OS and build, kernel, CPU, memory, uptime, shell and terminal — beside the system's logo, through sysinfo on every OS." +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +sysinfo = { version = "0.39.6", default-features = false, features = ["system"] } diff --git a/userland/toyfetch/src/main.rs b/userland/toyfetch/src/main.rs new file mode 100644 index 00000000000..783c0d75d04 --- /dev/null +++ b/userland/toyfetch/src/main.rs @@ -0,0 +1,46 @@ +//! `toyfetch`: what the system it runs on says of itself, beside its logo. +//! +//! Every fact comes through `sysinfo` and needs no authority a process does not +//! already hold; a fact the system does not answer is left out, never guessed. + +mod render; + +use std::path::Path; + +use render::{Cpu, Facts, Memory}; +use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; + +fn main() { + let system = System::new_with_specifics( + RefreshKind::nothing() + .with_memory(MemoryRefreshKind::nothing().with_ram()) + .with_cpu(CpuRefreshKind::nothing()), + ); + let cpus = system.cpus(); + let facts = Facts { + os: System::long_os_version(), + kernel: System::kernel_version().map(|_| System::kernel_long_version()), + arch: System::cpu_arch(), + cpu: cpus.first().map(|cpu| Cpu { + brand: Some(cpu.brand().trim().to_owned()).filter(|brand| !brand.is_empty()), + count: cpus.len(), + }), + memory: (system.total_memory() > 0).then(|| Memory { + used: system.used_memory(), + total: system.total_memory(), + }), + uptime: Some(System::uptime()).filter(|&secs| secs > 0), + shell: std::env::var("SHELL").ok().map(|shell| { + Path::new(&shell) + .file_name() + .map_or(shell.clone(), |name| name.to_string_lossy().into_owned()) + }), + terminal: std::env::var("TERM_PROGRAM") + .or_else(|_| std::env::var("TERM")) + .ok(), + }; + print!( + "{}", + render::render(&facts, render::logo_for(&System::distribution_id())) + ); +} diff --git a/userland/toyfetch/src/render.rs b/userland/toyfetch/src/render.rs new file mode 100644 index 00000000000..35401c45e90 --- /dev/null +++ b/userland/toyfetch/src/render.rs @@ -0,0 +1,292 @@ +//! What toyfetch prints: the system's logo on the left, if it has one, and a +//! `label: value` line per fact it answered on the right, in ANSI SGR colour. + +/// What the system said of itself. A fact it did not answer is `None` and +/// prints no line. +pub struct Facts { + pub os: Option, + pub kernel: Option, + pub arch: String, + pub cpu: Option, + pub memory: Option, + /// Seconds since boot. + pub uptime: Option, + pub shell: Option, + pub terminal: Option, +} + +pub struct Cpu { + /// `None` where the system names no model. + pub brand: Option, + pub count: usize, +} + +/// Bytes. +pub struct Memory { + pub used: u64, + pub total: u64, +} + +/// A logo: its lines, each with the SGR foreground colour it is drawn in, and +/// the colour its labels take. +pub struct Logo { + pub lines: &'static [(u8, &'static str)], + pub accent: u8, +} + +/// A spinning top. +const TOYOS: Logo = Logo { + lines: &[ + (37, " _|_"), + (36, " .-' '-."), + (36, " .' '."), + (33, " (====ToyOS====)"), + (35, " '. .'"), + (35, " '. .'"), + (35, " '. .'"), + (37, " V"), + ], + accent: 36, +}; + +/// The logo of the system `os-release(5)`'s `ID` names, if toyfetch has one. +pub fn logo_for(distribution_id: &str) -> Option<&'static Logo> { + match distribution_id { + "toyos" => Some(&TOYOS), + _ => None, + } +} + +const RESET: &str = "\x1b[0m"; +/// Columns between the logo and the facts. +const GAP: usize = 3; +const MIB: u64 = 1024 * 1024; + +pub fn render(facts: &Facts, logo: Option<&Logo>) -> String { + let accent = logo.map_or(36, |logo| logo.accent); + let mut info: Vec = fields(facts) + .into_iter() + .filter_map(|(label, value)| Some(format!("\x1b[1;{accent}m{label}{RESET}: {}", value?))) + .collect(); + info.push(String::new()); + info.push( + (0..8) + .map(|colour| format!("\x1b[4{colour}m ")) + .collect::() + + RESET, + ); + + let logo_lines = logo.map_or(&[][..], |logo| logo.lines); + let width = logo_lines + .iter() + .map(|(_, line)| line.chars().count()) + .max() + .unwrap_or(0); + let mut out = String::new(); + for row in 0..logo_lines.len().max(info.len()) { + let fact = info.get(row).map_or("", String::as_str); + if let Some(logo) = logo { + let (colour, line) = logo.lines.get(row).copied().unwrap_or((0, "")); + if !line.is_empty() { + out.push_str(&format!("\x1b[1;{colour}m{line}{RESET}")); + } + if !fact.is_empty() { + out.push_str(&" ".repeat(width - line.chars().count() + GAP)); + } + } + out.push_str(fact); + out.push('\n'); + } + out +} + +fn fields(facts: &Facts) -> [(&'static str, Option); 8] { + [ + ("OS", facts.os.clone()), + ("Kernel", facts.kernel.clone()), + ("Arch", Some(facts.arch.clone())), + ( + "CPU", + facts.cpu.as_ref().map(|cpu| match &cpu.brand { + Some(brand) => format!("{brand} ({})", cpu.count), + None => format!("model not reported ({})", cpu.count), + }), + ), + ( + "Memory", + facts + .memory + .as_ref() + .map(|m| format!("{} MiB / {} MiB", m.used / MIB, m.total / MIB)), + ), + ("Uptime", facts.uptime.map(uptime)), + ("Shell", facts.shell.clone()), + ("Terminal", facts.terminal.clone()), + ] +} + +/// `secs` as days, hours, minutes and seconds, leaving out each unit that is +/// zero. +fn uptime(secs: u64) -> String { + let units = [ + (secs / 86_400, "d"), + (secs / 3_600 % 24, "h"), + (secs / 60 % 60, "m"), + (secs % 60, "s"), + ]; + let said: Vec = units + .iter() + .filter(|(n, _)| *n > 0) + .map(|(n, unit)| format!("{n}{unit}")) + .collect(); + if said.is_empty() { + "0s".to_owned() + } else { + said.join(" ") + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const SWATCH: &str = "\x1b[40m \x1b[41m \x1b[42m \x1b[43m \x1b[44m \x1b[45m \x1b[46m \x1b[47m \x1b[0m"; + + fn full() -> Facts { + Facts { + os: Some("ToyOS 1a2b3c4d5e6f (dirty)".into()), + kernel: Some("ToyOS 1a2b3c4d5e6f".into()), + arch: "x86_64".into(), + cpu: Some(Cpu { + brand: Some("AMD Ryzen 7 PRO 4750U".into()), + count: 16, + }), + memory: Some(Memory { + used: 300 * MIB + 5, + total: 2048 * MIB, + }), + uptime: Some(90_061), + shell: Some("zsh".into()), + terminal: Some("Apple_Terminal".into()), + } + } + + fn bare() -> Facts { + Facts { + os: None, + kernel: None, + arch: "aarch64".into(), + cpu: None, + memory: None, + uptime: None, + shell: None, + terminal: None, + } + } + + /// The text a terminal shows: every SGR sequence taken out. + fn shown(text: &str) -> String { + let mut out = String::new(); + let mut rest = text; + while let Some(at) = rest.find("\x1b[") { + out.push_str(&rest[..at]); + let end = rest[at..].find('m').expect("an SGR sequence ends in m"); + rest = &rest[at + end + 1..]; + } + out + rest + } + + #[test] + fn every_fact_prints_beside_the_logo_in_its_colours() { + assert_eq!( + render(&full(), Some(&TOYOS)), + "\x1b[1;37m _|_\x1b[0m \x1b[1;36mOS\x1b[0m: ToyOS 1a2b3c4d5e6f (dirty)\n\ + \x1b[1;36m .-' '-.\x1b[0m \x1b[1;36mKernel\x1b[0m: ToyOS 1a2b3c4d5e6f\n\ + \x1b[1;36m .' '.\x1b[0m \x1b[1;36mArch\x1b[0m: x86_64\n\ + \x1b[1;33m (====ToyOS====)\x1b[0m \x1b[1;36mCPU\x1b[0m: AMD Ryzen 7 PRO 4750U (16)\n\ + \x1b[1;35m '. .'\x1b[0m \x1b[1;36mMemory\x1b[0m: 300 MiB / 2048 MiB\n\ + \x1b[1;35m '. .'\x1b[0m \x1b[1;36mUptime\x1b[0m: 1d 1h 1m 1s\n\ + \x1b[1;35m '. .'\x1b[0m \x1b[1;36mShell\x1b[0m: zsh\n\ + \x1b[1;37m V\x1b[0m \x1b[1;36mTerminal\x1b[0m: Apple_Terminal\n\ + \n" + .to_owned() + + " " + + SWATCH + + "\n" + ); + } + + #[test] + fn a_fact_the_system_did_not_answer_prints_no_line() { + assert_eq!( + shown(&render(&bare(), None)), + "Arch: aarch64\n\n \n" + ); + let said = shown(&render(&bare(), Some(&TOYOS))); + for label in [ + "OS", "Kernel", "CPU", "Memory", "Uptime", "Shell", "Terminal", + ] { + assert!(!said.contains(&format!("{label}:")), "{label} in\n{said}"); + } + } + + /// The logo is taller than three facts: its rows below them carry no + /// padding, and every fact starts in one column. + #[test] + fn the_logo_taller_than_the_facts_pads_only_rows_with_a_fact() { + let said = shown(&render(&bare(), Some(&TOYOS))); + let lines: Vec<&str> = said.lines().collect(); + assert_eq!(lines.len(), TOYOS.lines.len()); + assert_eq!(lines[0], " _|_ Arch: aarch64"); + assert_eq!(lines[1], " .-' '-."); + assert_eq!(lines[2], format!(" .' '. {}", " ".repeat(24))); + assert_eq!(lines[3], " (====ToyOS====)"); + } + + /// The facts are taller than the logo: their rows below it start in the + /// column the logo's rows put them in. + #[test] + fn the_facts_taller_than_the_logo_keep_their_column() { + const SHORT: Logo = Logo { + lines: &[(31, "/\\"), (31, "\\/")], + accent: 32, + }; + let said = shown(&render(&full(), Some(&SHORT))); + let lines: Vec<&str> = said.lines().collect(); + assert_eq!(lines[0], "/\\ OS: ToyOS 1a2b3c4d5e6f (dirty)"); + assert_eq!(lines[1], "\\/ Kernel: ToyOS 1a2b3c4d5e6f"); + assert_eq!(lines[2], " Arch: x86_64"); + assert_eq!(lines[7], " Terminal: Apple_Terminal"); + assert_eq!(lines[8], ""); + assert!(render(&full(), Some(&SHORT)).contains("\x1b[1;32mOS\x1b[0m")); + } + + #[test] + fn a_cpu_without_a_model_says_so() { + let facts = Facts { + cpu: Some(Cpu { + brand: None, + count: 4, + }), + ..bare() + }; + assert!(shown(&render(&facts, None)).contains("CPU: model not reported (4)\n")); + } + + #[test] + fn only_toyos_has_a_logo() { + assert!(logo_for("toyos").is_some()); + for other in ["macos", "ubuntu", "windows", "ToyOS", ""] { + assert!(logo_for(other).is_none(), "{other}"); + } + } + + #[test] + fn uptime_leaves_out_zero_units() { + assert_eq!(uptime(0), "0s"); + assert_eq!(uptime(59), "59s"); + assert_eq!(uptime(3_600), "1h"); + assert_eq!(uptime(86_400 + 120), "1d 2m"); + assert_eq!(uptime(90_061), "1d 1h 1m 1s"); + } +} From abc10206b1dddeada3f5bb7efc540a4f5a347cf1 Mon Sep 17 00:00:00 2001 From: japabu Date: Sat, 10 Oct 2026 15:10:36 +0200 Subject: [PATCH 2/5] issues: a process on AArch64 cannot learn its CPU model Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- ...s-on-aarch64-cannot-learn-its-cpu-model.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 issues/a-process-on-aarch64-cannot-learn-its-cpu-model.md diff --git a/issues/a-process-on-aarch64-cannot-learn-its-cpu-model.md b/issues/a-process-on-aarch64-cannot-learn-its-cpu-model.md new file mode 100644 index 00000000000..45d866cbdd2 --- /dev/null +++ b/issues/a-process-on-aarch64-cannot-learn-its-cpu-model.md @@ -0,0 +1,25 @@ +--- +status: open +kind: track +opened: 2026-10-10 +--- + +# A process on AArch64 cannot learn its CPU model + +On x86-64 a process reads its CPU's vendor and brand string with CPUID, +which the kernel does not fault, and ToyOSOrg/sysinfo's ToyOS backend +answers `Cpu::brand` and `Cpu::vendor_id` from it. On AArch64 the model is +`MIDR_EL1`, which traps from EL0, and an unserved EL0 sync exception ends the +process (`kernel/src/arch/aarch64/trap.rs`); the kernel publishes it nowhere. +So the backend answers empty strings there, and `toyfetch` says "model not +reported". + +Built: nothing. The answer is ambient, as the `SYS_SYSINFO` header is: a +CPU's identity authorizes nothing. + +## Exit condition + +A process on AArch64 reads each CPU's `MIDR_EL1` (implementer, part, +variant, revision) without a capability, the fork's AArch64 arm answers +`vendor_id` and `brand` from it, and `toyfetch` on the `virt` machine prints +a model. From 7cf435af36e3943504efca7f97bd0fc297685b86 Mon Sep 17 00:00:00 2001 From: japabu Date: Sat, 10 Oct 2026 15:45:00 +0200 Subject: [PATCH 3/5] toyfetch: the sysinfo fork reads os-release through toyos-osrelease toyos-osrelease is published beside toyos-abi, toyos and toyos-window, and redirected to its path under [patch.crates-io], so the sysinfo fork depends on it by version and reads GUEST_PATH through parse: the file has one path and one parser, and the fork's own reader is gone (ToyOSOrg/sysinfo bcb23815, appended to toyos-0.39.6). The crate gains ID and Release::pretty_name, which its Display writes with, so the fork reads PRETTY_NAME and ID from the declaration rather than spelling them again. The fork's kernel_version is now uname_release, so sysinfo and `uname -r` agree on a dirty build; toyfetch_reports asserts both lines through the crate. sdkversion's model fork names toyos-osrelease as sysinfo's does, so the published manifests are held to resolve it too. Logo::accent took one value; it is the const ACCENT. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- Cargo.lock | 3 ++- Cargo.toml | 1 + src/sdkversion.rs | 4 +++- tests/toyos.rs | 8 ++------ toyos-osrelease/Cargo.toml | 5 +++-- toyos-osrelease/src/lib.rs | 36 ++++++++++++++++++++++++--------- userland/toyfetch/src/render.rs | 18 +++++++---------- 7 files changed, 45 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b5fd706163..1a41e0a3b0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5362,7 +5362,7 @@ dependencies = [ [[package]] name = "sysinfo" version = "0.39.6" -source = "git+https://github.com/ToyOSOrg/sysinfo?branch=toyos-0.39.6#52adf173726ed6f0b9b7d6bbf38ccee476368dd5" +source = "git+https://github.com/ToyOSOrg/sysinfo?branch=toyos-0.39.6#bcb2381592d4e8a9a40f11f41ccc3c915ca6a876" dependencies = [ "libc", "memchr", @@ -5371,6 +5371,7 @@ dependencies = [ "objc2-io-kit", "toyos", "toyos-abi", + "toyos-osrelease", "windows", ] diff --git a/Cargo.toml b/Cargo.toml index ec43e9e8eec..20d33e474c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -262,6 +262,7 @@ loom = { git = "https://github.com/ToyOSOrg/loom", branch = "toyos" } toyos-abi = { path = "toyos-abi" } toyos = { path = "toyos" } toyos-window = { path = "userland/toyos-window" } +toyos-osrelease = { path = "toyos-osrelease" } getrandom = { git = "https://github.com/ToyOSOrg/getrandom", branch = "toyos-0.2-sdk-0.12", package = "getrandom" } getrandom_03 = { git = "https://github.com/ToyOSOrg/getrandom", branch = "toyos-0.3-sdk-0.12", package = "getrandom" } getrandom_04 = { git = "https://github.com/ToyOSOrg/getrandom", branch = "toyos-0.4", package = "getrandom" } diff --git a/src/sdkversion.rs b/src/sdkversion.rs index 99890315bb7..a3552abe400 100644 --- a/src/sdkversion.rs +++ b/src/sdkversion.rs @@ -23,6 +23,7 @@ pub struct Crate { pub const PUBLISHED: &[Crate] = &[ Crate { name: "toyos-abi", dir: "toyos-abi" }, Crate { name: "toyos-keymap", dir: "toyos-keymap" }, + Crate { name: "toyos-osrelease", dir: "toyos-osrelease" }, Crate { name: "toyos-font", dir: "userland/toyos-font" }, Crate { name: "toyos", dir: "toyos" }, Crate { name: "toyos-window", dir: "userland/toyos-window" }, @@ -268,7 +269,8 @@ mod tests { std::fs::write(tmp.join("fork/src/lib.rs"), "").unwrap(); let fork = format!( "[package]\nname = \"fork\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n[dependencies]\n\ - toyos-abi = \">=0.12, <1\"\ntoyos = \">=0.13, <1\"\ntoyos-window = \">=0.15, <1\"\n\n\ + toyos-abi = \">=0.12, <1\"\ntoyos = \">=0.13, <1\"\ntoyos-window = \">=0.15, <1\"\n\ + toyos-osrelease = \">=0.1, <1\"\n\n\ [patch.crates-io]\n{patch}" ); std::fs::write(tmp.join("fork/Cargo.toml"), fork).unwrap(); diff --git a/tests/toyos.rs b/tests/toyos.rs index 7e97a2044f0..3b2c77f2c8f 100644 --- a/tests/toyos.rs +++ b/tests/toyos.rs @@ -3807,13 +3807,9 @@ fn toyfetch_reports(test_config: &Path) -> Result<(), String> { } }; - let dirty = match release.tree { - toyos_osrelease::Tree::Clean => "", - toyos_osrelease::Tree::Dirty => " (dirty)", - }; let wanted = [ - ("OS", format!("{} {}{dirty}", toyos_osrelease::NAME, release.short())), - ("Kernel", format!("{} {}", toyos_osrelease::NAME, release.short())), + ("OS", release.pretty_name().to_string()), + ("Kernel", format!("{} {}", toyos_osrelease::NAME, release.uname_release())), ("Arch", release.arch.machine().to_string()), ]; for (label, value) in wanted { diff --git a/toyos-osrelease/Cargo.toml b/toyos-osrelease/Cargo.toml index 82270851eec..61247eb533a 100644 --- a/toyos-osrelease/Cargo.toml +++ b/toyos-osrelease/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "toyos-osrelease" -description = "The one definition of /system/etc/os-release: which build an image is, written by the build and read by the supervisor and libc's uname (no_std, no alloc)." -version = "0.1.0" +description = "The one definition of /system/etc/os-release: which build an image is, written by the build and read by the supervisor, libc's uname and the sysinfo fork (no_std, no alloc)." +version = "0.1.0" # never bumped; crates.io's is assigned on publish (src/sdkversion.rs) edition = "2021" license = "MIT OR Apache-2.0" +repository = "https://github.com/ToyOSOrg/ToyOS" diff --git a/toyos-osrelease/src/lib.rs b/toyos-osrelease/src/lib.rs index b0197831ad0..c738b6a2610 100644 --- a/toyos-osrelease/src/lib.rs +++ b/toyos-osrelease/src/lib.rs @@ -1,5 +1,6 @@ //! `/system/etc/os-release`: which build an image is, written by the build -//! system into ROOT and read by `/system/bin/supervisor` and libc's `uname`. +//! system into ROOT and read by `/system/bin/supervisor`, libc's `uname` and +//! the sysinfo fork. //! //! The freedesktop os-release format (`os-release(5)`), ToyOS's own fields //! prefixed `TOYOS_` as it asks. The build is the file's one writer, so @@ -46,6 +47,9 @@ pub const GUEST_PATH: &str = concat!("/system/", path!()); /// The operating system's name, `NAME` and `uname`'s `sysname`. pub const NAME: &str = "ToyOS"; +/// `ID`, the operating system's identifier. +pub const ID: &str = "toyos"; + /// What `/system/bin/supervisor` says a release under, followed by its commit. pub const SAID: &str = "supervisor: build "; @@ -130,6 +134,25 @@ impl Release { pub fn uname_release(&self) -> UnameRelease<'_> { UnameRelease(self) } + + /// `PRETTY_NAME`: [`NAME`], [`Release::short`], and ` (dirty)` after it + /// for a tree that was not that commit's. + pub fn pretty_name(&self) -> PrettyName<'_> { + PrettyName(self) + } +} + +/// [`Release::pretty_name`]. +pub struct PrettyName<'a>(&'a Release); + +impl fmt::Display for PrettyName<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{NAME} {}", self.0.short())?; + match self.0.tree { + Tree::Clean => Ok(()), + Tree::Dirty => f.write_str(" (dirty)"), + } + } } /// [`Release::uname_release`]. @@ -148,15 +171,10 @@ impl fmt::Display for UnameRelease<'_> { /// The file's bytes. impl fmt::Display for Release { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let short = self.short(); - let dirty = match self.tree { - Tree::Clean => "", - Tree::Dirty => " (dirty)", - }; writeln!(f, "NAME=\"{NAME}\"")?; - writeln!(f, "ID=\"toyos\"")?; - writeln!(f, "VERSION_ID=\"{short}\"")?; - writeln!(f, "PRETTY_NAME=\"{NAME} {short}{dirty}\"")?; + writeln!(f, "ID=\"{ID}\"")?; + writeln!(f, "VERSION_ID=\"{}\"", self.short())?; + writeln!(f, "PRETTY_NAME=\"{}\"", self.pretty_name())?; writeln!(f, "{BUILD_ID}=\"{}\"", self.commit.as_str())?; writeln!(f, "{ARCHITECTURE}=\"{}\"", self.arch.os_release())?; writeln!(f, "HOME_URL=\"https://github.com/ToyOSOrg/ToyOS\"")?; diff --git a/userland/toyfetch/src/render.rs b/userland/toyfetch/src/render.rs index 35401c45e90..df2ae51c53b 100644 --- a/userland/toyfetch/src/render.rs +++ b/userland/toyfetch/src/render.rs @@ -27,11 +27,9 @@ pub struct Memory { pub total: u64, } -/// A logo: its lines, each with the SGR foreground colour it is drawn in, and -/// the colour its labels take. +/// A logo: its lines, each with the SGR foreground colour it is drawn in. pub struct Logo { pub lines: &'static [(u8, &'static str)], - pub accent: u8, } /// A spinning top. @@ -46,7 +44,6 @@ const TOYOS: Logo = Logo { (35, " '. .'"), (37, " V"), ], - accent: 36, }; /// The logo of the system `os-release(5)`'s `ID` names, if toyfetch has one. @@ -57,16 +54,17 @@ pub fn logo_for(distribution_id: &str) -> Option<&'static Logo> { } } +/// The SGR foreground colour every label takes. +const ACCENT: u8 = 36; const RESET: &str = "\x1b[0m"; /// Columns between the logo and the facts. const GAP: usize = 3; const MIB: u64 = 1024 * 1024; pub fn render(facts: &Facts, logo: Option<&Logo>) -> String { - let accent = logo.map_or(36, |logo| logo.accent); let mut info: Vec = fields(facts) .into_iter() - .filter_map(|(label, value)| Some(format!("\x1b[1;{accent}m{label}{RESET}: {}", value?))) + .filter_map(|(label, value)| Some(format!("\x1b[1;{ACCENT}m{label}{RESET}: {}", value?))) .collect(); info.push(String::new()); info.push( @@ -155,7 +153,7 @@ mod tests { fn full() -> Facts { Facts { os: Some("ToyOS 1a2b3c4d5e6f (dirty)".into()), - kernel: Some("ToyOS 1a2b3c4d5e6f".into()), + kernel: Some("ToyOS 1a2b3c4d5e6f-dirty".into()), arch: "x86_64".into(), cpu: Some(Cpu { brand: Some("AMD Ryzen 7 PRO 4750U".into()), @@ -201,7 +199,7 @@ mod tests { assert_eq!( render(&full(), Some(&TOYOS)), "\x1b[1;37m _|_\x1b[0m \x1b[1;36mOS\x1b[0m: ToyOS 1a2b3c4d5e6f (dirty)\n\ - \x1b[1;36m .-' '-.\x1b[0m \x1b[1;36mKernel\x1b[0m: ToyOS 1a2b3c4d5e6f\n\ + \x1b[1;36m .-' '-.\x1b[0m \x1b[1;36mKernel\x1b[0m: ToyOS 1a2b3c4d5e6f-dirty\n\ \x1b[1;36m .' '.\x1b[0m \x1b[1;36mArch\x1b[0m: x86_64\n\ \x1b[1;33m (====ToyOS====)\x1b[0m \x1b[1;36mCPU\x1b[0m: AMD Ryzen 7 PRO 4750U (16)\n\ \x1b[1;35m '. .'\x1b[0m \x1b[1;36mMemory\x1b[0m: 300 MiB / 2048 MiB\n\ @@ -249,16 +247,14 @@ mod tests { fn the_facts_taller_than_the_logo_keep_their_column() { const SHORT: Logo = Logo { lines: &[(31, "/\\"), (31, "\\/")], - accent: 32, }; let said = shown(&render(&full(), Some(&SHORT))); let lines: Vec<&str> = said.lines().collect(); assert_eq!(lines[0], "/\\ OS: ToyOS 1a2b3c4d5e6f (dirty)"); - assert_eq!(lines[1], "\\/ Kernel: ToyOS 1a2b3c4d5e6f"); + assert_eq!(lines[1], "\\/ Kernel: ToyOS 1a2b3c4d5e6f-dirty"); assert_eq!(lines[2], " Arch: x86_64"); assert_eq!(lines[7], " Terminal: Apple_Terminal"); assert_eq!(lines[8], ""); - assert!(render(&full(), Some(&SHORT)).contains("\x1b[1;32mOS\x1b[0m")); } #[test] From 4d8e6f48f45c6d2d4ae2924d825a6934df70e905 Mon Sep 17 00:00:00 2001 From: japabu Date: Sat, 10 Oct 2026 16:12:28 +0200 Subject: [PATCH 4/5] sdkversion: toyos-osrelease is PUBLISHED's last row until crates.io holds it crates.io takes a crate's first version only with an API token, and publish.yml has only the trusted-publishing token, so main's publish job is refused at toyos-osrelease until the owner publishes it once. The publisher stops at its first refusal; as the last row, nothing depends on it and every crate above it still goes up. Skipping a crate the index has never seen was the other design: more code, and a refusal turned green. The track records the owner's stage, with an exit a read of the index can fail, and stops counting the SDK crates. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- issues/toyos-is-a-normal-target.md | 22 +++++++++++++++------- src/sdkversion.rs | 4 +++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/issues/toyos-is-a-normal-target.md b/issues/toyos-is-a-normal-target.md index 48a31bacb11..77f20d38a56 100644 --- a/issues/toyos-is-a-normal-target.md +++ b/issues/toyos-is-a-normal-target.md @@ -9,19 +9,27 @@ opened: 2026-09-03 A clean clone of any third-party program that targets ToyOS cannot resolve its dependencies on Linux or macOS, because cargo resolves every platform's dependencies and the ToyOS crates the forks name — `toyos-abi`, `toyos`, -`toyos-window` — are on no registry. A git dependency on the monorepo is not the -answer: it clones the `rust` submodule. So the five SDK crates go on crates.io -and stay there. The ABI they carry is unstable by owner ruling: a built program +`toyos-window`, `toyos-osrelease` — are on no registry. A git dependency on the +monorepo is not the answer: it clones the `rust` submodule. So the SDK crates, +`src/sdkversion.rs` `PUBLISHED`, go on crates.io and stay there. The ABI they carry is unstable by owner ruling: a built program that breaks, breaks. Stages, in order: 1. **Done.** `toyos-abi`, `toyos-keymap`, `toyos-font`, `toyos` and `toyos-window` carry a description and a repository, and are published by - `.github/workflows/publish.yml`. -2. **The owner's.** `CARGO_REGISTRY_TOKEN` as a repository secret, then the - first publish. Until it is there the publish job fails by name on every - landing, which is the intended noise. + `.github/workflows/publish.yml` through crates.io trusted publishing. +2. **The owner's: `toyos-osrelease`'s first publish.** It is in `PUBLISHED`, + and the sysinfo fork names it by version, but crates.io takes a crate's + first version only with an API token, and trusted publishing is configured + per crate after it. Until then main's `publish` job is red on every landing, + refused at `toyos-osrelease`, which is `PUBLISHED`'s last row so that every + crate above it still goes up; and the sysinfo fork resolves only inside this + workspace, through its `[patch.crates-io]`. The owner publishes it once with + a token and names `publish.yml` its trusted publisher. Exit: + `https://index.crates.io/to/yo/toyos-osrelease` answers 200, main's next + `publish` is green, and the row's "last until crates.io holds it" comment + is deleted. 3. **The forks.** softbuffer names `toyos-window` and sits on the v0.4.8 release, and raw-window-handle sits on v0.6.2, so nothing the window path goes through is based on a master any diff --git a/src/sdkversion.rs b/src/sdkversion.rs index a3552abe400..090522d3b9a 100644 --- a/src/sdkversion.rs +++ b/src/sdkversion.rs @@ -23,10 +23,12 @@ pub struct Crate { pub const PUBLISHED: &[Crate] = &[ Crate { name: "toyos-abi", dir: "toyos-abi" }, Crate { name: "toyos-keymap", dir: "toyos-keymap" }, - Crate { name: "toyos-osrelease", dir: "toyos-osrelease" }, Crate { name: "toyos-font", dir: "userland/toyos-font" }, Crate { name: "toyos", dir: "toyos" }, Crate { name: "toyos-window", dir: "userland/toyos-window" }, + // Last until crates.io holds it: its first publish needs the owner's token, and + // until then the run stops here with every crate above it up. + Crate { name: "toyos-osrelease", dir: "toyos-osrelease" }, ]; /// One crate at this tree: the version crates.io has or is owed, whether it is From 7d89aa2a728b99b98e48e36f688d9102a61f883a Mon Sep 17 00:00:00 2001 From: japabu Date: Sat, 10 Oct 2026 17:43:47 +0200 Subject: [PATCH 5/5] sdkversion: crates.io holds toyos-osrelease, and the tree track places it in sdk/ The owner authorized its first publish with his token: from a --shared --no-recurse-submodules clone of 4d8e6f48f, the tree's own sdkversion plan assigned 0.1.0+95aa302d859ac447, cargo publish took it, and index.crates.io/to/yo/toyos-osrelease answers 200. Its crates.io trusted publisher is ToyOSOrg/ToyOS publish.yml, as every other SDK crate's is, so main's publish job takes every later version. The row's "last until crates.io holds it" comment goes with the reason for it; the row stays last, which is still a dependency order. Stage 2 of the normal-target track is done. The tree track had toyos-osrelease in lib/ (rule step 5), but the sysinfo fork, outside this repository, now uses it from crates.io: rule step 3 puts it in sdk/, and stage 5 moves it with the other SDK crates. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C --- issues/the-tree-says-who-uses-each-thing.md | 11 ++++++----- issues/toyos-is-a-normal-target.md | 13 ++----------- src/sdkversion.rs | 2 -- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/issues/the-tree-says-who-uses-each-thing.md b/issues/the-tree-says-who-uses-each-thing.md index 07e68c8d47b..426f148efed 100644 --- a/issues/the-tree-says-who-uses-each-thing.md +++ b/issues/the-tree-says-who-uses-each-thing.md @@ -128,9 +128,9 @@ apps/ CLAUDE.md (sdk/ and registries only; Linux under Wayland, macOS, Windo calc editor files paint snake doom/ (doomgeneric/ DOOM1.WAD soundfont licences); toyfetch when built sdk/ CLAUDE.md (identity, sysroot, publication; std links abi and toyos) - abi/ toyos/ std/ keymap/ font/ window/ filepicker/ libc/ (arch/) + abi/ toyos/ std/ keymap/ font/ window/ filepicker/ osrelease/ libc/ (arch/) lib/ acpi bcachefs blackbox blockhold blockring bootmap elf elide fat32 gpt hda i219 inspect - logstream manifest osrelease quiesce rootimage swap symbols tco tmpdir tsc untrusted + logstream manifest quiesce rootimage swap symbols tco tmpdir tsc untrusted update userbound wallclock xhci share/ mirrors /system/share: fonts/ (+ OFL) icons/ (+ MIT) images/ system.toml console.toml diag.toml @@ -156,8 +156,9 @@ Where this differs from the tree the owner adopted: ruling `files` stops using it, which leaves the compositor its one user (rule step 4). - `apps/` names toyfetch, by the Apps ruling. -- `lib/osrelease` is `toyos-osrelease`, which #722 added after the design; - the build, `libc` and the supervisor use it (rule step 5). +- `sdk/osrelease` is `toyos-osrelease`, which #722 added after the design; + the sysinfo fork, outside this repository, uses it from crates.io (rule + step 3). - `lib/tsc` is `toyos-tsc`, which #721 added after the design; the kernel and the loader use it (rule step 5). - `sdk/std/` is std's ToyOS backend, which left the `rust` fork after the @@ -186,7 +187,7 @@ tree's top level: `.cargo`, `.claude`, `.github`, `.gitignore`, 3. **One workspace.** Landed in #746. 4. **std off repository paths:** `issues/std-names-the-sdk-crates-by-path.md`. **Exit:** that file's. -5. **`sdk/`:** abi, toyos, keymap, font, window, filepicker and libc (with +5. **`sdk/`:** abi, toyos, keymap, font, window, filepicker, osrelease and libc (with `tests/libc-arch` as `sdk/libc/arch/`); `src/sysroot.rs`, `src/sdkversion.rs` and `src/release.rs` read the directory instead of a list. A branch that bumps the `rust` gitlink merges the fork change with diff --git a/issues/toyos-is-a-normal-target.md b/issues/toyos-is-a-normal-target.md index 77f20d38a56..b6e43f8c0e7 100644 --- a/issues/toyos-is-a-normal-target.md +++ b/issues/toyos-is-a-normal-target.md @@ -19,17 +19,8 @@ Stages, in order: 1. **Done.** `toyos-abi`, `toyos-keymap`, `toyos-font`, `toyos` and `toyos-window` carry a description and a repository, and are published by `.github/workflows/publish.yml` through crates.io trusted publishing. -2. **The owner's: `toyos-osrelease`'s first publish.** It is in `PUBLISHED`, - and the sysinfo fork names it by version, but crates.io takes a crate's - first version only with an API token, and trusted publishing is configured - per crate after it. Until then main's `publish` job is red on every landing, - refused at `toyos-osrelease`, which is `PUBLISHED`'s last row so that every - crate above it still goes up; and the sysinfo fork resolves only inside this - workspace, through its `[patch.crates-io]`. The owner publishes it once with - a token and names `publish.yml` its trusted publisher. Exit: - `https://index.crates.io/to/yo/toyos-osrelease` answers 200, main's next - `publish` is green, and the row's "last until crates.io holds it" comment - is deleted. +2. **Done.** `toyos-osrelease`, which the sysinfo fork names by version, is + on crates.io, and names `publish.yml` its trusted publisher. 3. **The forks.** softbuffer names `toyos-window` and sits on the v0.4.8 release, and raw-window-handle sits on v0.6.2, so nothing the window path goes through is based on a master any diff --git a/src/sdkversion.rs b/src/sdkversion.rs index 090522d3b9a..4cdfa2a635b 100644 --- a/src/sdkversion.rs +++ b/src/sdkversion.rs @@ -26,8 +26,6 @@ pub const PUBLISHED: &[Crate] = &[ Crate { name: "toyos-font", dir: "userland/toyos-font" }, Crate { name: "toyos", dir: "toyos" }, Crate { name: "toyos-window", dir: "userland/toyos-window" }, - // Last until crates.io holds it: its first publish needs the owner's token, and - // until then the run stops here with every crate above it up. Crate { name: "toyos-osrelease", dir: "toyos-osrelease" }, ];