core: review fixes, init builds the agent, console without a terminal
Activation path (review T6R): a broken plugin or a stray file in /plugins blocks no activation; candidates replace their own entries; discovery has a 10 s deadline and its process is stopped after the answer; an unchanged hash keeps plugin.prev.wasm; activations run in their own task; the watcher is not recursive; no duplicate services; rpc.retired waits for live processes of its version; reads and hashes on blocking threads. Guest I/O and per-call paths: a stdin write error is EOF, stdout and stderr are drained before the status; stderr is lossy UTF-8 and goes to the journal from its own reader; compiled components are freed; shutdown starts no new processes; lock queues drop stopping processes; compile before the admission gate; CommandPre per component; core.unlock in order; null errors are successes; the journal thread survives SQLite errors. sausage init builds the agent with cargo.build; sausage without a command creates ./world. The console reads plain lines without a terminal. PROTOCOL.md and DECISIONS.md describe the decided rules. The flake tests the whole workspace and checks the packaged cargo.build.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
description = "Sausage: grow a system of WASI actors by talking to it";
|
||||
description = "Sausage: grow a system of WASI plugins by talking to it";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
@@ -16,12 +16,12 @@
|
||||
inherit system;
|
||||
overlays = [ rust-overlay.overlays.default ];
|
||||
};
|
||||
# The toolchain that builds the host and, at runtime, the actors.
|
||||
# The toolchain that builds the host and, at runtime, the plugins.
|
||||
rust = pkgs.rust-bin.stable.latest.default.override {
|
||||
targets = [ "wasm32-wasip2" ];
|
||||
extensions = [ "rust-src" ];
|
||||
};
|
||||
# The runtime cargo actor needs Cargo, rustc (with wasm32-wasip2), and
|
||||
# The native cargo plugin needs Cargo, rustc (with wasm32-wasip2), and
|
||||
# the native linker, even when the host itself is started with a bare
|
||||
# PATH. Keep this same runtime path in the packaged host and exercise
|
||||
# the installed binary below with PATH deliberately empty.
|
||||
@@ -41,7 +41,7 @@
|
||||
src = ./.;
|
||||
};
|
||||
# The test packages for `checks.tests`.
|
||||
testPackages = "-p sausage -p sausage-actor -p agent";
|
||||
testPackages = "--workspace";
|
||||
cargoArtifacts = craneLib.buildDepsOnly (common // {
|
||||
doCheck = false;
|
||||
# The dependencies of the host binary and of the tests.
|
||||
@@ -56,7 +56,7 @@
|
||||
cargoExtraArgs = "--locked -p sausage";
|
||||
doCheck = false;
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
# The `cargo` actor runs cargo at runtime. Build scripts need a C linker,
|
||||
# The native `cargo` plugin runs cargo at runtime. Build scripts need a C linker,
|
||||
# and C code for WASI needs the WASI clang.
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/sausage --prefix PATH : ${runtimePath} \
|
||||
@@ -67,9 +67,9 @@
|
||||
});
|
||||
# `nix run .#loop -- <world>` runs the newest `sausage run <world>`
|
||||
# (without a folder: bare `sausage`, i.e. ./world, created if needed)
|
||||
# from the public repo, again and again: after a crash or a broker
|
||||
# `exit` request it refreshes the flake and starts the host again,
|
||||
# so the world updates itself. `/quit` (exit code 0) ends the loop.
|
||||
# from the public repo, again and again: after a crash or a call of
|
||||
# `core.exit` it refreshes the flake and starts the host again, so
|
||||
# the world updates itself. `quit` (exit code 0) ends the loop.
|
||||
# SAUSAGE_FLAKE overrides where it fetches from (any flake URL,
|
||||
# e.g. a fork or a local checkout).
|
||||
repoUrl = "git+https://gitea.surma.technology/surma/sausage";
|
||||
@@ -89,9 +89,9 @@
|
||||
{
|
||||
packages.default = sausage;
|
||||
|
||||
# `nix flake check` runs every test of the host, the SDK, and the
|
||||
# agent. The tests build WASM actors with cargo, offline, from the
|
||||
# vendored crates of Cargo.lock.
|
||||
# `nix flake check` runs every test of the workspace. The tests build
|
||||
# WASM plugins with cargo, offline, from the vendored crates of
|
||||
# Cargo.lock.
|
||||
# The binary the host loop runs (`nix run`), built from the same
|
||||
# cached dependencies as the tests.
|
||||
checks.package = sausage;
|
||||
@@ -100,24 +100,22 @@
|
||||
cargoExtraArgs = "--locked ${testPackages}";
|
||||
# Nothing builds on the test run's target folder: don't store it.
|
||||
doInstallCargoArtifacts = false;
|
||||
preCheck = ''
|
||||
# Its own lock file pins crates that the vendored set may lack.
|
||||
rm -f cli/tests/actors/toolbox/Cargo.lock
|
||||
'';
|
||||
# The SDK tests run sample plugins with the wasmtime CLI.
|
||||
nativeBuildInputs = [ pkgs.wasmtime ];
|
||||
});
|
||||
# Exercise the installed host exactly as the loop launches it: the
|
||||
# caller has no Cargo on PATH, so only the packaged runtime toolchain
|
||||
# can run the native tests and the wasm32-wasip2 build.
|
||||
# can run `cargo.build`: the tests under WASI and the wasm32-wasip2
|
||||
# build, with C code from the packaged WASI clang.
|
||||
checks.runtime-toolchain = pkgs.runCommand "sausage-runtime-toolchain" { } ''
|
||||
set -eu
|
||||
root="$TMPDIR/runtime-toolchain"
|
||||
world="$root/world"
|
||||
emptyPath="$root/empty-bin"
|
||||
mkdir -p "$emptyPath" "$root/home" "$root/cargo-home"
|
||||
for actor in broker console cargo fs; do
|
||||
mkdir -p "$world/actor/$actor/public" "$world/actor/$actor/data"
|
||||
done
|
||||
source="$world/actor/console/public/probe"
|
||||
mkdir -p "$world/plugins" "$world/state" "$world/config"
|
||||
echo "[]" > "$world/config/boot.json"
|
||||
source="$world/plugins/probe/source"
|
||||
mkdir -p "$source/src" "$source/tests"
|
||||
cat > "$source/Cargo.toml" <<'TOML'
|
||||
[package]
|
||||
@@ -125,8 +123,7 @@
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
TOML
|
||||
# C code for WASI, compiled with the packaged WASI clang. The native
|
||||
# test build has no C, so it needs no C compiler for the host.
|
||||
# C code for WASI, compiled with the packaged WASI clang.
|
||||
printf 'int probe_add(int a, int b) { return a + b; }\n' > "$source/src/add.c"
|
||||
cat > "$source/build.rs" <<'RUST'
|
||||
use std::process::Command;
|
||||
@@ -156,7 +153,7 @@
|
||||
RUST
|
||||
cat > "$source/tests/probe.rs" <<'RUST'
|
||||
#[test]
|
||||
fn native_test_harness_runs() {
|
||||
fn test_runs_under_wasi() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
RUST
|
||||
@@ -169,29 +166,23 @@
|
||||
hostOutput=''${host[0]}
|
||||
hostPid=$host_PID
|
||||
trap 'kill "$hostPid" 2>/dev/null || true' EXIT
|
||||
printf '%s\n' '@cargo {"type":"build","source":"/actor/console/public/probe"}' >&"$hostInput"
|
||||
printf '%s\n' 'cargo.build {"plugin":"probe"}' >&"$hostInput"
|
||||
built=0
|
||||
while IFS= read -r -t 300 line <&"$hostOutput"; do
|
||||
while IFS= read -r -t 600 line <&"$hostOutput"; do
|
||||
echo "$line"
|
||||
case "$line" in
|
||||
*'"type":"build_failed"'*)
|
||||
echo "packaged cargo actor build failed with an empty caller PATH" >&2
|
||||
*'error:'*)
|
||||
echo "packaged cargo.build failed with an empty caller PATH" >&2
|
||||
exit 1
|
||||
;;
|
||||
*'"type":"built"'*)
|
||||
case "$line" in
|
||||
*'"passed":1'*) built=1 ;;
|
||||
*) echo "native test did not pass" >&2; exit 1 ;;
|
||||
esac
|
||||
break
|
||||
;;
|
||||
*'"passed": 1'*) built=1; break ;;
|
||||
esac
|
||||
done
|
||||
if [ "$built" -ne 1 ]; then
|
||||
echo "packaged host did not build the probe" >&2
|
||||
echo "packaged host did not build the probe, or its test did not pass" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' '/quit' >&"$hostInput"
|
||||
printf '%s\n' 'quit' >&"$hostInput"
|
||||
wait "$hostPid"
|
||||
mkdir -p "$out"
|
||||
touch "$out/passed"
|
||||
|
||||
Reference in New Issue
Block a user