diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index e7c26ff..2318fdf 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -1,11 +1,11 @@ -name: 'Setup Rust Environment' -description: 'Sets up Rust toolchain with caching' +name: "Setup Rust Environment" +description: "Sets up Rust toolchain with caching" runs: - using: 'composite' + using: "composite" steps: - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@c5a29ddb4d9d194e7c84ec8c3fba61b1c31fee8c + uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 with: toolchain: stable @@ -19,4 +19,3 @@ runs: with: # Optional: cache dependencies as well cache-on-failure: true - \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b07d4bb..05d52d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,10 +5,12 @@ on: branches: - master - main + - dev pull_request: branches: - master - main + - dev concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..420ca84 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Release + +permissions: + pull-requests: write + contents: write + +on: + push: + branches: + - main + +jobs: + release-plz: + name: Release-plz + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + fetch-depth: 0 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable as of 2026-01-19 + + - name: Run release-plz + uses: MarcoIeni/release-plz-action@5ab144c9d67d4346240190d0f95ed08668677928 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_PUBLISH }} diff --git a/README.md b/README.md index d31a639..f0cd7b2 100644 --- a/README.md +++ b/README.md @@ -74,22 +74,47 @@ execution model while preserving the familiar POSIX/Linux syscall interface. ## Design Principles -ZeroOS is guided by a set of foundational principles aimed at delivering maximum security, transparency, and trust for developers building verifiable applications. +ZeroOS is guided by a set of foundational principles aimed at delivering maximum +security, transparency, and trust for developers building verifiable +applications. ### Fail-Fast Engineering -ZeroOS follows a fail-fast philosophy: rather than masking incomplete features behind silent stubs, the kernel immediately returns an error or halts when encountering unsupported requests (such as certain mmap flags or malloc options). This approach guarantees that every execution path in a zkVM trace is both intentional and fully supported. + +ZeroOS follows a fail-fast philosophy: rather than masking incomplete features +behind silent stubs, the kernel immediately returns an error or halts when +encountering unsupported requests (such as certain mmap flags or malloc +options). This approach guarantees that every execution path in a zkVM trace is +both intentional and fully supported. ### Toolchain Integrity -Developers can rely on standard toolchains without modification. ZeroOS works seamlessly with off-the-shelf musl-libc and Rust std, keeping the Trusted Computing Base (TCB) lean. By avoiding custom compiler patches or altered libraries, audit efforts remain focused on the ZeroOS kernel and application logic, not sprawling toolchain changes. + +Developers can rely on standard toolchains without modification. ZeroOS works +seamlessly with off-the-shelf musl-libc and Rust std, keeping the Trusted +Computing Base (TCB) lean. By avoiding custom compiler patches or altered +libraries, audit efforts remain focused on the ZeroOS kernel and application +logic, not sprawling toolchain changes. ### Surgical Modularity -The kernel is built from modular, compile-time configurable components. This design lets developers include only the functionality they truly need, reducing binary size and zkVM trace length. With less code to review, audits become more straightforward and effective. + +The kernel is built from modular, compile-time configurable components. This +design lets developers include only the functionality they truly need, reducing +binary size and zkVM trace length. With less code to review, audits become more +straightforward and effective. ### Architectural Transparency -ZeroOS prioritizes clarity over abstraction. Core operations—such as thread creation, trap handling, and context switching—are implemented with explicit initialization and visible resource ownership. This transparency makes it easier to inspect critical paths for vulnerabilities and ensures deterministic behavior. + +ZeroOS prioritizes clarity over abstraction. Core operations—such as thread +creation, trap handling, and context switching—are implemented with explicit +initialization and visible resource ownership. This transparency makes it easier +to inspect critical paths for vulnerabilities and ensures deterministic +behavior. ### Unikernel Simplicity -Operating as a statically linked unikernel in a single address space, ZeroOS avoids the complexity of multiple privilege levels and MMU management. In zkVM environments, these mechanisms often add unnecessary overhead and subtle risks. By stripping them away, ZeroOS achieves a simpler, more secure execution model. + +Operating as a statically linked unikernel in a single address space, ZeroOS +avoids the complexity of multiple privilege levels and MMU management. In zkVM +environments, these mechanisms often add unnecessary overhead and subtle risks. +By stripping them away, ZeroOS achieves a simpler, more secure execution model. ## Getting Started @@ -128,6 +153,14 @@ or cargo massage ``` +### Run CI locally (act) + +To install `act`, visit for installation instructions. + +```bash +cargo act pull_request +``` + ## License See `LICENSE-MIT` and `LICENSE-APACHE` for details. diff --git a/bootstrap b/bootstrap index 5ce020b..dca74ff 100755 --- a/bootstrap +++ b/bootstrap @@ -442,6 +442,7 @@ EOF log "Shell integration installed into ~/.bashrc and/or ~/.zshrc" fi log "Please restart your shell or run 'direnv allow' to activate the environment." + log " or run 'exec \$SHELL -l' to reload your shell." } main "$@" diff --git a/crates/cargo-matrix/src/matrix.rs b/crates/cargo-matrix/src/matrix.rs index 36e1a2a..5920cd2 100644 --- a/crates/cargo-matrix/src/matrix.rs +++ b/crates/cargo-matrix/src/matrix.rs @@ -59,12 +59,19 @@ enum FeatureSpec { OneOf(Vec), } +#[derive(serde::Deserialize)] +#[serde(untagged)] +pub enum Packages { + One(String), + Many(Vec), +} + #[derive(serde::Deserialize)] struct MatrixEntry { #[serde(default)] commands: BTreeMap, command: Option, - package: String, + package: Packages, target: Targets, #[serde(default)] features: Vec, @@ -225,103 +232,117 @@ pub fn run(args: MatrixArgs) -> Result<(), String> { } for entry in &cfg.entries { - if !args.packages.is_empty() && !args.packages.iter().any(|p| p == &entry.package) { - continue; - } + let entry_packages = match &entry.package { + Packages::One(s) => vec![s.as_str()], + Packages::Many(v) => v.iter().map(|s| s.as_str()).collect(), + }; + + for package in entry_packages { + if !args.packages.is_empty() && !args.packages.iter().any(|p| p == package) { + continue; + } + + let cmd_name = entry + .command + .as_ref() + .or(command.as_ref()) + .ok_or_else(|| "no command selected (pass --command )".to_string())?; + + let template = entry + .commands + .get(cmd_name) + .or_else(|| cfg.commands.get(cmd_name)); + + let template: &str = if let Some(t) = template { + t.as_str() + } else { + // Strict Structural Resolution. + // If a command is not defined in the package-local or global commands map, + // the package is considered to not support this command and is skipped. + continue; + }; - let cmd_name = entry - .command - .as_ref() - .or(command.as_ref()) - .ok_or_else(|| "no command selected (pass --command )".to_string())?; - - let template: &str = entry - .commands - .get(cmd_name) - .or_else(|| cfg.commands.get(cmd_name)) - .map(|s| s.as_str()) - .unwrap_or(cmd_name); - - let mut combos: Vec> = vec![Vec::new()]; - for spec in &entry.features { - match spec { - FeatureSpec::One(f) => { - for c in &mut combos { - c.push(f.clone()); + let mut combos: Vec> = vec![Vec::new()]; + for spec in &entry.features { + match spec { + FeatureSpec::One(f) => { + for c in &mut combos { + c.push(f.clone()); + } } - } - FeatureSpec::OneOf(group) => { - let mut next: Vec> = Vec::new(); - for opt in group { - for c in &combos { - let mut nc = c.clone(); - nc.push(opt.clone()); - next.push(nc); + FeatureSpec::OneOf(group) => { + let mut next: Vec> = Vec::new(); + for opt in group { + for c in &combos { + let mut nc = c.clone(); + nc.push(opt.clone()); + next.push(nc); + } } + combos = next; } - combos = next; } } - } - fn flatten_targets<'a>(t: &'a TargetElem, out: &mut Vec<&'a str>) { - match t { - TargetElem::One(s) => out.push(s.as_str()), - TargetElem::Many(v) => { - for inner in v { - flatten_targets(inner, out); + fn flatten_targets<'a>(t: &'a TargetElem, out: &mut Vec<&'a str>) { + match t { + TargetElem::One(s) => out.push(s.as_str()), + TargetElem::Many(v) => { + for inner in v { + flatten_targets(inner, out); + } } } } - } - let targets: Vec<&str> = match &entry.target { - Targets::One(t) => vec![t.as_str()], - Targets::Many(ts) => { - let mut out: Vec<&str> = Vec::new(); - for t in ts { - flatten_targets(t, &mut out); + let targets: Vec<&str> = match &entry.target { + Targets::One(t) => vec![t.as_str()], + Targets::Many(ts) => { + let mut out: Vec<&str> = Vec::new(); + for t in ts { + flatten_targets(t, &mut out); + } + out } - out - } - }; - - for target in targets { - let target = if target == "host" { - host.as_str() - } else { - target }; - let total = combos.len(); - for (idx, mut feats) in combos.iter().cloned().enumerate() { - feats.sort(); - feats.dedup(); - let feat_str = feats.join(","); - let features_flag = if feat_str.is_empty() { - String::new() - } else { - format!(r##"--features "{feat_str}""##) - }; - let cmd = render_template( - template, - &workspace, - &entry.package, - target, - &feat_str, - &features_flag, - ); - - let suffix = if total > 1 { - format!(" #{}/{}", idx + 1, total) + for target in targets { + let target = if target == "host" { + host.as_str() } else { - String::new() + target }; - - steps.push(Step { - name: format!("{} [{target}] ({cmd_name}){suffix}", entry.package), - cmd, - }); + let total = combos.len(); + for (idx, mut feats) in combos.iter().cloned().enumerate() { + feats.sort(); + feats.dedup(); + let feat_str = feats.join(","); + let features_flag = if feat_str.is_empty() { + String::new() + } else { + format!(r##"--features "{feat_str}""##) + }; + + let cmd = render_template( + template, + &workspace, + package, + target, + &feat_str, + &features_flag, + ); + + let suffix = if total > 1 { + format!(" #{}/{}", idx + 1, total) + } else { + String::new() + }; + + steps.push(Step { + name: format!("{package} [{target}] ({cmd_name}){suffix}"), + cmd, + }); + } } } } diff --git a/matrix.yaml b/matrix.yaml index fbc0862..d945ada 100644 --- a/matrix.yaml +++ b/matrix.yaml @@ -34,37 +34,27 @@ guest_targets: &guest_targets - *targets_linux_musl_gc entries: - - package: xtask + - package: + - xtask + - cargo-matrix + - mini-template + - zeroos-build + - spike-build target: - *host_targets - - package: cargo-matrix - target: - - *host_targets - - - package: mini-template + - package: + - zeroos-debug + - zeroos-macros target: - *host_targets + - *guest_targets - package: htif target: - *targets_none_elf_imac - *targets_linux_musl_gc - - package: zeroos-build - target: - - *host_targets - - - package: zeroos-debug - target: - - *host_targets - - *guest_targets - - - package: zeroos-macros - target: - - *host_targets - - *guest_targets - - package: zeroos-foundation target: - *guest_targets @@ -94,43 +84,25 @@ entries: features: - memory - - package: zeroos-runtime-musl + - package: + - zeroos-runtime-musl + - zeroos-runtime-gnu target: - *targets_linux_musl_gc - - package: zeroos-runtime-gnu - target: - - *targets_linux_musl_gc - - - package: zeroos-allocator-bump + - package: + - zeroos-allocator-bump + - zeroos-allocator-linked-list + - zeroos-allocator-buddy target: - *guest_targets - - package: zeroos-allocator-linked-list - target: - - *guest_targets - - - package: zeroos-allocator-buddy - target: - - *guest_targets - - - package: zeroos-vfs-core - target: - - *targets_linux_musl_gc - - - package: zeroos-device-console - target: - - *targets_linux_musl_gc - - - package: zeroos-device-null - target: - - *targets_linux_musl_gc - - - package: zeroos-device-urandom - target: - - *targets_linux_musl_gc - - - package: zeroos-device-zero + - package: + - zeroos-device-console + - zeroos-device-null + - zeroos-device-urandom + - zeroos-device-zero + - zeroos-vfs-core target: - *targets_linux_musl_gc @@ -216,7 +188,8 @@ entries: - thread - package: fibonacci - target: riscv64imac-unknown-none-elf + target: + - riscv64imac-unknown-none-elf commands: build: >- cargo spike build --package {package} --target "{target}" -- {features_flag} --quiet diff --git a/release-plz.toml b/release-plz.toml index 801e3d4..04973f7 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -8,47 +8,47 @@ release = true [[package]] name = "htif" -release = true +release = false [[package]] name = "zeroos" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-foundation" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-debug" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-macros" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-arch-riscv" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-scheduler-cooperative" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-os-linux" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-runtime-musl" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-runtime-gnu" @@ -58,54 +58,54 @@ release = false [[package]] name = "zeroos-runtime-nostd" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-build" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-allocator-bump" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-allocator-linked-list" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-allocator-buddy" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-vfs-core" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-device-console" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-device-null" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-device-zero" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-device-urandom" version_group = "zeroos" -release = true +release = false [[package]] name = "zeroos-rng" version_group = "zeroos" -release = true +release = false diff --git a/xtask/src/act.rs b/xtask/src/act.rs index 792c8d9..86cab2e 100644 --- a/xtask/src/act.rs +++ b/xtask/src/act.rs @@ -86,6 +86,33 @@ fn maybe_inject_remote_containers_ipc(cmd: &mut std::process::Command) { } } +fn home_local_bin_dirs() -> Option> { + let home = std::env::var_os("HOME")?; + let home = Path::new(&home); + Some(vec![home.join(".local/bin"), home.join("bin")]) +} + +fn append_dirs_to_path(cmd: &mut std::process::Command, dirs: &[PathBuf]) { + // Filter out missing dirs to keep PATH tidy (and avoid surprising entries). + let mut dirs: Vec = dirs.iter().filter(|p| p.is_dir()).cloned().collect(); + if dirs.is_empty() { + return; + } + + let existing = std::env::var_os("PATH").unwrap_or_default(); + let mut paths: Vec = std::env::split_paths(&existing).collect(); + + // Avoid duplicates: remove any existing occurrences of the dirs we’re about to append. + paths.retain(|p| !dirs.iter().any(|d| d == p)); + + // Append in the provided order. + paths.append(&mut dirs); + + if let Ok(joined) = std::env::join_paths(paths) { + cmd.env("PATH", joined); + } +} + pub fn run(args: ActArgs) -> Result<(), Box> { let workspace = crate::findup::workspace_root()?; @@ -96,6 +123,13 @@ pub fn run(args: ActArgs) -> Result<(), Box> { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); + // Make it easy to find locally installed CLIs (common in Dev Containers). + // We *append* these so we don't override anything already on PATH; they act as a fallback. + // Order matters: ~/.local/bin is typically preferred over ~/bin. + if let Some(extra_dirs) = home_local_bin_dirs() { + append_dirs_to_path(&mut cmd, &extra_dirs); + } + // In Dev Containers, Docker may be configured to use the dev-containers credential helper: // ~/.docker/config.json: { "credsStore": "dev-containers-" } // That helper expects REMOTE_CONTAINERS_IPC to be set; Cursor "attach" shells may not have it.