this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add test runner, Nix verification, and build-trivial scripts

New scripts to bridge the gap between implementation and execution:

- scripts/run-tests.sh: Unified test runner that compiles and runs all
regression tests (syscall + sandbox) inside a Darling prefix. Supports
--suite filtering, --verbose, and --keep for debugging.

- scripts/verify-nix.sh: Standalone health-check for a Nix installation
inside Darling. Checks infrastructure, core commands, evaluator, store
integrity, syscall health, network (--online), and environment. Supports
--json for CI consumption.

- scripts/build-trivial.sh: Progressive derivation build tests for Phase
4.1 with 5 levels: echo to $out, multi-line builder, input transform,
derivation dependency, and binary substitution. Auto-skips later levels
on foundational failure; prints targeted debugging hints.

Updated PLAN.md and plan/README.md with new scripts, command examples in
the What's Next section, and a Script Quick Reference table.

+1726 -19
+99 -19
PLAN.md
··· 15 15 | Phase 0 — Packaging | ✅ Done | `flake.nix`, `nix/package.nix`, `nix/devShell.nix`, `nix/nixosModule.nix`, `.envrc` | 16 16 | Phase 1 — Syscalls | ✅ Core done, triage ongoing | [Triage table](./plan/syscall-triage.md) | 17 17 | Phase 2 — Sandbox | ✅ Done | `src/sandbox/sandbox.c` (fixed), `src/sandbox-exec/` (new), `tests/sandbox/` (new) | 18 - | Phase 3 — Nix Install | 🚧 In progress | `scripts/install-nix-in-darling.sh` (new), `scripts/darling-nix` (new) | 19 - | Phase 4 — Building | 📋 Planned | — | 18 + | Phase 3 — Nix Install | 🚧 In progress | `scripts/install-nix-in-darling.sh`, `scripts/darling-nix`, `scripts/verify-nix.sh` | 19 + | Phase 4 — Building | 🚧 Tooling ready | `scripts/build-trivial.sh` (new) | 20 20 | Phase 5 — Daemon | 📋 Planned | — | 21 21 | Phase 6 — CI | 🚧 In progress | `.github/workflows/nix.yml` (new) | 22 22 | Phase 7 — Remote Builder | 📋 Planned | — | ··· 24 24 25 25 ### Recently Completed 26 26 27 + - **Test runner**: Created `scripts/run-tests.sh` — unified test runner that 28 + copies all regression test sources into a Darling prefix, compiles C suites 29 + with the macOS toolchain, executes them (and shell-based suites), and 30 + produces a colour-coded per-suite summary. Supports `--suite` filtering, 31 + `--verbose`, and `--keep` for post-mortem debugging. 32 + - **Nix verification**: Created `scripts/verify-nix.sh` — standalone 33 + health-check for a Nix installation inside Darling. Checks infrastructure 34 + (prefix, binaries), core commands (`nix --version`, `nix-env`, `nix-store`), 35 + evaluator (`nix eval`, `builtins.currentSystem == x86_64-darwin`), store 36 + integrity (SQLite PRAGMA, path count), syscall health (no "Unimplemented 37 + syscall" or STUB warnings), optional network tests (`--online`), and 38 + environment (macOS version, nix.conf settings). Supports `--json` for CI. 39 + - **Build test tooling**: Created `scripts/build-trivial.sh` — progressive 40 + derivation build script for Phase 4.1. Five levels: (1) echo to `$out`, 41 + (2) multi-line builder with mkdir/chmod/loops, (3) input transformation 42 + via `builtins.toFile`, (4) derivation dependency chain, (5) binary 43 + substitution from `cache.nixos.org`. Each level prints targeted debugging 44 + hints on failure. 27 45 - **Phase 1.8**: Verified emulated macOS version — `SystemVersion.plist` 28 46 already reports 11.7.4 (Big Sur), `CMakeLists.txt` sets 29 47 `CMAKE_OSX_DEPLOYMENT_TARGET 11.0`. No changes needed — task complete. ··· 117 135 │ ├── devShell.nix # Developer shell (Phase 0) 118 136 │ └── nixosModule.nix # NixOS module (Phase 0) 119 137 ├── scripts/ 138 + │ ├── build-trivial.sh # NEW — Progressive derivation build tests (Phase 4) 139 + │ ├── darling-nix # Host-side Nix command wrapper (Phase 3) 120 140 │ ├── install-nix-in-darling.sh # Automated Nix installer (Phase 3) 121 - │ ├── darling-nix # Host-side Nix command wrapper (Phase 3) 122 - │ └── triage-syscalls.sh # NEW — Automated syscall triage (Phase 1) 141 + │ ├── run-tests.sh # NEW — Unified regression test runner 142 + │ ├── triage-syscalls.sh # Automated syscall triage (Phase 1) 143 + │ └── verify-nix.sh # NEW — Standalone Nix health-check (Phase 3) 123 144 ├── src/ 124 145 │ ├── sandbox/sandbox.c # Fixed sandbox API stubs (Phase 2) 125 146 │ ├── sandbox-exec/ # NEW — sandbox-exec stub (Phase 2) ··· 133 154 │ └── syscall/ # NEW — syscall regression tests 134 155 │ ├── test_renameatx_np.c # renameatx_np tests (Phase 1) 135 156 │ ├── test_setattrlist_flags.c # setattrlist ATTR_CMN_FLAGS tests (Phase 1) 136 - │ └── test_utimensat.c # NEW — utimensat/timestamp tests (Phase 1) 157 + │ └── test_utimensat.c # utimensat/timestamp tests (Phase 1) 137 158 └── plan/ 138 159 ├── README.md # Index + priority table 139 160 ├── 00-background.md # Motivation & current state ··· 148 169 ├── 09-phase7-remote-builder.md # Phase 7 details 149 170 ├── 10-phase8-stretch.md # Phase 8 details 150 171 ├── 11-architecture.md # Architecture & decisions 151 - └── syscall-triage.md # NEW — syscall tracking table 172 + └── syscall-triage.md # Syscall tracking table 152 173 ``` 153 174 154 175 ## What's Next 155 176 156 177 The **critical path to MVP** (Nix running inside Darling) is: 157 178 158 - 1. **Build & test**: All core Phase 1 syscall work is complete. The 159 - immediate next step is to build Darling with these changes and run 160 - the full regression test suite inside `darling shell`: 161 - - `tests/syscall/test_renameatx_np.c` — renameatx_np (5 tests) 162 - - `tests/syscall/test_setattrlist_flags.c` — setattrlist/getattrlist (10 tests) 163 - - `tests/syscall/test_utimensat.c` — utimensat/timestamps (16 tests) 164 - - `tests/sandbox/test_sandbox_api.c` — sandbox API stubs 165 - - `tests/sandbox/test_sandbox_exec.sh` — sandbox-exec integration 179 + 1. **Build & test**: All core Phase 1 syscall work is complete. Build 180 + Darling with these changes and run the full regression test suite: 181 + 182 + ```bash 183 + # One command runs everything: 184 + ./scripts/run-tests.sh 185 + 186 + # Or target individual suites: 187 + ./scripts/run-tests.sh --suite renameatx_np --verbose 188 + ./scripts/run-tests.sh --suite sandbox_exec --keep 189 + ``` 190 + 191 + Suites exercised: 192 + - `renameatx_np` — renameatx_np syscall 488 (5 tests) 193 + - `setattrlist_flags` — setattrlist/getattrlist ATTR_CMN_FLAGS (10 tests) 194 + - `utimensat` — utimensat/setattrlistat timestamps (16 tests) 195 + - `sandbox_api` — sandbox C API stubs 196 + - `sandbox_exec` — sandbox-exec integration (20 tests) 166 197 167 198 2. **Phase 1.7 — Live triage**: Run `scripts/triage-syscalls.sh` inside a 168 199 Darling prefix with Nix installed to discover any remaining unimplemented 169 200 syscalls that weren't caught during code audit. This will populate the 170 201 triage table with real-world findings. 171 202 203 + ```bash 204 + ./scripts/triage-syscalls.sh --output plan/syscall-triage-live.md 205 + ``` 206 + 172 207 3. **Phase 3 — Nix installation**: With all known syscall blockers resolved 173 208 (`lchflags`, `mv`/renameatx_np, `touch`/utimensat, `clonefile` stub, 174 - `getattrlist` buffer ordering fix), run 175 - `scripts/install-nix-in-darling.sh` and iterate on any remaining issues. 176 - This should now be very close to working end-to-end. 209 + `getattrlist` buffer ordering fix), install Nix and verify: 210 + 211 + ```bash 212 + # Install Nix inside a Darling prefix 213 + ./scripts/install-nix-in-darling.sh 214 + 215 + # Verify the installation is healthy 216 + ./scripts/verify-nix.sh 217 + ./scripts/verify-nix.sh --online # also test network/cache access 218 + 219 + # Quick ad-hoc commands via the wrapper 220 + ./scripts/darling-nix nix --version 221 + ./scripts/darling-nix nix eval --expr 'builtins.currentSystem' 222 + ``` 177 223 178 224 4. **Phase 4 — Derivation building**: Once Nix installs successfully, 179 - attempt a trivial derivation build to exercise `sandbox-exec`, `bash`, 180 - and the full Nix build pipeline inside Darling. 225 + exercise the full build pipeline with progressively harder derivations: 226 + 227 + ```bash 228 + # Run all five levels (stops early if a foundational level fails) 229 + ./scripts/build-trivial.sh 230 + 231 + # Target a specific level with debug output 232 + ./scripts/build-trivial.sh --level 1 --debug 233 + 234 + # Keep built derivations for inspection 235 + ./scripts/build-trivial.sh --keep --verbose 236 + ``` 237 + 238 + Levels: 239 + 1. Echo to `$out` — minimal: sandbox-exec → bash → file creation 240 + 2. Multi-line builder — mkdir, chmod, loops, multiple output files 241 + 3. Input transformation — `builtins.toFile`, sort, wc 242 + 4. Derivation dependency — one derivation consumes another's output 243 + 5. Binary substitution — fetch pre-built `hello` from cache.nixos.org 181 244 182 245 ### Completed Task Summary 183 246 ··· 193 256 | 1.8 | ✅ | macOS version — already 11.7.4 (Big Sur), no changes needed | 194 257 | 2.1 | ✅ | `sandbox-exec` stub | 195 258 | 2.2 | ✅ | `sandbox_init` API stubs fixed | 259 + | 3.1 | ✅ | `install-nix-in-darling.sh` installer script | 260 + | 3.3 | ✅ | `verify-nix.sh` standalone verification | 261 + | 3.4 | ✅ | `darling-nix` host-side wrapper | 262 + | 4.1 | ✅ | `build-trivial.sh` progressive build tests (tooling ready) | 263 + | 6.3 | ✅ | `.github/workflows/nix.yml` CI workflow | 264 + | — | ✅ | `run-tests.sh` unified test runner | 196 265 | — | ✅ | `getattrlist` attribute buffer ordering bug fixed | 197 266 | — | ✅ | `diskutil info`/`list` stubs | 267 + 268 + ### Script Quick Reference 269 + 270 + | Script | Purpose | When to Use | 271 + |--------|---------|-------------| 272 + | `scripts/run-tests.sh` | Compile & run all regression tests inside Darling | After building Darling with changes | 273 + | `scripts/triage-syscalls.sh` | Discover unimplemented syscalls during Nix operations | After installing Nix inside Darling | 274 + | `scripts/install-nix-in-darling.sh` | Install Nix package manager inside a Darling prefix | One-time setup | 275 + | `scripts/verify-nix.sh` | Health-check a Nix installation inside Darling | After install, or to diagnose regressions | 276 + | `scripts/darling-nix` | Run Nix commands inside Darling from the host | Day-to-day Nix usage | 277 + | `scripts/build-trivial.sh` | Test derivation building with 5 progressive levels | After Nix is installed and verified | 198 278 199 279 See [plan/README.md](./plan/README.md) for the full priority table and effort 200 280 estimates.
+7
plan/README.md
··· 54 54 55 55 | File | Description | 56 56 |---|---| 57 + | `scripts/run-tests.sh` | Unified test runner — compiles and runs all regression tests inside Darling | 57 58 | `scripts/install-nix-in-darling.sh` | Automated Nix installer for Darling prefixes | 59 + | `scripts/verify-nix.sh` | Standalone health-check for a Nix installation inside Darling | 60 + | `scripts/build-trivial.sh` | Progressive derivation build tests (5 levels) for Phase 4 | 58 61 | `scripts/darling-nix` | Host-side wrapper to run Nix commands inside Darling | 62 + | `scripts/triage-syscalls.sh` | Automated syscall triage — discovers unimplemented syscalls during Nix ops | 59 63 | `tests/sandbox/test_sandbox_api.c` | C-level regression tests for sandbox API stubs | 60 64 | `tests/sandbox/test_sandbox_exec.sh` | Shell-level tests for the `sandbox-exec` stub binary | 65 + | `tests/syscall/test_renameatx_np.c` | renameatx_np regression tests (plain rename, SWAP, EXCL, invalid flags) | 66 + | `tests/syscall/test_setattrlist_flags.c` | setattrlist/getattrlist ATTR_CMN_FLAGS tests | 67 + | `tests/syscall/test_utimensat.c` | utimensat/setattrlistat timestamp handling tests | 61 68 62 69 ## References 63 70
+687
scripts/build-trivial.sh
··· 1 + #!/usr/bin/env bash 2 + # build-trivial.sh — Attempt to build trivial Nix derivations inside Darling 3 + # 4 + # This script exercises Phase 4.1 of the plan: building progressively more 5 + # complex derivations inside a Darling prefix to validate that the full Nix 6 + # build pipeline works (posix_spawn → sandbox-exec → builder → store). 7 + # 8 + # Usage: 9 + # ./scripts/build-trivial.sh [OPTIONS] 10 + # 11 + # Options: 12 + # --prefix <path> Darling prefix path (default: ~/.darling or $DPREFIX) 13 + # --level <N> Run only derivation level N (1-5, default: all) 14 + # --keep Don't garbage-collect built derivations 15 + # --verbose Show full nix-build output 16 + # --debug Pass -vvvv --debug to nix-build (very verbose) 17 + # --help Show this help message 18 + # 19 + # Derivation Levels: 20 + # 1 — Echo to $out (no deps, /bin/bash only) 21 + # 2 — Multi-line builder script writing to $out 22 + # 3 — Derivation that reads and transforms input files 23 + # 4 — Derivation with a dependency on another derivation 24 + # 5 — Fetch a file from the binary cache (requires network) 25 + # 26 + # Exit codes: 27 + # 0 — all attempted levels passed 28 + # 1 — one or more levels failed 29 + # 2 — infrastructure error 30 + # 31 + # See: plan/06-phase4-building.md (Task 4.1) 32 + 33 + set -euo pipefail 34 + 35 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 36 + 37 + # ── Defaults ──────────────────────────────────────────────────────────────── 38 + 39 + DARLING_PREFIX="${DPREFIX:-$HOME/.darling}" 40 + SELECTED_LEVEL="" 41 + KEEP_BUILDS=0 42 + VERBOSE=0 43 + DEBUG=0 44 + 45 + # ── Colors ────────────────────────────────────────────────────────────────── 46 + 47 + if [ -t 1 ]; then 48 + RED='\033[0;31m' 49 + GREEN='\033[0;32m' 50 + YELLOW='\033[0;33m' 51 + BLUE='\033[0;34m' 52 + BOLD='\033[1m' 53 + DIM='\033[2m' 54 + RESET='\033[0m' 55 + else 56 + RED='' GREEN='' YELLOW='' BLUE='' BOLD='' DIM='' RESET='' 57 + fi 58 + 59 + # ── Helpers ───────────────────────────────────────────────────────────────── 60 + 61 + log() { echo -e "${GREEN}[build-trivial]${RESET} $*"; } 62 + warn() { echo -e "${YELLOW}[build-trivial] WARNING:${RESET} $*" >&2; } 63 + err() { echo -e "${RED}[build-trivial] ERROR:${RESET} $*" >&2; } 64 + fatal() { err "$@"; exit 2; } 65 + 66 + dsh() { 67 + darling shell "$@" 68 + } 69 + 70 + # Run a command inside Darling with Nix on PATH 71 + dsh_nix() { 72 + local nix_flags="" 73 + if [ "$DEBUG" -eq 1 ]; then 74 + nix_flags="-vvvv --debug" 75 + fi 76 + 77 + darling shell bash -lc " 78 + # Source Nix profile 79 + if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then 80 + . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' 81 + elif [ -e \"\\\$HOME/.nix-profile/etc/profile.d/nix.sh\" ]; then 82 + . \"\\\$HOME/.nix-profile/etc/profile.d/nix.sh\" 83 + elif [ -e '/etc/profile.d/nix-darling.sh' ]; then 84 + . '/etc/profile.d/nix-darling.sh' 85 + fi 86 + $* 87 + " 2>&1 88 + } 89 + 90 + usage() { 91 + cat <<'EOF' 92 + Usage: ./scripts/build-trivial.sh [OPTIONS] 93 + 94 + Build progressively more complex Nix derivations inside Darling to validate 95 + the full build pipeline. 96 + 97 + Options: 98 + --prefix <path> Darling prefix (default: ~/.darling or $DPREFIX) 99 + --level <N> Run only level N (1-5; default: all) 100 + --keep Don't garbage-collect built derivations 101 + --verbose Show full nix-build output 102 + --debug Pass -vvvv --debug to nix-build 103 + --help Show this help 104 + 105 + Levels: 106 + 1 Echo to $out — minimal: /bin/bash writes a one-liner to $out 107 + 2 Multi-line builder — bash script with variables, loops, file I/O 108 + 3 Input transformation — derivation that reads from an input file 109 + 4 Derivation dependency — one derivation depends on another 110 + 5 Binary substitution — fetch a pre-built path from cache.nixos.org 111 + 112 + Each level exercises more of the Nix + Darling stack. If a level fails, 113 + the script prints debugging hints specific to that level's failure mode. 114 + EOF 115 + exit 0 116 + } 117 + 118 + # ── Argument Parsing ──────────────────────────────────────────────────────── 119 + 120 + while [ $# -gt 0 ]; do 121 + case "$1" in 122 + --prefix) 123 + [ $# -ge 2 ] || fatal "--prefix requires an argument" 124 + DARLING_PREFIX="$2" 125 + shift 2 126 + ;; 127 + --level) 128 + [ $# -ge 2 ] || fatal "--level requires an argument" 129 + SELECTED_LEVEL="$2" 130 + if ! [[ "$SELECTED_LEVEL" =~ ^[1-5]$ ]]; then 131 + fatal "--level must be 1-5, got: $SELECTED_LEVEL" 132 + fi 133 + shift 2 134 + ;; 135 + --keep) 136 + KEEP_BUILDS=1 137 + shift 138 + ;; 139 + --verbose|-v) 140 + VERBOSE=1 141 + shift 142 + ;; 143 + --debug) 144 + DEBUG=1 145 + VERBOSE=1 146 + shift 147 + ;; 148 + --help|-h) 149 + usage 150 + ;; 151 + *) 152 + fatal "Unknown option: $1 (try --help)" 153 + ;; 154 + esac 155 + done 156 + 157 + # ── Preflight ─────────────────────────────────────────────────────────────── 158 + 159 + log "${BOLD}Preflight checks...${RESET}" 160 + 161 + if ! command -v darling &>/dev/null; then 162 + fatal "darling is not in PATH" 163 + fi 164 + 165 + if [ ! -d "$DARLING_PREFIX" ]; then 166 + fatal "Darling prefix not found at $DARLING_PREFIX" 167 + fi 168 + 169 + if ! dsh echo ok &>/dev/null; then 170 + fatal "darling shell is not functional" 171 + fi 172 + 173 + # Check Nix is installed 174 + nix_version="" 175 + nix_version=$(dsh_nix "nix --version" 2>&1) || true 176 + if [ -z "$nix_version" ] || ! echo "$nix_version" | grep -qi "nix"; then 177 + fatal "Nix does not appear to be installed in the Darling prefix.\n" \ 178 + " Run: ./scripts/install-nix-in-darling.sh" 179 + fi 180 + 181 + log " Prefix: $DARLING_PREFIX" 182 + log " Nix: $nix_version" 183 + 184 + # ── Level Definitions ─────────────────────────────────────────────────────── 185 + 186 + # Each level is a function that: 187 + # - Prints what it exercises 188 + # - Runs the build 189 + # - Returns 0 on success, 1 on failure 190 + 191 + level1_echo_to_out() { 192 + cat <<'DESC' 193 + Level 1: Echo to $out 194 + Exercises: posix_spawn, sandbox-exec stub, /bin/bash, file creation 195 + in /nix/store, store path registration, chmod/lchflags on 196 + store path. 197 + DESC 198 + 199 + local expr='derivation { 200 + name = "darling-test-1-echo"; 201 + builder = "/bin/bash"; 202 + args = [ "-c" "echo Hello from Darling > $out" ]; 203 + system = "x86_64-darwin"; 204 + }' 205 + 206 + local nix_flags="" 207 + if [ "$DEBUG" -eq 1 ]; then 208 + nix_flags="-vvvv" 209 + fi 210 + 211 + local output="" 212 + local exit_code=0 213 + output=$(dsh_nix "nix-build --no-out-link $nix_flags --expr '$expr' 2>&1") || exit_code=$? 214 + 215 + if [ "$VERBOSE" -eq 1 ]; then 216 + echo "$output" | sed 's/^/ /' 217 + fi 218 + 219 + if [ "$exit_code" -ne 0 ]; then 220 + echo "$output" | tail -20 | sed 's/^/ /' 221 + echo "" 222 + echo " Debugging hints for Level 1 failure:" 223 + echo " • 'clearing flags of path': lchflags still broken (Phase 1.1)" 224 + echo " • 'Bad file descriptor' / ENOEXEC: sandbox-exec stub issue (Phase 2)" 225 + echo " • 'sandbox profile' write fail: check /tmp is writable inside prefix" 226 + echo " • Builder hangs: posix_spawn with POSIX_SPAWN_SETEXEC broken" 227 + echo " • 'Unimplemented syscall': check plan/syscall-triage.md" 228 + echo "" 229 + echo " Manual reproduction:" 230 + echo " darling shell bash -lc 'nix-build -vvvv --no-out-link --expr \"$expr\"'" 231 + echo " darling shell /usr/bin/sandbox-exec -f /dev/null /bin/bash -c 'echo ok'" 232 + return 1 233 + fi 234 + 235 + # Verify the output exists and has the right content 236 + local store_path 237 + store_path=$(echo "$output" | grep '^/nix/store/' | tail -1) 238 + if [ -z "$store_path" ]; then 239 + echo " Build appeared to succeed but no store path in output." 240 + echo " Output: $output" 241 + return 1 242 + fi 243 + 244 + local content 245 + content=$(dsh_nix "cat '$store_path'" 2>&1) || true 246 + if echo "$content" | grep -q "Hello from Darling"; then 247 + echo " Store path: $store_path" 248 + echo " Content: $(echo "$content" | head -1)" 249 + return 0 250 + else 251 + echo " Store path content mismatch." 252 + echo " Expected: 'Hello from Darling'" 253 + echo " Got: '$content'" 254 + return 1 255 + fi 256 + } 257 + 258 + level2_multiline_builder() { 259 + cat <<'DESC' 260 + Level 2: Multi-line builder script 261 + Exercises: bash scripting, variables, loops, file I/O, mkdir, directory 262 + output (vs single file), multiple files in $out. 263 + DESC 264 + 265 + local expr='derivation { 266 + name = "darling-test-2-multiline"; 267 + builder = "/bin/bash"; 268 + args = [ "-c" " 269 + set -e 270 + mkdir -p $out/bin $out/share 271 + echo \"#!/bin/bash\" > $out/bin/hello 272 + echo \"echo Hello from Darling build\" >> $out/bin/hello 273 + chmod +x $out/bin/hello 274 + for i in 1 2 3; do 275 + echo \"Item $i\" > $out/share/item-$i.txt 276 + done 277 + echo done > $out/share/status.txt 278 + " ]; 279 + system = "x86_64-darwin"; 280 + }' 281 + 282 + local nix_flags="" 283 + if [ "$DEBUG" -eq 1 ]; then 284 + nix_flags="-vvvv" 285 + fi 286 + 287 + local output="" 288 + local exit_code=0 289 + output=$(dsh_nix "nix-build --no-out-link $nix_flags --expr '$expr' 2>&1") || exit_code=$? 290 + 291 + if [ "$VERBOSE" -eq 1 ]; then 292 + echo "$output" | sed 's/^/ /' 293 + fi 294 + 295 + if [ "$exit_code" -ne 0 ]; then 296 + echo "$output" | tail -20 | sed 's/^/ /' 297 + echo "" 298 + echo " Debugging hints for Level 2 failure:" 299 + echo " • mkdir/chmod failures: filesystem or syscall issue" 300 + echo " • 'for' loop issues: bash not fully working in sandbox" 301 + echo " • Same as Level 1 hints if Level 1 also failed" 302 + return 1 303 + fi 304 + 305 + local store_path 306 + store_path=$(echo "$output" | grep '^/nix/store/' | tail -1) 307 + if [ -z "$store_path" ]; then 308 + echo " No store path in output." 309 + return 1 310 + fi 311 + 312 + # Verify directory structure 313 + local verify_exit=0 314 + dsh_nix " 315 + test -x '$store_path/bin/hello' && 316 + test -f '$store_path/share/item-1.txt' && 317 + test -f '$store_path/share/item-2.txt' && 318 + test -f '$store_path/share/item-3.txt' && 319 + test -f '$store_path/share/status.txt' && 320 + grep -q 'done' '$store_path/share/status.txt' 321 + " >/dev/null 2>&1 || verify_exit=$? 322 + 323 + if [ "$verify_exit" -eq 0 ]; then 324 + echo " Store path: $store_path" 325 + echo " Verified: bin/hello (executable), share/item-{1,2,3}.txt, share/status.txt" 326 + return 0 327 + else 328 + echo " Output verification failed." 329 + echo " Store path: $store_path" 330 + dsh_nix "find '$store_path' -type f 2>/dev/null" | sed 's/^/ /' || true 331 + return 1 332 + fi 333 + } 334 + 335 + level3_input_transform() { 336 + cat <<'DESC' 337 + Level 3: Input transformation 338 + Exercises: builtins.toFile, passing string context to a derivation, 339 + reading input files, text processing (wc, sort). 340 + DESC 341 + 342 + local expr='let 343 + input = builtins.toFile "input.txt" "apple\nbanana\ncherry\ndate\nelderberry\n"; 344 + in derivation { 345 + name = "darling-test-3-transform"; 346 + builder = "/bin/bash"; 347 + args = [ "-c" " 348 + set -e 349 + mkdir -p $out 350 + cp ${input} $out/original.txt 351 + sort ${input} > $out/sorted.txt 352 + wc -l < ${input} | tr -d \" \" > $out/count.txt 353 + " ]; 354 + system = "x86_64-darwin"; 355 + inherit input; 356 + }' 357 + 358 + local nix_flags="" 359 + if [ "$DEBUG" -eq 1 ]; then 360 + nix_flags="-vvvv" 361 + fi 362 + 363 + local output="" 364 + local exit_code=0 365 + output=$(dsh_nix "nix-build --no-out-link $nix_flags --expr '$expr' 2>&1") || exit_code=$? 366 + 367 + if [ "$VERBOSE" -eq 1 ]; then 368 + echo "$output" | sed 's/^/ /' 369 + fi 370 + 371 + if [ "$exit_code" -ne 0 ]; then 372 + echo "$output" | tail -20 | sed 's/^/ /' 373 + echo "" 374 + echo " Debugging hints for Level 3 failure:" 375 + echo " • builtins.toFile failure: store write issue" 376 + echo " • sort/wc not found: PATH issue inside build sandbox" 377 + echo " • Input file not readable: store path access issue" 378 + return 1 379 + fi 380 + 381 + local store_path 382 + store_path=$(echo "$output" | grep '^/nix/store/' | tail -1) 383 + if [ -z "$store_path" ]; then 384 + echo " No store path in output." 385 + return 1 386 + fi 387 + 388 + local count 389 + count=$(dsh_nix "cat '$store_path/count.txt'" 2>&1 | tr -d '[:space:]') 390 + if [ "$count" = "5" ]; then 391 + echo " Store path: $store_path" 392 + echo " Line count: $count (correct)" 393 + return 0 394 + else 395 + echo " Count verification failed: expected '5', got '$count'" 396 + return 1 397 + fi 398 + } 399 + 400 + level4_derivation_dependency() { 401 + cat <<'DESC' 402 + Level 4: Derivation dependency 403 + Exercises: one derivation depending on another's output, Nix's dependency 404 + tracking, incremental builds, store path references. 405 + DESC 406 + 407 + local expr='let 408 + dep = derivation { 409 + name = "darling-test-4-dep"; 410 + builder = "/bin/bash"; 411 + args = [ "-c" "echo DEPENDENCY_OUTPUT > $out" ]; 412 + system = "x86_64-darwin"; 413 + }; 414 + in derivation { 415 + name = "darling-test-4-consumer"; 416 + builder = "/bin/bash"; 417 + args = [ "-c" " 418 + set -e 419 + mkdir -p $out 420 + content=\$(cat ${dep}) 421 + echo \"Read from dep: \$content\" > $out/result.txt 422 + echo ${dep} > $out/dep-path.txt 423 + " ]; 424 + system = "x86_64-darwin"; 425 + inherit dep; 426 + }' 427 + 428 + local nix_flags="" 429 + if [ "$DEBUG" -eq 1 ]; then 430 + nix_flags="-vvvv" 431 + fi 432 + 433 + local output="" 434 + local exit_code=0 435 + output=$(dsh_nix "nix-build --no-out-link $nix_flags --expr '$expr' 2>&1") || exit_code=$? 436 + 437 + if [ "$VERBOSE" -eq 1 ]; then 438 + echo "$output" | sed 's/^/ /' 439 + fi 440 + 441 + if [ "$exit_code" -ne 0 ]; then 442 + echo "$output" | tail -20 | sed 's/^/ /' 443 + echo "" 444 + echo " Debugging hints for Level 4 failure:" 445 + echo " • If dep itself failed: same as Level 1/2 hints" 446 + echo " • If consumer failed reading dep: store path access issue" 447 + echo " • Build order issue: Nix should build dep first automatically" 448 + return 1 449 + fi 450 + 451 + local store_path 452 + store_path=$(echo "$output" | grep '^/nix/store/' | tail -1) 453 + if [ -z "$store_path" ]; then 454 + echo " No store path in output." 455 + return 1 456 + fi 457 + 458 + local result 459 + result=$(dsh_nix "cat '$store_path/result.txt'" 2>&1) 460 + if echo "$result" | grep -q "DEPENDENCY_OUTPUT"; then 461 + echo " Store path: $store_path" 462 + echo " Result: $result" 463 + echo " Dep path: $(dsh_nix "cat '$store_path/dep-path.txt'" 2>&1 | head -1)" 464 + return 0 465 + else 466 + echo " Dependency content not found in result." 467 + echo " Expected to contain: 'DEPENDENCY_OUTPUT'" 468 + echo " Got: '$result'" 469 + return 1 470 + fi 471 + } 472 + 473 + level5_binary_substitution() { 474 + cat <<'DESC' 475 + Level 5: Binary substitution (requires network) 476 + Exercises: curl/TLS, binary cache access, NAR download, signature 477 + verification, store path registration from remote. 478 + DESC 479 + 480 + # Check if we can reach the cache first 481 + local cache_check="" 482 + cache_check=$(dsh_nix "curl -sfI https://cache.nixos.org/nix-cache-info 2>&1 | head -1") || true 483 + if ! echo "$cache_check" | grep -qi "200"; then 484 + echo " Cannot reach cache.nixos.org — skipping Level 5." 485 + echo " (This level requires network access.)" 486 + echo " cache check result: $cache_check" 487 + return 2 # special: skip, not fail 488 + fi 489 + 490 + # Try to realise a simple, very small store path from the cache. 491 + # We use `nix-store -r` on a known path, or `nix build` with --substituters. 492 + # The safest approach: evaluate a nixpkgs attr and fetch it. 493 + local output="" 494 + local exit_code=0 495 + output=$(dsh_nix " 496 + # Try to fetch a tiny package from the cache. 497 + # 'hello' is small and almost always cached. 498 + nix build --no-link --print-out-paths \\ 499 + --expr '(import <nixpkgs> {}).hello' \\ 500 + --substituters https://cache.nixos.org \\ 501 + --trusted-public-keys 'cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' \\ 502 + 2>&1 503 + ") || exit_code=$? 504 + 505 + if [ "$VERBOSE" -eq 1 ]; then 506 + echo "$output" | sed 's/^/ /' 507 + fi 508 + 509 + if [ "$exit_code" -ne 0 ]; then 510 + echo "$output" | tail -20 | sed 's/^/ /' 511 + echo "" 512 + echo " Debugging hints for Level 5 failure:" 513 + echo " • 'SSL' / 'TLS' error: Darling's certificate bundle may be outdated" 514 + echo " • 'cannot connect': DNS or network issue inside Darling" 515 + echo " • '<nixpkgs>' not found: channels not set up — run nix-channel --update" 516 + echo " • NAR download failure: curl or decompression issue" 517 + echo " • Signature mismatch: trusted-public-keys not configured" 518 + return 1 519 + fi 520 + 521 + local store_path 522 + store_path=$(echo "$output" | grep '^/nix/store/' | tail -1) 523 + if [ -z "$store_path" ]; then 524 + echo " Build output did not contain a store path." 525 + echo " Output: $(echo "$output" | tail -5)" 526 + return 1 527 + fi 528 + 529 + # Try running the fetched hello binary 530 + local hello_output 531 + hello_output=$(dsh_nix "'$store_path/bin/hello'" 2>&1) || true 532 + if echo "$hello_output" | grep -qi "hello"; then 533 + echo " Store path: $store_path" 534 + echo " Output: $hello_output" 535 + return 0 536 + else 537 + echo " Store path: $store_path" 538 + echo " hello binary did not produce expected output: $hello_output" 539 + echo " (This may be OK — the binary was fetched, which is the main test.)" 540 + return 0 541 + fi 542 + } 543 + 544 + # ── Determine which levels to run ────────────────────────────────────────── 545 + 546 + ALL_LEVELS=(1 2 3 4 5) 547 + 548 + if [ -n "$SELECTED_LEVEL" ]; then 549 + RUN_LEVELS=("$SELECTED_LEVEL") 550 + else 551 + RUN_LEVELS=("${ALL_LEVELS[@]}") 552 + fi 553 + 554 + # ── Execute ───────────────────────────────────────────────────────────────── 555 + 556 + echo "" 557 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 558 + log "${BOLD} Trivial Derivation Build Tests (Phase 4.1)${RESET}" 559 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 560 + echo "" 561 + 562 + LEVELS_PASSED=0 563 + LEVELS_FAILED=0 564 + LEVELS_SKIPPED=0 565 + 566 + declare -A LEVEL_RESULT # "pass", "fail", "skip" 567 + LEVEL_NAMES=( 568 + [1]="Echo to \$out" 569 + [2]="Multi-line builder" 570 + [3]="Input transformation" 571 + [4]="Derivation dependency" 572 + [5]="Binary substitution (network)" 573 + ) 574 + 575 + LEVEL_FUNCS=( 576 + [1]=level1_echo_to_out 577 + [2]=level2_multiline_builder 578 + [3]=level3_input_transform 579 + [4]=level4_derivation_dependency 580 + [5]=level5_binary_substitution 581 + ) 582 + 583 + for level in "${RUN_LEVELS[@]}"; do 584 + echo -e "${BLUE}━━━ Level $level: ${LEVEL_NAMES[$level]} ━━━${RESET}" 585 + echo "" 586 + 587 + exit_code=0 588 + ${LEVEL_FUNCS[$level]} || exit_code=$? 589 + 590 + echo "" 591 + 592 + if [ "$exit_code" -eq 0 ]; then 593 + LEVELS_PASSED=$((LEVELS_PASSED + 1)) 594 + LEVEL_RESULT[$level]="pass" 595 + echo -e " ${GREEN}✓ Level $level PASSED${RESET}" 596 + elif [ "$exit_code" -eq 2 ]; then 597 + LEVELS_SKIPPED=$((LEVELS_SKIPPED + 1)) 598 + LEVEL_RESULT[$level]="skip" 599 + echo -e " ${YELLOW}⊘ Level $level SKIPPED${RESET}" 600 + else 601 + LEVELS_FAILED=$((LEVELS_FAILED + 1)) 602 + LEVEL_RESULT[$level]="fail" 603 + echo -e " ${RED}✗ Level $level FAILED${RESET}" 604 + 605 + # If an early level fails, later levels will almost certainly fail too 606 + if [ "$level" -le 2 ] && [ -z "$SELECTED_LEVEL" ]; then 607 + warn "Level $level failed — skipping remaining levels (they depend on this)." 608 + for remaining in "${RUN_LEVELS[@]}"; do 609 + if [ "$remaining" -gt "$level" ] && [ -z "${LEVEL_RESULT[$remaining]+x}" ]; then 610 + LEVELS_SKIPPED=$((LEVELS_SKIPPED + 1)) 611 + LEVEL_RESULT[$remaining]="skip" 612 + fi 613 + done 614 + break 615 + fi 616 + fi 617 + 618 + echo "" 619 + done 620 + 621 + # ── Garbage Collection ────────────────────────────────────────────────────── 622 + 623 + if [ "$KEEP_BUILDS" -eq 0 ] && [ "$LEVELS_PASSED" -gt 0 ]; then 624 + log "Cleaning up test derivations..." 625 + dsh_nix "nix-collect-garbage 2>/dev/null" >/dev/null 2>&1 || true 626 + else 627 + if [ "$KEEP_BUILDS" -eq 1 ]; then 628 + log "Keeping built derivations (--keep)." 629 + fi 630 + fi 631 + 632 + # ── Summary ───────────────────────────────────────────────────────────────── 633 + 634 + TOTAL=$((LEVELS_PASSED + LEVELS_FAILED + LEVELS_SKIPPED)) 635 + 636 + echo "" 637 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 638 + log "${BOLD} Build Test Summary${RESET}" 639 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 640 + echo "" 641 + 642 + printf " %-5s %-35s %s\n" "Level" "Name" "Result" 643 + printf " %-5s %-35s %s\n" "─────" "───────────────────────────────────" "──────" 644 + 645 + for level in "${ALL_LEVELS[@]}"; do 646 + result="${LEVEL_RESULT[$level]:-skip}" 647 + case "$result" in 648 + pass) printf " %-5s %-35s ${GREEN}✓ PASS${RESET}\n" "$level" "${LEVEL_NAMES[$level]}" ;; 649 + fail) printf " %-5s %-35s ${RED}✗ FAIL${RESET}\n" "$level" "${LEVEL_NAMES[$level]}" ;; 650 + skip) printf " %-5s %-35s ${YELLOW}⊘ SKIP${RESET}\n" "$level" "${LEVEL_NAMES[$level]}" ;; 651 + esac 652 + done 653 + 654 + echo "" 655 + printf " Total: %d levels\n" "$TOTAL" 656 + printf " Passed: ${GREEN}%d${RESET}\n" "$LEVELS_PASSED" 657 + printf " Failed: ${RED}%d${RESET}\n" "$LEVELS_FAILED" 658 + printf " Skipped: ${YELLOW}%d${RESET}\n" "$LEVELS_SKIPPED" 659 + echo "" 660 + 661 + if [ "$LEVELS_FAILED" -gt 0 ]; then 662 + err "Some build levels failed." 663 + echo "" >&2 664 + echo "Next steps:" >&2 665 + echo " • Run a single level: $0 --level N --debug" >&2 666 + echo " • Manual build: darling shell bash -lc 'nix-build -vvvv --expr \"...\"'" >&2 667 + echo " • Manual sandbox test: darling shell /usr/bin/sandbox-exec -f /dev/null /bin/bash -c 'echo ok'" >&2 668 + echo " • Check syscalls: ./scripts/triage-syscalls.sh" >&2 669 + echo " • Host-side trace: strace -f -p \$(pidof darlingserver) 2>&1 | head -500" >&2 670 + echo " • Darling xtrace: DARLING_XTRACE=1 darling shell bash -lc 'nix-build --expr ...'" >&2 671 + echo "" >&2 672 + exit 1 673 + elif [ "$LEVELS_PASSED" -eq 0 ]; then 674 + warn "No levels were attempted — check that Nix is installed." 675 + exit 2 676 + else 677 + log "${GREEN}All build levels passed!${RESET}" 678 + if [ "$LEVELS_SKIPPED" -gt 0 ]; then 679 + log "${DIM}($LEVELS_SKIPPED levels skipped — re-run with --level N to target them)${RESET}" 680 + fi 681 + log "" 682 + log "Phase 4.1 is complete! Next steps:" 683 + log " • Phase 4.2: Test bash in build sandboxes (more complex scripts)" 684 + log " • Phase 4.5: Build a C program with Darwin stdenv" 685 + log " • See: plan/06-phase4-building.md" 686 + exit 0 687 + fi
+385
scripts/run-tests.sh
··· 1 + #!/usr/bin/env bash 2 + # run-tests.sh — Compile and run all regression tests inside a Darling prefix 3 + # 4 + # This script copies the test source files into the Darling prefix, compiles 5 + # them using Darling's macOS toolchain (cc), and executes them. It collects 6 + # results from all test suites and produces a summary. 7 + # 8 + # Usage: 9 + # ./scripts/run-tests.sh [OPTIONS] 10 + # 11 + # Options: 12 + # --prefix <path> Darling prefix path (default: ~/.darling or $DPREFIX) 13 + # --suite <name> Run only the named suite (can be repeated) 14 + # Available: renameatx_np, setattrlist_flags, utimensat, 15 + # sandbox_api, sandbox_exec 16 + # --keep Keep compiled test binaries in the prefix after running 17 + # --verbose Show full test output even on success 18 + # --help Show this help message 19 + # 20 + # Prerequisites: 21 + # - Darling must be installed and `darling shell echo ok` must work 22 + # - The Darling prefix must be initialized 23 + # 24 + # Exit code: 25 + # 0 — all tests passed 26 + # 1 — one or more tests failed 27 + # 2 — infrastructure error (Darling not working, compilation failure, etc.) 28 + # 29 + # See: plan/PLAN.md "What's Next" item 1 30 + 31 + set -euo pipefail 32 + 33 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 34 + REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" 35 + 36 + # ── Defaults ──────────────────────────────────────────────────────────────── 37 + 38 + DARLING_PREFIX="${DPREFIX:-$HOME/.darling}" 39 + KEEP_BINARIES=0 40 + VERBOSE=0 41 + SELECTED_SUITES=() 42 + 43 + # Test directory inside the Darling prefix 44 + DARLING_TEST_DIR="/tmp/darling-nix-tests" 45 + 46 + # ── Colors ────────────────────────────────────────────────────────────────── 47 + 48 + if [ -t 1 ]; then 49 + RED='\033[0;31m' 50 + GREEN='\033[0;32m' 51 + YELLOW='\033[0;33m' 52 + BLUE='\033[0;34m' 53 + BOLD='\033[1m' 54 + DIM='\033[2m' 55 + RESET='\033[0m' 56 + else 57 + RED='' GREEN='' YELLOW='' BLUE='' BOLD='' DIM='' RESET='' 58 + fi 59 + 60 + # ── Helpers ───────────────────────────────────────────────────────────────── 61 + 62 + log() { echo -e "${GREEN}[run-tests]${RESET} $*"; } 63 + warn() { echo -e "${YELLOW}[run-tests] WARNING:${RESET} $*" >&2; } 64 + err() { echo -e "${RED}[run-tests] ERROR:${RESET} $*" >&2; } 65 + fatal() { err "$@"; exit 2; } 66 + 67 + dsh() { 68 + darling shell "$@" 69 + } 70 + 71 + usage() { 72 + cat <<'EOF' 73 + Usage: ./scripts/run-tests.sh [OPTIONS] 74 + 75 + Compile and run all Darling regression tests. 76 + 77 + Options: 78 + --prefix <path> Darling prefix (default: ~/.darling or $DPREFIX) 79 + --suite <name> Run only the named suite (repeatable) 80 + Available: renameatx_np, setattrlist_flags, utimensat, 81 + sandbox_api, sandbox_exec 82 + --keep Keep compiled binaries in the prefix after running 83 + --verbose Show full test output even on success 84 + --help Show this help 85 + 86 + Suites: 87 + renameatx_np — renameatx_np syscall 488 (5 tests) 88 + setattrlist_flags — setattrlist/getattrlist ATTR_CMN_FLAGS (10 tests) 89 + utimensat — utimensat/setattrlistat timestamps (16 tests) 90 + sandbox_api — sandbox C API stubs 91 + sandbox_exec — sandbox-exec integration (shell tests) 92 + EOF 93 + exit 0 94 + } 95 + 96 + # ── Argument Parsing ──────────────────────────────────────────────────────── 97 + 98 + while [ $# -gt 0 ]; do 99 + case "$1" in 100 + --prefix) 101 + [ $# -ge 2 ] || fatal "--prefix requires an argument" 102 + DARLING_PREFIX="$2" 103 + shift 2 104 + ;; 105 + --suite) 106 + [ $# -ge 2 ] || fatal "--suite requires an argument" 107 + SELECTED_SUITES+=("$2") 108 + shift 2 109 + ;; 110 + --keep) 111 + KEEP_BINARIES=1 112 + shift 113 + ;; 114 + --verbose|-v) 115 + VERBOSE=1 116 + shift 117 + ;; 118 + --help|-h) 119 + usage 120 + ;; 121 + *) 122 + fatal "Unknown option: $1 (try --help)" 123 + ;; 124 + esac 125 + done 126 + 127 + # ── Suite Definitions ─────────────────────────────────────────────────────── 128 + 129 + # Each suite is defined as: 130 + # SUITE_<name>_TYPE = "c" | "sh" 131 + # SUITE_<name>_SOURCE = relative path from repo root 132 + # SUITE_<name>_DESC = human description 133 + # SUITE_<name>_CFLAGS = extra compiler flags (C suites only) 134 + 135 + declare -A SUITE_TYPE SUITE_SOURCE SUITE_DESC SUITE_CFLAGS 136 + 137 + SUITE_TYPE[renameatx_np]="c" 138 + SUITE_SOURCE[renameatx_np]="tests/syscall/test_renameatx_np.c" 139 + SUITE_DESC[renameatx_np]="renameatx_np (syscall 488) — plain rename, SWAP, EXCL, invalid flags" 140 + SUITE_CFLAGS[renameatx_np]="" 141 + 142 + SUITE_TYPE[setattrlist_flags]="c" 143 + SUITE_SOURCE[setattrlist_flags]="tests/syscall/test_setattrlist_flags.c" 144 + SUITE_DESC[setattrlist_flags]="setattrlist/getattrlist ATTR_CMN_FLAGS — lchflags, chflags, combined attrs" 145 + SUITE_CFLAGS[setattrlist_flags]="" 146 + 147 + SUITE_TYPE[utimensat]="c" 148 + SUITE_SOURCE[utimensat]="tests/syscall/test_utimensat.c" 149 + SUITE_DESC[utimensat]="utimensat/setattrlistat — timestamps, MODTIME, ACCTIME, CRTIME, symlinks" 150 + SUITE_CFLAGS[utimensat]="" 151 + 152 + SUITE_TYPE[sandbox_api]="c" 153 + SUITE_SOURCE[sandbox_api]="tests/sandbox/test_sandbox_api.c" 154 + SUITE_DESC[sandbox_api]="sandbox C API — sandbox_init, sandbox_free_error" 155 + SUITE_CFLAGS[sandbox_api]="" 156 + 157 + SUITE_TYPE[sandbox_exec]="sh" 158 + SUITE_SOURCE[sandbox_exec]="tests/sandbox/test_sandbox_exec.sh" 159 + SUITE_DESC[sandbox_exec]="sandbox-exec stub — flag parsing, exec, exit codes, Nix patterns" 160 + SUITE_CFLAGS[sandbox_exec]="" 161 + 162 + ALL_SUITES=(renameatx_np setattrlist_flags utimensat sandbox_api sandbox_exec) 163 + 164 + # ── Determine which suites to run ────────────────────────────────────────── 165 + 166 + if [ ${#SELECTED_SUITES[@]} -eq 0 ]; then 167 + RUN_SUITES=("${ALL_SUITES[@]}") 168 + else 169 + RUN_SUITES=() 170 + for s in "${SELECTED_SUITES[@]}"; do 171 + if [ -z "${SUITE_TYPE[$s]+x}" ]; then 172 + fatal "Unknown suite: $s (available: ${ALL_SUITES[*]})" 173 + fi 174 + RUN_SUITES+=("$s") 175 + done 176 + fi 177 + 178 + # ── Preflight ─────────────────────────────────────────────────────────────── 179 + 180 + log "${BOLD}Preflight checks...${RESET}" 181 + 182 + if ! command -v darling &>/dev/null; then 183 + fatal "darling is not installed or not in PATH" 184 + fi 185 + 186 + if [ ! -d "$DARLING_PREFIX" ]; then 187 + fatal "Darling prefix not found at $DARLING_PREFIX\n" \ 188 + " Initialize with: darling shell true" 189 + fi 190 + 191 + if ! dsh echo ok &>/dev/null; then 192 + fatal "darling shell is not functional\n" \ 193 + " Try: darling shell echo ok" 194 + fi 195 + 196 + log " Prefix: $DARLING_PREFIX" 197 + log " Suites: ${RUN_SUITES[*]}" 198 + 199 + # Verify source files exist 200 + for suite in "${RUN_SUITES[@]}"; do 201 + src="$REPO_DIR/${SUITE_SOURCE[$suite]}" 202 + if [ ! -f "$src" ]; then 203 + fatal "Source file not found: ${SUITE_SOURCE[$suite]}\n" \ 204 + " Expected at: $src" 205 + fi 206 + done 207 + 208 + # ── Copy test sources into the prefix ─────────────────────────────────────── 209 + 210 + log "${BOLD}Copying test sources into Darling prefix...${RESET}" 211 + 212 + PREFIX_TEST_DIR="$DARLING_PREFIX/private/tmp/darling-nix-tests" 213 + mkdir -p "$PREFIX_TEST_DIR" 214 + 215 + for suite in "${RUN_SUITES[@]}"; do 216 + src="$REPO_DIR/${SUITE_SOURCE[$suite]}" 217 + cp "$src" "$PREFIX_TEST_DIR/" 218 + log " Copied ${SUITE_SOURCE[$suite]}" 219 + done 220 + 221 + # ── Compile C test suites ────────────────────────────────────────────────── 222 + 223 + log "${BOLD}Compiling C test suites inside Darling...${RESET}" 224 + 225 + COMPILE_FAILURES=0 226 + 227 + for suite in "${RUN_SUITES[@]}"; do 228 + if [ "${SUITE_TYPE[$suite]}" != "c" ]; then 229 + continue 230 + fi 231 + 232 + src_basename="$(basename "${SUITE_SOURCE[$suite]}")" 233 + bin_name="${src_basename%.c}" 234 + 235 + log " Compiling $src_basename ..." 236 + 237 + compile_output=$(dsh bash -c \ 238 + "cd $DARLING_TEST_DIR && cc -Wall -Wextra -o '$bin_name' '$src_basename' ${SUITE_CFLAGS[$suite]} 2>&1" \ 239 + 2>&1) || { 240 + err " Compilation of $src_basename FAILED:" 241 + echo "$compile_output" | sed 's/^/ /' >&2 242 + COMPILE_FAILURES=$((COMPILE_FAILURES + 1)) 243 + continue 244 + } 245 + 246 + if [ "$VERBOSE" -eq 1 ] && [ -n "$compile_output" ]; then 247 + echo "$compile_output" | sed 's/^/ /' 248 + fi 249 + 250 + log " ${GREEN}✓${RESET} $bin_name compiled" 251 + done 252 + 253 + if [ "$COMPILE_FAILURES" -gt 0 ]; then 254 + err "$COMPILE_FAILURES suite(s) failed to compile" 255 + if [ "$COMPILE_FAILURES" -eq "${#RUN_SUITES[@]}" ]; then 256 + fatal "All suites failed to compile — is the Darling toolchain working?" 257 + fi 258 + fi 259 + 260 + # ── Run test suites ───────────────────────────────────────────────────────── 261 + 262 + echo "" 263 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 264 + log "${BOLD} Running Darling regression tests${RESET}" 265 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 266 + echo "" 267 + 268 + SUITES_PASSED=0 269 + SUITES_FAILED=0 270 + SUITES_SKIPPED=0 271 + 272 + declare -A SUITE_RESULTS # "pass", "fail", "skip" 273 + declare -A SUITE_OUTPUT 274 + 275 + for suite in "${RUN_SUITES[@]}"; do 276 + src_basename="$(basename "${SUITE_SOURCE[$suite]}")" 277 + echo -e "${BLUE}━━━ Suite: $suite ━━━${RESET}" 278 + echo -e "${DIM}${SUITE_DESC[$suite]}${RESET}" 279 + echo "" 280 + 281 + if [ "${SUITE_TYPE[$suite]}" = "c" ]; then 282 + bin_name="${src_basename%.c}" 283 + 284 + # Check the binary was compiled 285 + compile_check=$(dsh test -x "$DARLING_TEST_DIR/$bin_name" 2>&1 && echo "yes" || echo "no") 286 + if [ "$compile_check" != "yes" ]; then 287 + echo -e " ${YELLOW}SKIPPED${RESET} — compilation failed" 288 + echo "" 289 + SUITES_SKIPPED=$((SUITES_SKIPPED + 1)) 290 + SUITE_RESULTS[$suite]="skip" 291 + continue 292 + fi 293 + 294 + # Run the test binary 295 + output="" 296 + exit_code=0 297 + output=$(dsh bash -c "cd $DARLING_TEST_DIR && ./$bin_name 2>&1" 2>&1) || exit_code=$? 298 + 299 + elif [ "${SUITE_TYPE[$suite]}" = "sh" ]; then 300 + # Shell tests run directly 301 + output="" 302 + exit_code=0 303 + output=$(dsh bash -c "cd $DARLING_TEST_DIR && sh '$src_basename' 2>&1" 2>&1) || exit_code=$? 304 + fi 305 + 306 + SUITE_OUTPUT[$suite]="$output" 307 + 308 + if [ "$exit_code" -eq 0 ]; then 309 + SUITES_PASSED=$((SUITES_PASSED + 1)) 310 + SUITE_RESULTS[$suite]="pass" 311 + if [ "$VERBOSE" -eq 1 ]; then 312 + echo "$output" 313 + echo "" 314 + fi 315 + echo -e " ${GREEN}✓ PASSED${RESET}" 316 + else 317 + SUITES_FAILED=$((SUITES_FAILED + 1)) 318 + SUITE_RESULTS[$suite]="fail" 319 + # Always show output on failure 320 + echo "$output" 321 + echo "" 322 + echo -e " ${RED}✗ FAILED${RESET} (exit code: $exit_code)" 323 + fi 324 + 325 + echo "" 326 + done 327 + 328 + # ── Cleanup ───────────────────────────────────────────────────────────────── 329 + 330 + if [ "$KEEP_BINARIES" -eq 0 ]; then 331 + log "Cleaning up test files from prefix..." 332 + rm -rf "$PREFIX_TEST_DIR" 333 + else 334 + log "Test binaries preserved at: $DARLING_TEST_DIR (inside prefix)" 335 + log " Host path: $PREFIX_TEST_DIR" 336 + fi 337 + 338 + # ── Summary ───────────────────────────────────────────────────────────────── 339 + 340 + TOTAL=$((SUITES_PASSED + SUITES_FAILED + SUITES_SKIPPED)) 341 + 342 + echo "" 343 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 344 + log "${BOLD} Test Summary${RESET}" 345 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 346 + echo "" 347 + 348 + # Per-suite results table 349 + printf " %-25s %s\n" "Suite" "Result" 350 + printf " %-25s %s\n" "─────────────────────────" "──────" 351 + for suite in "${RUN_SUITES[@]}"; do 352 + result="${SUITE_RESULTS[$suite]:-skip}" 353 + case "$result" in 354 + pass) printf " %-25s ${GREEN}✓ PASS${RESET}\n" "$suite" ;; 355 + fail) printf " %-25s ${RED}✗ FAIL${RESET}\n" "$suite" ;; 356 + skip) printf " %-25s ${YELLOW}⊘ SKIP${RESET}\n" "$suite" ;; 357 + esac 358 + done 359 + 360 + echo "" 361 + printf " Total: %d suites\n" "$TOTAL" 362 + printf " Passed: ${GREEN}%d${RESET}\n" "$SUITES_PASSED" 363 + printf " Failed: ${RED}%d${RESET}\n" "$SUITES_FAILED" 364 + printf " Skipped: ${YELLOW}%d${RESET}\n" "$SUITES_SKIPPED" 365 + echo "" 366 + 367 + if [ "$SUITES_FAILED" -gt 0 ]; then 368 + err "Some test suites failed." 369 + echo "" 370 + echo "Debugging tips:" >&2 371 + echo " • Re-run with --verbose to see full output for passing tests" >&2 372 + echo " • Run a single suite: $0 --suite <name>" >&2 373 + echo " • Run with --keep to preserve binaries, then inspect inside:" >&2 374 + echo " darling shell $DARLING_TEST_DIR/<test_binary>" >&2 375 + echo " • Trace syscalls: strace -f -p \$(pidof darlingserver) 2>&1 | head" >&2 376 + echo " • Darling xtrace: DARLING_XTRACE=1 darling shell $DARLING_TEST_DIR/<test_binary>" >&2 377 + echo "" 378 + exit 1 379 + elif [ "$SUITES_SKIPPED" -gt 0 ] && [ "$SUITES_PASSED" -eq 0 ]; then 380 + warn "All suites were skipped — check compilation errors above." 381 + exit 2 382 + else 383 + log "${GREEN}All tests passed!${RESET}" 384 + exit 0 385 + fi
+548
scripts/verify-nix.sh
··· 1 + #!/usr/bin/env bash 2 + # verify-nix.sh — Standalone health-check for a Nix installation inside Darling 3 + # 4 + # This script runs a comprehensive set of checks to verify that Nix is 5 + # correctly installed and functional inside a Darling prefix. It can be 6 + # used after running install-nix-in-darling.sh, or at any later time to 7 + # diagnose regressions. 8 + # 9 + # Usage: 10 + # ./scripts/verify-nix.sh [OPTIONS] 11 + # 12 + # Options: 13 + # --prefix <path> Darling prefix path (default: ~/.darling or $DPREFIX) 14 + # --online Include checks that require network access (curl, cache) 15 + # --offline Skip all network-dependent checks (default) 16 + # --verbose Show command output even on success 17 + # --json Output results as JSON (for CI consumption) 18 + # --help Show this help message 19 + # 20 + # Exit codes: 21 + # 0 — all checks passed 22 + # 1 — one or more checks failed 23 + # 2 — infrastructure error (Darling not working, Nix not installed, etc.) 24 + # 25 + # See: plan/05-phase3-nix-install.md (Task 3.3 — Verify Core Nix Commands) 26 + 27 + set -euo pipefail 28 + 29 + # ── Defaults ──────────────────────────────────────────────────────────────── 30 + 31 + DARLING_PREFIX="${DPREFIX:-$HOME/.darling}" 32 + ONLINE=0 33 + VERBOSE=0 34 + JSON_OUTPUT=0 35 + 36 + # ── Colors ────────────────────────────────────────────────────────────────── 37 + 38 + if [ -t 1 ] && [ "$JSON_OUTPUT" -eq 0 ]; then 39 + RED='\033[0;31m' 40 + GREEN='\033[0;32m' 41 + YELLOW='\033[0;33m' 42 + BLUE='\033[0;34m' 43 + BOLD='\033[1m' 44 + DIM='\033[2m' 45 + RESET='\033[0m' 46 + else 47 + RED='' GREEN='' YELLOW='' BLUE='' BOLD='' DIM='' RESET='' 48 + fi 49 + 50 + # ── Helpers ───────────────────────────────────────────────────────────────── 51 + 52 + log() { [ "$JSON_OUTPUT" -eq 0 ] && echo -e "${GREEN}[verify-nix]${RESET} $*" || true; } 53 + warn() { [ "$JSON_OUTPUT" -eq 0 ] && echo -e "${YELLOW}[verify-nix] WARNING:${RESET} $*" >&2 || true; } 54 + err() { [ "$JSON_OUTPUT" -eq 0 ] && echo -e "${RED}[verify-nix] ERROR:${RESET} $*" >&2 || true; } 55 + fatal() { err "$@"; exit 2; } 56 + 57 + dsh() { 58 + darling shell "$@" 59 + } 60 + 61 + # Run a command inside Darling with Nix on PATH via a login shell 62 + dsh_nix() { 63 + darling shell bash -lc " 64 + # Source Nix profile 65 + if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then 66 + . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' 67 + elif [ -e \"\$HOME/.nix-profile/etc/profile.d/nix.sh\" ]; then 68 + . \"\$HOME/.nix-profile/etc/profile.d/nix.sh\" 69 + elif [ -e '/etc/profile.d/nix-darling.sh' ]; then 70 + . '/etc/profile.d/nix-darling.sh' 71 + fi 72 + $* 73 + " 2>&1 74 + } 75 + 76 + usage() { 77 + cat <<'EOF' 78 + Usage: ./scripts/verify-nix.sh [OPTIONS] 79 + 80 + Verify that Nix is correctly installed and functional inside a Darling prefix. 81 + 82 + Options: 83 + --prefix <path> Darling prefix (default: ~/.darling or $DPREFIX) 84 + --online Include network-dependent checks (curl, binary cache) 85 + --offline Skip network checks (default) 86 + --verbose Show command output even on success 87 + --json Output results as JSON 88 + --help Show this help 89 + 90 + Categories: 91 + Infrastructure Darling shell, prefix, Nix binaries present 92 + Core nix --version, nix-env --version, nix-store --verify 93 + Evaluator nix-instantiate --eval, nix eval, builtins.currentSystem 94 + Store Store database integrity, path registration 95 + Network curl to cache.nixos.org, HTTPS/TLS (--online only) 96 + Syscall Check for unimplemented syscall warnings 97 + 98 + EOF 99 + exit 0 100 + } 101 + 102 + # ── Argument Parsing ──────────────────────────────────────────────────────── 103 + 104 + while [ $# -gt 0 ]; do 105 + case "$1" in 106 + --prefix) 107 + [ $# -ge 2 ] || fatal "--prefix requires an argument" 108 + DARLING_PREFIX="$2" 109 + shift 2 110 + ;; 111 + --online) 112 + ONLINE=1 113 + shift 114 + ;; 115 + --offline) 116 + ONLINE=0 117 + shift 118 + ;; 119 + --verbose|-v) 120 + VERBOSE=1 121 + shift 122 + ;; 123 + --json) 124 + JSON_OUTPUT=1 125 + # Disable color when outputting JSON 126 + RED='' GREEN='' YELLOW='' BLUE='' BOLD='' DIM='' RESET='' 127 + shift 128 + ;; 129 + --help|-h) 130 + usage 131 + ;; 132 + *) 133 + fatal "Unknown option: $1 (try --help)" 134 + ;; 135 + esac 136 + done 137 + 138 + # ── Check Result Tracking ────────────────────────────────────────────────── 139 + 140 + TOTAL=0 141 + PASSED=0 142 + FAILED=0 143 + SKIPPED=0 144 + 145 + # For JSON output 146 + declare -a JSON_RESULTS=() 147 + 148 + # Record the result of a single check 149 + record_result() { 150 + local name="$1" 151 + local status="$2" # pass, fail, skip 152 + local detail="${3:-}" 153 + 154 + TOTAL=$((TOTAL + 1)) 155 + 156 + case "$status" in 157 + pass) 158 + PASSED=$((PASSED + 1)) 159 + if [ "$JSON_OUTPUT" -eq 0 ]; then 160 + printf " ${GREEN}✓${RESET} %-55s ${GREEN}PASS${RESET}\n" "$name" 161 + if [ "$VERBOSE" -eq 1 ] && [ -n "$detail" ]; then 162 + echo "$detail" | sed 's/^/ /' 163 + fi 164 + fi 165 + ;; 166 + fail) 167 + FAILED=$((FAILED + 1)) 168 + if [ "$JSON_OUTPUT" -eq 0 ]; then 169 + printf " ${RED}✗${RESET} %-55s ${RED}FAIL${RESET}\n" "$name" 170 + if [ -n "$detail" ]; then 171 + echo "$detail" | head -20 | sed 's/^/ /' 172 + fi 173 + fi 174 + ;; 175 + skip) 176 + SKIPPED=$((SKIPPED + 1)) 177 + if [ "$JSON_OUTPUT" -eq 0 ]; then 178 + printf " ${YELLOW}⊘${RESET} %-55s ${YELLOW}SKIP${RESET}\n" "$name" 179 + if [ -n "$detail" ]; then 180 + echo " $detail" 181 + fi 182 + fi 183 + ;; 184 + esac 185 + 186 + # Escape strings for JSON 187 + local json_detail 188 + json_detail=$(echo "$detail" | head -5 | tr '\n' ' ' | sed 's/"/\\"/g; s/\t/ /g') 189 + JSON_RESULTS+=("{\"name\":\"$name\",\"status\":\"$status\",\"detail\":\"$json_detail\"}") 190 + } 191 + 192 + # Run a check: check <name> <command...> 193 + # Runs the command, records pass/fail based on exit code. 194 + # Captures stdout+stderr for detail. 195 + check() { 196 + local name="$1" 197 + shift 198 + local output="" 199 + local exit_code=0 200 + 201 + output=$("$@" 2>&1) || exit_code=$? 202 + 203 + if [ "$exit_code" -eq 0 ]; then 204 + record_result "$name" "pass" "$output" 205 + else 206 + record_result "$name" "fail" "$output" 207 + fi 208 + } 209 + 210 + # Run a check inside Darling with Nix on PATH 211 + check_nix() { 212 + local name="$1" 213 + shift 214 + local cmd="$*" 215 + local output="" 216 + local exit_code=0 217 + 218 + output=$(dsh_nix "$cmd" 2>&1) || exit_code=$? 219 + 220 + # Also check for "Unimplemented syscall" in output — even if exit=0, 221 + # unimplemented syscall messages indicate problems. 222 + if echo "$output" | grep -qi "unimplemented syscall" 2>/dev/null; then 223 + local syscall_warns 224 + syscall_warns=$(echo "$output" | grep -i "unimplemented syscall" | head -5) 225 + record_result "$name" "fail" "Unimplemented syscall(s) detected:\n$syscall_warns" 226 + return 227 + fi 228 + 229 + if [ "$exit_code" -eq 0 ]; then 230 + record_result "$name" "pass" "$output" 231 + else 232 + record_result "$name" "fail" "$output" 233 + fi 234 + } 235 + 236 + # ── Section header ────────────────────────────────────────────────────────── 237 + 238 + section() { 239 + if [ "$JSON_OUTPUT" -eq 0 ]; then 240 + echo "" 241 + echo -e "${BLUE}━━━ $1 ━━━${RESET}" 242 + echo "" 243 + fi 244 + } 245 + 246 + # ── Preflight: Infrastructure Checks ─────────────────────────────────────── 247 + 248 + if [ "$JSON_OUTPUT" -eq 0 ]; then 249 + echo "" 250 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 251 + log "${BOLD} Nix-in-Darling Verification${RESET}" 252 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 253 + fi 254 + 255 + section "Infrastructure" 256 + 257 + # Check darling binary exists 258 + check "darling binary in PATH" command -v darling 259 + 260 + # Check prefix exists 261 + check "Darling prefix exists" test -d "$DARLING_PREFIX" 262 + 263 + # Check darling shell is functional 264 + check "darling shell functional" dsh echo ok 265 + 266 + # Check /nix exists in the prefix 267 + check "/nix exists in prefix" test -d "$DARLING_PREFIX/nix" 268 + 269 + # Check /nix/store exists 270 + check "/nix/store exists" test -d "$DARLING_PREFIX/nix/store" 271 + 272 + # Check /nix/var/nix/db exists 273 + check "/nix/var/nix/db exists" test -d "$DARLING_PREFIX/nix/var/nix/db" 274 + 275 + # Check nix.conf exists 276 + check "/etc/nix/nix.conf exists" test -f "$DARLING_PREFIX/etc/nix/nix.conf" 277 + 278 + # Check nix.conf has single-user setting 279 + if [ -f "$DARLING_PREFIX/etc/nix/nix.conf" ]; then 280 + check "nix.conf: build-users-group is empty" \ 281 + grep -q 'build-users-group =' "$DARLING_PREFIX/etc/nix/nix.conf" 282 + else 283 + record_result "nix.conf: build-users-group is empty" "skip" "nix.conf not found" 284 + fi 285 + 286 + # Check sandbox-exec stub 287 + check "sandbox-exec stub installed" \ 288 + test -f "$DARLING_PREFIX/usr/bin/sandbox-exec" -o -f "$DARLING_PREFIX/libexec/darling/usr/bin/sandbox-exec" 289 + 290 + # Check that nix binary exists somewhere in the store 291 + nix_binary_found=0 292 + if compgen -G "$DARLING_PREFIX/nix/store/*/bin/nix" >/dev/null 2>&1; then 293 + nix_binary_found=1 294 + fi 295 + if [ "$nix_binary_found" -eq 1 ]; then 296 + record_result "Nix binary found in /nix/store" "pass" "" 297 + else 298 + record_result "Nix binary found in /nix/store" "fail" "No /nix/store/*/bin/nix found" 299 + fi 300 + 301 + # ── Core: Nix binaries load and report version ───────────────────────────── 302 + 303 + section "Core" 304 + 305 + check_nix "nix --version" "nix --version" 306 + check_nix "nix-env --version" "nix-env --version" 307 + check_nix "nix-store --version" "nix-store --version" 308 + check_nix "nix-instantiate --version" "nix-instantiate --version" 309 + check_nix "nix-build --version" "nix-build --version" 310 + 311 + # ── Evaluator: Nix expression evaluation ─────────────────────────────────── 312 + 313 + section "Evaluator" 314 + 315 + check_nix "nix-instantiate --eval -E '1 + 1'" \ 316 + "nix-instantiate --eval -E '1 + 1'" 317 + 318 + check_nix "nix eval --expr '1 + 1'" \ 319 + "nix eval --expr '1 + 1'" 320 + 321 + check_nix "nix eval --expr '\"hello\"'" \ 322 + "nix eval --expr '\"hello\"'" 323 + 324 + # Check that builtins.currentSystem reports x86_64-darwin 325 + system_output="" 326 + system_exit=0 327 + system_output=$(dsh_nix "nix eval --expr 'builtins.currentSystem' --raw" 2>&1) || system_exit=$? 328 + 329 + if [ "$system_exit" -eq 0 ] && [ "$system_output" = "x86_64-darwin" ]; then 330 + record_result "builtins.currentSystem == x86_64-darwin" "pass" "$system_output" 331 + elif [ "$system_exit" -eq 0 ]; then 332 + record_result "builtins.currentSystem == x86_64-darwin" "fail" \ 333 + "Expected 'x86_64-darwin', got '$system_output'" 334 + else 335 + record_result "builtins.currentSystem == x86_64-darwin" "fail" "$system_output" 336 + fi 337 + 338 + # Slightly more complex evaluation — list operations, let bindings 339 + check_nix "nix eval: list operations" \ 340 + "nix eval --expr 'builtins.length [1 2 3]'" 341 + 342 + check_nix "nix eval: let binding" \ 343 + "nix eval --expr 'let x = 21; in x * 2'" 344 + 345 + check_nix "nix eval: string interpolation" \ 346 + "nix eval --expr 'let name = \"darling\"; in \"hello \${name}\"'" 347 + 348 + check_nix "nix eval: import <nixpkgs> (if channel set up)" \ 349 + "nix-instantiate --eval -E 'builtins.typeOf (import <nixpkgs> {})' 2>/dev/null || echo skip" 350 + 351 + # ── Store: Database and integrity ────────────────────────────────────────── 352 + 353 + section "Store" 354 + 355 + check_nix "nix-store --verify (basic)" \ 356 + "nix-store --verify 2>&1 || true" 357 + 358 + # Check that the SQLite database is accessible 359 + check_nix "nix-store --dump-db (database readable)" \ 360 + "nix-store --dump-db | head -1 >/dev/null 2>&1 && echo ok" 361 + 362 + # Count store paths 363 + store_count_output="" 364 + store_count_exit=0 365 + store_count_output=$(dsh_nix "ls /nix/store/ 2>/dev/null | wc -l" 2>&1) || store_count_exit=$? 366 + if [ "$store_count_exit" -eq 0 ]; then 367 + record_result "Store paths exist (count: $(echo "$store_count_output" | tr -d '[:space:]'))" "pass" "" 368 + else 369 + record_result "Store paths exist" "fail" "$store_count_output" 370 + fi 371 + 372 + # Check that the store database is not corrupt 373 + check_nix "SQLite database integrity" \ 374 + "sqlite3 /nix/var/nix/db/db.sqlite 'PRAGMA integrity_check;' 2>/dev/null || echo 'sqlite3 not available'" 375 + 376 + # ── Syscall: Check for warnings ──────────────────────────────────────────── 377 + 378 + section "Syscall Health" 379 + 380 + # Run a nix command and check that no "Unimplemented syscall" messages appear 381 + syscall_output="" 382 + syscall_exit=0 383 + syscall_output=$(dsh_nix "nix --version 2>&1; nix-instantiate --eval -E '1 + 1' 2>&1" 2>&1) || syscall_exit=$? 384 + 385 + if echo "$syscall_output" | grep -qi "unimplemented syscall" 2>/dev/null; then 386 + unimpl_lines=$(echo "$syscall_output" | grep -i "unimplemented syscall") 387 + record_result "No 'Unimplemented syscall' warnings" "fail" "$unimpl_lines" 388 + else 389 + record_result "No 'Unimplemented syscall' warnings" "pass" "" 390 + fi 391 + 392 + # Check for STUB warnings 393 + if echo "$syscall_output" | grep -qi "STUB" 2>/dev/null; then 394 + stub_lines=$(echo "$syscall_output" | grep -i "STUB" | head -10) 395 + record_result "No STUB warnings during basic operations" "fail" "$stub_lines" 396 + else 397 + record_result "No STUB warnings during basic operations" "pass" "" 398 + fi 399 + 400 + # Check for segfaults / signals in Nix operations 401 + segfault_output="" 402 + segfault_exit=0 403 + segfault_output=$(dsh_nix "nix eval --expr '1 + 1' 2>&1" 2>&1) || segfault_exit=$? 404 + 405 + if [ "$segfault_exit" -gt 128 ]; then 406 + signal=$((segfault_exit - 128)) 407 + record_result "No crashes (signals) during evaluation" "fail" "Killed by signal $signal (exit code $segfault_exit)" 408 + else 409 + record_result "No crashes (signals) during evaluation" "pass" "" 410 + fi 411 + 412 + # ── Network (optional) ───────────────────────────────────────────────────── 413 + 414 + section "Network" 415 + 416 + if [ "$ONLINE" -eq 1 ]; then 417 + check_nix "curl to cache.nixos.org" \ 418 + "curl -sfI https://cache.nixos.org/nix-cache-info >/dev/null 2>&1 && echo ok" 419 + 420 + check_nix "HTTPS/TLS handshake" \ 421 + "curl -sf https://cache.nixos.org/nix-cache-info | head -3" 422 + 423 + # Try to look up a well-known store path (bash) in the binary cache 424 + check_nix "Binary cache: nix path-info (bash)" \ 425 + "nix path-info --store https://cache.nixos.org /nix/store/\$(nix eval --raw nixpkgs#bash.outPath 2>/dev/null | sed 's|/nix/store/||') 2>&1 || echo 'path-info not available (channels may not be set up)'" 426 + 427 + # DNS resolution 428 + check_nix "DNS resolution" \ 429 + "nslookup cache.nixos.org >/dev/null 2>&1 || host cache.nixos.org >/dev/null 2>&1 || echo 'DNS tools not available, but curl worked'" 430 + else 431 + record_result "curl to cache.nixos.org" "skip" "Use --online to enable network checks" 432 + record_result "HTTPS/TLS handshake" "skip" "Use --online to enable network checks" 433 + fi 434 + 435 + # ── Environment ───────────────────────────────────────────────────────────── 436 + 437 + section "Environment" 438 + 439 + # Check macOS version reported by Darling 440 + version_output="" 441 + version_exit=0 442 + version_output=$(dsh sw_vers -productVersion 2>&1) || version_exit=$? 443 + if [ "$version_exit" -eq 0 ]; then 444 + major=$(echo "$version_output" | cut -d. -f1) 445 + if [ "$major" -ge 11 ] 2>/dev/null; then 446 + record_result "macOS version >= 11 (Big Sur): $version_output" "pass" "" 447 + else 448 + record_result "macOS version >= 11 (Big Sur): $version_output" "fail" \ 449 + "Nix Darwin binaries target macOS 11+; current version is $version_output" 450 + fi 451 + else 452 + record_result "macOS version >= 11 (Big Sur)" "fail" "$version_output" 453 + fi 454 + 455 + # Check that sandbox = false in nix.conf 456 + if [ -f "$DARLING_PREFIX/etc/nix/nix.conf" ]; then 457 + if grep -q 'sandbox = false' "$DARLING_PREFIX/etc/nix/nix.conf" 2>/dev/null; then 458 + record_result "nix.conf: sandbox = false" "pass" "" 459 + else 460 + record_result "nix.conf: sandbox = false" "fail" \ 461 + "Sandbox should be disabled in Darling; add 'sandbox = false' to /etc/nix/nix.conf" 462 + fi 463 + else 464 + record_result "nix.conf: sandbox = false" "skip" "nix.conf not found" 465 + fi 466 + 467 + # Check experimental features enabled 468 + if [ -f "$DARLING_PREFIX/etc/nix/nix.conf" ]; then 469 + if grep -q 'experimental-features.*nix-command' "$DARLING_PREFIX/etc/nix/nix.conf" 2>/dev/null; then 470 + record_result "nix.conf: nix-command flakes enabled" "pass" "" 471 + else 472 + record_result "nix.conf: nix-command flakes enabled" "skip" \ 473 + "experimental-features not set (optional but recommended)" 474 + fi 475 + else 476 + record_result "nix.conf: nix-command flakes enabled" "skip" "nix.conf not found" 477 + fi 478 + 479 + # ── JSON Output ───────────────────────────────────────────────────────────── 480 + 481 + if [ "$JSON_OUTPUT" -eq 1 ]; then 482 + echo "{" 483 + echo " \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"," 484 + echo " \"prefix\": \"$DARLING_PREFIX\"," 485 + echo " \"online\": $ONLINE," 486 + echo " \"total\": $TOTAL," 487 + echo " \"passed\": $PASSED," 488 + echo " \"failed\": $FAILED," 489 + echo " \"skipped\": $SKIPPED," 490 + echo " \"results\": [" 491 + 492 + local_first=1 493 + for result in "${JSON_RESULTS[@]}"; do 494 + if [ "$local_first" -eq 1 ]; then 495 + local_first=0 496 + else 497 + echo "," 498 + fi 499 + printf " %s" "$result" 500 + done 501 + echo "" 502 + echo " ]" 503 + echo "}" 504 + # Set exit code but don't print summary 505 + if [ "$FAILED" -gt 0 ]; then 506 + exit 1 507 + else 508 + exit 0 509 + fi 510 + fi 511 + 512 + # ── Summary ───────────────────────────────────────────────────────────────── 513 + 514 + echo "" 515 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 516 + log "${BOLD} Verification Summary${RESET}" 517 + log "${BOLD}═══════════════════════════════════════════════════════════${RESET}" 518 + echo "" 519 + printf " Total: %d checks\n" "$TOTAL" 520 + printf " Passed: ${GREEN}%d${RESET}\n" "$PASSED" 521 + printf " Failed: ${RED}%d${RESET}\n" "$FAILED" 522 + printf " Skipped: ${YELLOW}%d${RESET}\n" "$SKIPPED" 523 + echo "" 524 + 525 + if [ "$FAILED" -gt 0 ]; then 526 + err "Some checks failed." 527 + echo "" >&2 528 + echo "Troubleshooting:" >&2 529 + echo " • If Nix binaries aren't found, run: ./scripts/install-nix-in-darling.sh" >&2 530 + echo " • If syscall warnings appear, check: plan/syscall-triage.md" >&2 531 + echo " • If evaluator fails, try: darling shell bash -lc 'nix eval --expr 1+1' 2>&1" >&2 532 + echo " • For detailed tracing: DARLING_XTRACE=1 darling shell bash -lc 'nix --version'" >&2 533 + echo " • For host-side tracing: strace -f -p \$(pidof darlingserver) 2>&1 | head -500" >&2 534 + echo " • Re-run with --verbose for more detail" >&2 535 + echo " • Re-run with --online for network checks" >&2 536 + echo "" >&2 537 + exit 1 538 + elif [ "$PASSED" -eq 0 ]; then 539 + warn "No checks passed — Nix may not be installed." 540 + echo " Run: ./scripts/install-nix-in-darling.sh" >&2 541 + exit 2 542 + else 543 + log "${GREEN}All checks passed!${RESET} Nix is healthy inside Darling." 544 + if [ "$ONLINE" -eq 0 ]; then 545 + log "${DIM}(Run with --online to also verify network/cache access)${RESET}" 546 + fi 547 + exit 0 548 + fi