diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63a77a2..239250b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,6 +88,39 @@ jobs: - name: Integration test — coop uninstall run: ./tests/integration-uninstall.sh + nix: + name: Nix build (${{ matrix.system }}) + permissions: {} + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-24.04 + system: x86_64-linux + - os: macos-15 + system: aarch64-darwin + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install Nix + uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 + with: + enable_kvm: false + extra_nix_config: | + sandbox = true + + - name: Verify native Nix system + env: + EXPECTED_NIX_SYSTEM: ${{ matrix.system }} + run: test "$(nix eval --impure --raw --expr builtins.currentSystem)" = "$EXPECTED_NIX_SYSTEM" + + - name: Build and test Nix package + run: nix build .#coop --print-build-logs --no-link --no-update-lock-file + deny: runs-on: ubuntu-latest steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb8acb8..377c63c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -181,6 +181,10 @@ CI must pass before a pull request can merge. The - **`cargo fmt -- --check`** — formatting. - **`cargo clippy --workspace --all-targets --all-features -- -D warnings`** — lints. - **`cargo test --workspace`** — tests for both crates. +- **`nix build .#coop`** — sandboxed package builds, workspace unit tests, and + installation checks on Linux x86_64 (`ubuntu-24.04`) and Apple Silicon macOS + (`macos-15`), using the committed `flake.lock` and the package's + platform-specific test exclusions in `flake.nix`. - **`./tests/integration-install.sh`**, **`./tests/integration-update.sh`**, and **`./tests/integration-uninstall.sh`** — installer provenance, update, and uninstall flows. diff --git a/flake.nix b/flake.nix index f582512..7a7dc5f 100644 --- a/flake.nix +++ b/flake.nix @@ -67,6 +67,7 @@ pkgs.gitMinimal pkgs.gnused pkgs.openssh + pkgs.rsync ]; # Guest simulations need store tools and a usable login shell. # Use Bash's readonly BASHOPTS for the rejected-assignment fixture; @@ -88,7 +89,14 @@ substituteInPlace src/commands/lifecycle.rs \ --replace-fail '"/usr/bin/printenv PATH"' '"${pkgs.coreutils}/bin/printenv PATH"' \ --replace-fail '.envs(ssh.get_envs().map(|(name, value)| (name, value.unwrap())))' \ - '.env("SHELL", "${pkgs.bash}/bin/bash").envs(ssh.get_envs().map(|(name, value)| (name, value.unwrap())))' + '.env("SHELL", "${pkgs.bash}/bin/bash").envs(ssh.get_envs().map(|(name, value)| (name, value.unwrap())))' \ + --replace-fail '#!/bin/bash' '#!${pkgs.bash}/bin/bash' \ + --replace-fail 'exec /bin/cat --' 'exec ${pkgs.coreutils}/bin/cat --' + substituteInPlace src/creation_hooks.rs \ + --replace-fail 'SHELL=/bin/bash /bin/sh -c' \ + 'SHELL=${pkgs.bash}/bin/bash ${pkgs.bash}/bin/bash -c' \ + --replace-fail 'r#"#!/bin/bash' 'r#"#!${pkgs.bash}/bin/bash' \ + --replace-fail '/guest-bin:/usr/bin:/bin' '/guest-bin:${pkgs.bash}/bin:/usr/bin:/bin' substituteInPlace src/ssh.rs \ --replace-fail '/usr/bin/sed' '${pkgs.gnused}/bin/sed' \ --replace-fail 'SHELL=/bin/bash /bin/sh -c' \ @@ -96,6 +104,11 @@ # The Linux sandbox has no /bin/mkdir for the limactl shim. substituteInPlace src/lima.rs \ --replace-fail '/bin/mkdir' '${pkgs.coreutils}/bin/mkdir' + # Shutdown fixtures clear PATH to test a missing SSH client. + substituteInPlace src/vm.rs \ + --replace-fail '/bin/sleep' '${pkgs.coreutils}/bin/sleep' \ + --replace-fail '/bin/cat' '${pkgs.coreutils}/bin/cat' \ + --replace-fail '/bin/rm' '${pkgs.coreutils}/bin/rm' ''; # CMake builds aws-lc-sys through Cargo, not the top-level project. dontUseCmakeConfigure = true; @@ -119,6 +132,9 @@ # multicall coreutils. Its probes also require privileged sudo, # which is unavailable in the Nix build sandbox. "--skip=config::tests::is_running_true_for_live_firecracker_like_pid" + "--skip=config::tests::probe_liveness_recognizes_running_firecracker" + # This CLI test also invokes the privileged socket probe via sudo. + "--skip=stop_retains_proxy_for_full_socket_queue_and_cleans_after_close" # Nix's Linux syscall filter rejects setxattr with ENOTSUP, so # the ACL fixtures fail even on ACL-capable filesystems. diff --git a/src/guest_files.rs b/src/guest_files.rs index 7c6c924..474ca65 100644 --- a/src/guest_files.rs +++ b/src/guest_files.rs @@ -526,6 +526,7 @@ mod tests { #[cfg(target_os = "macos")] #[test] fn staging_removes_inherited_read_acls() { + // BSD ACL flags must work even when GNU coreutils is first on PATH. use std::process::Command; const MARKER: &str = "COOP_TEST_STAGING_ACL"; if std::env::var_os(MARKER).is_some() { @@ -537,7 +538,7 @@ mod tests { &[], ) .unwrap(); - let listing = Command::new("ls") + let listing = Command::new("/bin/ls") .arg("-lde") .arg(staged.directory.path()) .output() @@ -552,7 +553,7 @@ mod tests { } let root = tempfile::tempdir().unwrap(); assert!( - Command::new("chmod") + Command::new("/bin/chmod") .arg("+a") .arg("everyone allow read,search,file_inherit,directory_inherit") .arg(root.path())