feat: release Dever 0.1.5 with direct workers and long transactions

This commit is contained in:
Codex committed 2026-10-11 11:17:33 +00:00
1 parent 89629c2197
commit a900d7e8db
153 files changed
+3676 -3708

No files matched your search

+112 -23
View File
@@ -17,8 +17,8 @@ mod daemon;
mod inputs;
#[path = "../../dever-tests/tests/support/process.rs"]
mod process;
#[path = "../../dever-tests/tests/support/sandbox.rs"]
mod sandbox;
#[path = "../../dever-tests/tests/support/process_runtime.rs"]
mod process_runtime;
use super::temp::TemporaryDirectory;
#[path = "ecosystem_acceptance.rs"]
@@ -147,11 +147,15 @@ fn author_inputs(author: &Path, workspace: &Path) -> Ed25519KeyPair {
),
);
}
let compiler = input(author, Path::new(env!("CARGO_BIN_EXE_dever")), "dever");
let compiler = input(
author,
&workspace.join("target/native-release-inputs/bin/dever"),
"dever",
);
let core_libraries = inputs::core_libraries()
.into_iter()
.map(|(name, source)| {
let mut value = input(author, &source, name);
let mut value = input(author, &source, &name);
value["path"] = json!(name);
value
})
@@ -171,11 +175,11 @@ fn author_inputs(author: &Path, workspace: &Path) -> Ed25519KeyPair {
.map(|name| links[*name].clone())
.collect::<Vec<_>>()
};
let sandbox = sandbox::assets(workspace).into_iter().map(|asset| {
let process_runtime = process_runtime::assets(workspace).into_iter().map(|asset| {
let relative = format!("inputs/{}", asset.path);
write(author, &relative, &asset.bytes);
fs::set_permissions(author.join(&relative), fs::Permissions::from_mode(if asset.executable { 0o755 } else { 0o644 })).unwrap();
json!({"source":relative, "path":asset.path.strip_prefix("sandbox/").unwrap(), "sha256":sha256_file(&author.join(&relative)).unwrap()})
json!({"source":relative, "path":asset.path.strip_prefix("process-runtime/").unwrap(), "sha256":sha256_file(&author.join(&relative)).unwrap()})
}).collect::<Vec<_>>();
write(
author,
@@ -193,7 +197,7 @@ fn author_inputs(author: &Path, workspace: &Path) -> Ed25519KeyPair {
"skill":[skill],
"start":ordered(&["crt1.o","crti.o","crtbeginT.o"]),
"libraries":ordered(&["libc.a","libm.a","libmvec.a","libgcc.a","libgcc_eh.a"]),
"end":ordered(&["crtend.o","crtn.o"]), "sandbox":sandbox
"end":ordered(&["crtend.o","crtn.o"]), "process_runtime":process_runtime
}))
.unwrap(),
);
@@ -211,10 +215,11 @@ fn isolated_os_command(root: &Path) -> Command {
}
fn isolated_os_namespace(root: &Path) -> Command {
let assets = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../target/sandbox-inputs/assets")
.canonicalize()
.expect("prepare the explicit Linux isolation assets");
let isolator = PathBuf::from(
std::env::var_os("DEVER_TEST_ISOLATOR")
.expect("set an absolute test-only namespace isolator path"),
);
assert!(isolator.is_absolute());
let init = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../target/debug/examples/native-acceptance-init")
.canonicalize()
@@ -222,10 +227,7 @@ fn isolated_os_namespace(root: &Path) -> Command {
let bytes = fs::read(&init).unwrap();
let executable = goblin::elf::Elf::parse(&bytes).unwrap();
assert!(executable.interpreter.is_none() && executable.libraries.is_empty());
// A plain chroot cannot create a nested user namespace. Use a real mount
// namespace root so the program can retain its normal Worker sandbox.
dever_sandbox::validate_bwrap(&fs::read(assets.join("bin/bwrap")).unwrap()).unwrap();
let mut command = Command::new(assets.join("bin/bwrap"));
let mut command = Command::new(isolator);
command
.env_clear()
.args([
@@ -301,6 +303,33 @@ fn application(root: &Path, profile: &str) {
"exercise() (okay: Bool) { dever.task.sleep(1)\n okay = true }\n"
};
write(root, "module/sample/value/app.dever", app);
if matches!(profile, "sqlite" | "both") {
write(
root,
"module/sample/value/api.dever",
"cmd exercise = app.exercise\nlong cmd stages = app.stages\n",
);
write(
root,
"module/sample/value/app.dever",
format!(
"{app}\nstages() (okay: Bool) {{ id = store()\n dever.task.sleep(1)\n okay = finish(id) }}\ntransaction store() (id: model.id) {{ saved = model.create({{ name = \"stage\" }})\n id = saved.id }}\ntransaction finish(id: model.id) (okay: Bool) {{ count = model.count()\n removed = model.delete(id)\n okay = count == 1 and removed == 1 }}\n"
),
);
}
}
#[test]
fn release_profile_projects_have_valid_source_contracts() {
for profile in PROFILES {
let project = TemporaryDirectory::new();
application(project.path(), profile);
command(
Command::new(env!("CARGO_BIN_EXE_dever"))
.arg("check")
.arg(project.path()),
);
}
}
fn assert_response(output: Output) {
@@ -489,7 +518,7 @@ fn signed_core_builds_without_host_compiler_libraries_or_tools() {
#[test]
#[ignore = "requires four optimized archives and component-fixture; starts only an owned daemon, SQLite and exec Worker fixtures"]
fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon() {
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let workspace = inputs::workspace();
let profiles = archive_report(&workspace);
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
@@ -505,7 +534,7 @@ fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon()
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
command(
Command::new(workspace.join("target/debug/examples/native-release"))
Command::new(workspace.join("target/native-release-inputs/bin/native-release"))
.arg(author.path())
.arg("--output")
.arg(&release),
@@ -523,10 +552,13 @@ fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon()
fs::remove_dir_all(&release).unwrap();
drop(author);
let compiler = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let mut daemon = daemon::TestDaemon::start_with_executable(
&layout,
&workspace.join("target/native-release-inputs/bin/deverd"),
);
let projects = TemporaryDirectory::new();
let mut binaries = serde_json::Map::new();
let mut programs: Vec<(PathBuf, PathBuf)> = Vec::new();
let mut programs = Vec::new();
for profile in PROFILES {
let project = projects.path().join(profile);
application(&project, profile);
@@ -538,6 +570,15 @@ fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon()
"{}",
]),
));
if matches!(profile, "sqlite" | "both") {
assert_response(command(
Command::new(&compiler).arg("run").arg(&project).args([
"--",
"sample.value.stages",
"{}",
]),
));
}
let executable = project.join("program");
command(
Command::new(&compiler)
@@ -547,7 +588,7 @@ fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon()
.arg(&executable),
);
binaries.insert(profile.into(), json!({"bytes":fs::metadata(&executable).unwrap().len(),"sha256":sha256_file(&executable).unwrap()}));
programs.push((project, executable));
programs.push((profile, project, executable));
}
assert_eq!(cache_status(&layout).unwrap().entries, 4);
let exec_project = TemporaryDirectory::new();
@@ -595,19 +636,25 @@ fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon()
"managed_run": true, "standalone_run": true,
"source_project_removed": true, "response": 7
});
for (project, executable) in programs {
for (profile, project, executable) in programs {
fs::remove_dir_all(project.join("module")).unwrap();
assert_response(command(
Command::new(executable).args(["sample.value.exercise", "{}"]),
Command::new(&executable).args(["sample.value.exercise", "{}"]),
));
if matches!(profile, "sqlite" | "both") {
assert_response(command(
Command::new(executable).args(["sample.value.stages", "{}"]),
));
}
}
let report = json!({
"format":"dever-native-release-acceptance-v1", "platform":"linux-x86_64",
"version":version.as_str(), "profiles":profiles,"programs":binaries,"exec":exec,
"manifest_sha256":format!("{:x}", sha2::Sha256::digest(&manifest)),
"checks":{"pack_determinism":true,"signed_install":true,"managed_profiles":4,"standalone_profiles":4,"sqlite_crud":true,"signed_exec_worker":true},
"checks":{"pack_determinism":true,"signed_install":true,"managed_profiles":4,"standalone_profiles":4,"sqlite_crud":true,"long_staged_transactions":true,"signed_exec_worker":true},
"postgres_server":"not_run", "cross_host_source_reproducibility":"not_run"
});
retain_candidate(&layout, &version, "native", &report);
use sha2::Digest;
write(
&workspace,
@@ -615,3 +662,45 @@ fn optimized_profiles_make_reproducible_signed_release_and_run_through_daemon()
serde_json::to_vec_pretty(&report).unwrap(),
);
}
fn retain_candidate(layout: &Layout, version: &Version, name: &str, report: &Value) {
let Some(destination) = std::env::var_os("DEVER_NATIVE_ACCEPTANCE_OUTPUT") else {
return;
};
let destination = PathBuf::from(destination);
assert!(destination.is_absolute());
fs::create_dir_all(&destination).unwrap();
let destination = destination.join(name);
fs::create_dir(&destination).unwrap();
let payload = destination.join("release");
let resources = dever_cli::toolchain::SignedResources::load(layout, version).unwrap();
let manifest = resources.manifest();
for artifact in manifest.artifacts.iter().chain(
manifest
.extensions
.iter()
.flat_map(|extension| &extension.artifacts),
) {
let output = payload.join(&artifact.path);
fs::create_dir_all(output.parent().unwrap()).unwrap();
fs::copy(resources.path(&artifact.path).unwrap(), &output).unwrap();
assert_eq!(sha256_file(&output).unwrap(), artifact.sha256);
}
for file in ["manifest.json", "manifest.sig"] {
fs::copy(
layout.versions().join(version.as_str()).join(file),
payload.join(file),
)
.unwrap();
}
fs::copy(
layout.state().join("trusted-release-key"),
destination.join("test-release.pub"),
)
.unwrap();
fs::write(
destination.join("verification.json"),
serde_json::to_vec_pretty(report).unwrap(),
)
.unwrap();
}
@@ -64,7 +64,6 @@ fn bootstrap_payload_has_a_separate_signed_complete_loader_closure() {
"bootstrap/deverd",
"bootstrap/lib/libgcc_s.so.1",
"bootstrap/deverd.service",
"bootstrap/dever-sandbox",
] {
let artifact = manifest
.artifacts
@@ -78,9 +77,12 @@ fn bootstrap_payload_has_a_separate_signed_complete_loader_closure() {
assert!(unit.contains("RuntimeDirectory=dever"));
assert!(!unit.contains("Environment"));
assert!(!unit.contains("PATH"));
assert_eq!(
fs::read_to_string(release.join("bootstrap/dever-sandbox")).unwrap(),
dever_sandbox::APPARMOR_PROFILE
assert!(!release.join("bootstrap/dever-process").exists());
assert!(
!manifest
.artifacts
.iter()
.any(|artifact| artifact.path.contains("bwrap") || artifact.path.contains("apparmor"))
);
}
@@ -174,6 +176,73 @@ fn signed_installed_versions_keep_their_original_bootstrap_service_template() {
);
}
#[test]
fn signed_historical_bootstrap_profiles_are_read_only_and_version_bound() {
use dever_cli::toolchain::{Artifact, Layout, MachineManager, Version};
for release_version in ["0.1.4", "0.1.5"] {
let mut author = Author::new();
author.settings["version"] = json!(release_version);
add_bootstrap(&mut author);
author.save();
let machine = TemporaryDirectory::new();
let layout = Layout::new(machine.path().join("machine"));
layout.initialize().unwrap();
let version = Version::parse(release_version).unwrap();
let installed = layout.versions().join(release_version);
let mut manifest = packaging::create(&author.root, &installed).unwrap();
let profile = installed.join("bootstrap/dever-sandbox");
fs::write(&profile, b"historical profile fixture\n").unwrap();
manifest.artifacts.push(Artifact {
path: "bootstrap/dever-sandbox".into(),
bytes: fs::metadata(&profile).unwrap().len(),
sha256: sha256_file(&profile).unwrap(),
});
let encoded = serde_json::to_vec(&manifest).unwrap();
let key = Ed25519KeyPair::from_pkcs8(&fs::read(author.root.join("signing.pk8")).unwrap())
.unwrap();
fs::write(installed.join("manifest.json"), &encoded).unwrap();
fs::write(
installed.join("manifest.sig"),
key.sign(&encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
fs::write(
layout.state().join("trusted-release-key"),
author
.public_key
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
let manager = MachineManager::new(layout);
if release_version == "0.1.4" {
assert_eq!(
manager.resolve_core(&version).unwrap(),
installed.join("dever-core")
);
fs::write(profile, b"unsigned change").unwrap();
assert!(
manager
.resolve_core(&version)
.unwrap_err()
.contains("integrity verification")
);
} else {
assert!(
manager
.resolve_core(&version)
.unwrap_err()
.contains("unsupported artifact")
);
}
}
}
#[test]
#[ignore = "root-only installer trust rejection; never touches the actual machine installation"]
fn bootstrap_installer_rejects_untrusted_inputs_before_publication() {
@@ -313,7 +382,6 @@ fn bootstrap_recovery_is_idempotent_and_rejects_unrecorded_targets() {
for directory in [
"usr/local/bin",
"etc/systemd/system/multi-user.target.wants",
"etc/apparmor.d",
] {
fs::create_dir_all(root.join(directory)).unwrap();
}
@@ -331,20 +399,12 @@ fn bootstrap_recovery_is_idempotent_and_rejects_unrecorded_targets() {
let link_hash =
|target: &str| format!("{:x}", Sha256::digest(format!("link\0{target}").as_bytes()));
let journal = json!({"format":"dever-bootstrap-transaction-v1","phase":"prepared","service_was_active":null,"service_start_requested":false,"changes":[
{"target":"sandbox","before":null,"after":node_hash("directory",b"")},
{"target":"apparmor","before":null,"after":node_hash("file",b"new profile")},
{"target":"bin","before":node_hash("directory",b""),"after":node_hash("directory",b"")},
{"target":"service","before":null,"after":node_hash("file",b"new service")},
{"target":"entry","before":null,"after":link_hash("/opt/dever/bin/dever")},
{"target":"enable","before":null,"after":link_hash("../deverd.service")},
{"target":"active","before":null,"after":node_hash("file",b"new active")}
]});
fs::create_dir(root.join("opt/dever/.dever-bootstrap-sandbox.next")).unwrap();
fs::write(
root.join("etc/apparmor.d/.dever-bootstrap-dever-sandbox.next"),
b"new profile",
)
.unwrap();
let path = layout.state().join("bootstrap-install.json");
fs::write(&path, serde_json::to_vec(&journal).unwrap()).unwrap();
let mut invalid = journal.clone();
@@ -374,8 +434,6 @@ fn bootstrap_recovery_is_idempotent_and_rejects_unrecorded_targets() {
// undo the new active version, including after a failed cleanup retry.
fs::write(&service, b"new service").unwrap();
symlink("/opt/dever/bin/dever", &entry).unwrap();
fs::create_dir(root.join("opt/dever/sandbox")).unwrap();
fs::write(root.join("etc/apparmor.d/dever-sandbox"), b"new profile").unwrap();
symlink(
"../deverd.service",
root.join("etc/systemd/system/multi-user.target.wants/deverd.service"),
@@ -212,24 +212,13 @@ fn signed_bootstrap_installs_and_serves_two_projects_without_host_libraries() {
.arg(installer.path()),
);
let layout = Layout::new(image.path().join("opt/dever"));
let sandbox_artifact = manifest
.artifacts
.iter()
.find(|artifact| artifact.path == "sandbox/linux-x86_64/bin/bwrap")
.unwrap();
let sandbox_store = layout.root().join("sandbox");
let sandbox_helper = sandbox_store.join(&sandbox_artifact.sha256).join("bwrap");
assert_eq!(
sha256_file(&sandbox_helper).unwrap(),
sandbox_artifact.sha256
);
assert_eq!(
fs::metadata(&sandbox_helper).unwrap().mode() & 0o7777,
0o755
);
assert_eq!(
fs::read_to_string(image.path().join("etc/apparmor.d/dever-sandbox")).unwrap(),
dever_sandbox::APPARMOR_PROFILE
assert!(!image.path().join("etc/apparmor.d/dever-process").exists());
assert!(
!manifest
.artifacts
.iter()
.any(|artifact| artifact.path.ends_with("/bin/bwrap")
|| artifact.path.ends_with("/bin/guard"))
);
let launcher = layout.bin().join("dever");
let executable = layout.bin().join("deverd");
@@ -305,38 +294,6 @@ fn signed_bootstrap_installs_and_serves_two_projects_without_host_libraries() {
active
);
fs::write(&unit_path, old_unit).unwrap();
// Retain a different, correctly signed static helper identity through an
// upgrade. ELF trailing padding keeps this executable's actual behavior.
let mut previous_helper = fs::read(&sandbox_helper).unwrap();
previous_helper.push(0);
let previous_hash = dever_sandbox::validate_bwrap(&previous_helper).unwrap();
let previous_root = sandbox_store.join(&previous_hash);
fs::create_dir(&previous_root).unwrap();
fs::write(previous_root.join("bwrap"), &previous_helper).unwrap();
fs::set_permissions(
previous_root.join("bwrap"),
fs::Permissions::from_mode(0o755),
)
.unwrap();
let mut previous_manifest = manifest.clone();
let previous_artifact = previous_manifest
.artifacts
.iter_mut()
.find(|artifact| artifact.path == sandbox_artifact.path)
.unwrap();
previous_artifact.sha256 = previous_hash.clone();
previous_artifact.bytes = previous_helper.len() as u64;
let previous_encoded = serde_json::to_vec(&previous_manifest).unwrap();
fs::write(
previous_root.join("manifest.sig"),
key.sign(&previous_encoded)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>(),
)
.unwrap();
fs::write(previous_root.join("manifest.json"), previous_encoded).unwrap();
// Retry is supported, but a later damaged package cannot damage the
// published launcher or compiler selection.
command(
@@ -345,15 +302,6 @@ fn signed_bootstrap_installs_and_serves_two_projects_without_host_libraries() {
.arg(installer.path()),
);
assert_eq!(fs::read_to_string(&unit_path).unwrap(), current_unit);
assert_eq!(
sha256_file(&sandbox_helper).unwrap(),
sandbox_artifact.sha256
);
assert_eq!(
sha256_file(&previous_root.join("bwrap")).unwrap(),
previous_hash
);
assert_eq!(fs::read_dir(&sandbox_store).unwrap().count(), 2);
let before_bad_install = fs::read(layout.state().join("active-version")).unwrap();
fs::write(
release.join("bootstrap/lib/libgcc_s.so.1"),
@@ -98,23 +98,11 @@ fn packaged_project(root: &Path, source: &Path, layout: &Layout) {
}
#[test]
#[ignore = "requires explicit current runtime archive, author C/LLVM inputs and owned daemon/sandbox"]
#[ignore = "requires explicit current runtime archive, author C/LLVM inputs and owned daemon/process_runtime"]
fn signed_cli_runs_and_packages_ordinary_command_with_private_library() {
let workspace = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let workspace = inputs::workspace();
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
// This test validates the command boundary, not release-profile trimming.
// Use the explicitly rebuilt archive containing the current wire ABI.
let current = input(
author.path(),
&workspace.join("target/native-runtime-abi/debug/libdever_backend_bridge.a"),
"command-base.a",
);
let settings_path = author.path().join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&settings_path).unwrap()).unwrap();
settings["profiles"]["base"] = current;
fs::write(settings_path, serde_json::to_vec(&settings).unwrap()).unwrap();
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
@@ -123,7 +111,10 @@ fn signed_cli_runs_and_packages_ordinary_command_with_private_library() {
packaging::create(author.path(), &release).unwrap();
install_release(&layout, &version, &key);
let compiler = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start(&layout);
let mut daemon = daemon::TestDaemon::start_with_executable(
&layout,
&workspace.join("target/native-release-inputs/bin/deverd"),
);
let source = TemporaryDirectory::new();
project(source.path(), &workspace);
command(Command::new(&compiler).arg("check").arg(source.path()));
@@ -1,5 +1,5 @@
//! Explicit x86 compiler -> ARM application acceptance; QEMU user mode does
//! not establish the namespace/seccomp contract of external Workers.
//! not establish external Worker lifecycle behavior on a real target kernel.
use super::*;
use dever_cli::toolchain::BuildTarget;
use dever_cli::toolchain::compilation::{CompileKind, CompileRequest};
@@ -281,7 +281,7 @@ fn signed_host_compiler_builds_arm_profiles_and_runs_standalone_under_qemu() {
"format":"dever-cross-application-acceptance-v1", "host":"linux-x86_64", "target":"linux-aarch64",
"programs":evidence, "http":http, "qemu_sha256":sha256_file(&emulator).unwrap(),
"checks":{"signed_install":true,"profiles":4,"private_arm_build":true,"standalone_after_source_and_machine_removal":true,"sqlite_crud":true,"cache_entries":6,"cache_target_isolation":true,"tampered_target_cache_hit_rejected":true},
"postgres_server":"not_run", "worker_sandbox":"requires_arm_kernel_system_emulation"
"postgres_server":"not_run", "worker_execution":"requires_arm_kernel_system_emulation"
});
write(
&workspace,
@@ -15,8 +15,8 @@ fn digest(bytes: &[u8]) -> String {
format!("{:x}", Sha256::digest(bytes))
}
fn add_target_sandbox(author: &Path, workspace: &Path) {
let root = workspace.join("target/arm64-cross/sandbox");
fn add_target_process_runtime(author: &Path, workspace: &Path) {
let root = workspace.join("target/arm64-cross/process_runtime");
let mut files = Vec::new();
let mut inputs = Vec::new();
for directory in ["bin", "lib"] {
@@ -25,16 +25,16 @@ fn add_target_sandbox(author: &Path, workspace: &Path) {
assert!(entry.file_type().unwrap().is_file());
let relative = format!("{directory}/{}", entry.file_name().to_str().unwrap());
let bytes = fs::read(entry.path()).unwrap();
let source = format!("inputs/arm-sandbox/{relative}");
let source = format!("inputs/arm-process-runtime/{relative}");
write(author, &source, &bytes);
inputs.push(json!({"source":source,"path":relative,"sha256":digest(&bytes)}));
files.push((relative, bytes));
}
}
dever_sandbox::validate_assets_for_target(&files, TARGET.platform()).unwrap();
dever_process::validate_assets_for_target(&files, TARGET.platform()).unwrap();
let path = author.join("config/setting.json");
let mut settings: Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
settings["sandbox_targets"] = json!({"linux-aarch64":inputs});
settings["process_runtime_targets"] = json!({"linux-aarch64":inputs});
fs::write(path, serde_json::to_vec(&settings).unwrap()).unwrap();
}
@@ -267,7 +267,7 @@ fn prepare_locked_application(
}
#[test]
#[ignore = "requires explicit ARM runtimes/dependencies/sandbox and native archives; builds with an owned host daemon and exports apps for ARM system QEMU"]
#[ignore = "requires explicit ARM runtimes/dependencies/process_runtime and native archives; builds with an owned host daemon and exports apps for ARM system QEMU"]
fn signed_arm_ecosystems_build_offline_standalone_apps_for_system_guest() {
build_guest_apps(&ECOSYSTEMS, "ecosystem-build-acceptance.json");
}
@@ -294,7 +294,7 @@ fn build_guest_apps(ecosystems: &[&str], report: &str) {
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
add_arm_inputs(author.path(), &workspace);
add_target_sandbox(author.path(), &workspace);
add_target_process_runtime(author.path(), &workspace);
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
@@ -349,6 +349,6 @@ fn build_guest_apps(ecosystems: &[&str], report: &str) {
"checks":{"signed_runtime_install":true,"offline_lib_doctor":ecosystems.len(),"actual_cli_arm_build":ecosystems.len(),"source_and_build_machine_removed":true},
"guest_probe":{"arguments":["sample.worker.probe","{}"],"expected_data":true},
"guest_failure":{"arguments":["sample.worker.reject","{}"],"expected":"runtime fault; next fresh probe still succeeds; no remaining Worker"},
"guest_execution":"not_run", "sandbox_execution":"requires full ARM kernel guest"
"guest_execution":"not_run", "worker_execution":"requires ARM system guest verification"
})).unwrap());
}
@@ -204,6 +204,115 @@ fn ecosystem_inputs(author: &Path, workspace: &Path) {
fs::write(config, serde_json::to_vec(&settings).unwrap()).unwrap();
}
#[test]
#[ignore = "requires explicit native and zero-Lib ecosystem packs; uses an owned root installation and daemon"]
fn signed_zero_lib_ecosystems_run_and_build_in_the_current_container() {
let workspace = inputs::workspace();
let author = TemporaryDirectory::new();
let key = author_inputs(author.path(), &workspace);
ecosystem_inputs(author.path(), &workspace);
let machine = machine_temporary_directory(&workspace);
let layout = Layout::new(machine.path());
layout.initialize().unwrap();
let version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
let release = layout.downloads().join(version.as_str());
packaging::create(author.path(), &release).unwrap();
install_release(&layout, &version, &key);
fs::remove_dir_all(&release).unwrap();
drop(author);
let core = layout.versions().join(version.as_str()).join("dever-core");
let mut daemon = daemon::TestDaemon::start_with_executable(
&layout,
&workspace.join("target/native-release-inputs/bin/deverd"),
);
let deployments = TemporaryDirectory::new();
let mut programs = Vec::new();
for ecosystem in ECOSYSTEMS {
let project = TemporaryDirectory::new();
let (extension, source) = match ecosystem {
"pip" => (
"py",
"import gzip\nasync def probe(payload, setting):\n return {\"okay\": gzip.decompress(gzip.compress(b'dever')) == b'dever'}\n",
),
"npm" => (
"mjs",
"import {createHash} from 'node:crypto';\nexport async function probe() { return {okay:createHash('sha256').update('dever').digest().length === 32}; }\n",
),
"go" => ("go", include_str!("../../ecosystem-release/probe.go")),
_ => unreachable!(),
};
write(project.path(), "config/setting.json", "{}");
write(
project.path(),
"module/sample/worker/port.dever",
"probe() (okay: Bool) fails app.ProbeResult\n",
);
write(
project.path(),
"module/sample/worker/app.dever",
"type ProbeResult { error Unavailable(message: Text) }\nprobe() (okay: Bool) { okay = port.probe() }\n",
);
write(
project.path(),
"module/sample/worker/api.dever",
"cmd probe = app.probe\n",
);
write(
project.path(),
"module/sample/worker/adapter.dever",
format!("external {ecosystem} \"worker/probe.{extension}\" {{}}\n"),
);
write(
project.path(),
&format!("module/sample/worker/worker/probe.{extension}"),
source,
);
command(Command::new(&core).arg("check").arg(project.path()));
command(Command::new(&core).args(["lib", "add"]).arg(project.path()));
assert_response(command(
Command::new(&core).arg("run").arg(project.path()).args([
"--",
"sample.worker.probe",
"{}",
]),
));
let destination = deployments.path().join(ecosystem);
write(&destination, "config/setting.json", "{}");
let program = destination.join("program");
command(
Command::new(&core)
.arg("build")
.arg(project.path())
.arg("--output")
.arg(&program),
);
programs.push((ecosystem, destination, program));
}
daemon.stop();
for (ecosystem, destination, program) in programs {
assert_response(command(
Command::new(program)
.current_dir(destination)
.args(["sample.worker.probe", "{}"]),
));
eprintln!(
"{ecosystem}: signed runtime, managed execution and source-free standalone execution passed"
);
}
retain_candidate(
&layout,
&version,
"ecosystems",
&json!({
"format":"dever-zero-lib-release-acceptance-v1",
"platform":"linux-x86_64", "version":version.as_str(),
"checks":{"signed_install":true,"managed_ecosystems":3,"standalone_ecosystems":3,"source_projects_removed":true},
"third_party_packages":"not_run", "host_language_absence":"not_verified",
"gpu":"not_run", "public_release":"not_published"
}),
);
}
fn application(root: &Path, ecosystem: &str) {
let (extension, source) = match ecosystem {
"pip" => ("py", include_str!("../../ecosystem-release/probe.py")),
@@ -389,10 +498,10 @@ fn signed_ecosystems_build_and_run_without_system_language_installations() {
nonroot_standalone(&isolated.path().join(ecosystem), &programs[ecosystem]),
);
}
write(&workspace, "target/closure-sandbox-ecosystem-release-acceptance.json", serde_json::to_vec_pretty(&json!({
write(&workspace, "target/closure-process_runtime-ecosystem-release-acceptance.json", serde_json::to_vec_pretty(&json!({
"format":"dever-ecosystem-release-acceptance-v1", "platform":"linux-x86_64",
"runtimes":runtimes,"programs":programs,"standalone_nonroot":standalone_nonroot,
"checks":{"signed_install":true,"locked_runtimes":3,"managed_check_run_build":3,"standalone_isolated_root":3,"standalone_nonroot":3,"shared_compile_entries":3,"python_stdlib_paths_in_bundle":true,"os_sandbox":true,"offline_build_receipt_replay":true},
"checks":{"signed_install":true,"locked_runtimes":3,"managed_check_run_build":3,"standalone_isolated_root":3,"standalone_nonroot":3,"shared_compile_entries":3,"python_stdlib_paths_in_bundle":true,"direct_workers":true,"offline_build_receipt_replay":true},
"os_baseline":"explicit Linux GNU OS libraries and named device nodes inside an owned root",
"runtime_pack_provenance":"test author signed the exact pip/npm packs bound to the completed source builds; maker determinism is verified separately",
"third_party_packages":{"pip":"simplejson@3.20.1 _speedups C extension","npm":"bufferutil@4.0.9 build/Release addon"}, "other_platforms":"not_run", "public_release":"not_published"
@@ -298,10 +298,8 @@ fn go_cross_pack_binds_host_tools_and_arm_standard_library_separately() {
#[test]
#[cfg(target_arch = "x86_64")]
fn sandbox_cross_assets_preserve_loader_mode_and_validate_deployment_target() {
fn process_runtime_cross_assets_preserve_loader_mode_and_validate_deployment_target() {
let mut files = vec![
("bin/bwrap".into(), native_elf::static_executable()),
("bin/guard".into(), native_elf::static_executable()),
(
"lib/ld-linux-aarch64.so.1".into(),
native_elf::library("ld-linux-aarch64.so.1", &[]),
@@ -314,12 +312,12 @@ fn sandbox_cross_assets_preserve_loader_mode_and_validate_deployment_target() {
for (_, bytes) in &mut files {
bytes[18..20].copy_from_slice(&183u16.to_le_bytes());
}
dever_sandbox::validate_assets_for_target(&files, "linux-aarch64").unwrap();
dever_process::validate_assets_for_target(&files, "linux-aarch64").unwrap();
let mut author = super::targets::cross_author();
let inputs = files
.iter()
.map(|(path, bytes)| {
let source = format!("inputs/arm-sandbox/{path}");
let source = format!("inputs/arm-process-runtime/{path}");
let file = author.root.join(&source);
fs::create_dir_all(file.parent().unwrap()).unwrap();
fs::write(&file, bytes).unwrap();
@@ -327,18 +325,16 @@ fn sandbox_cross_assets_preserve_loader_mode_and_validate_deployment_target() {
json!({"source":source,"path":path,"sha256":sha256_file(&file).unwrap()})
})
.collect::<Vec<_>>();
author.settings["sandbox_targets"] = json!({"linux-aarch64":inputs});
author.settings["process_runtime_targets"] = json!({"linux-aarch64":inputs});
author.save();
let output = author.output("cross-sandbox");
let output = author.output("cross-process_runtime");
packaging::create(&author.root, &output).unwrap();
for (path, mode) in [
("bin/bwrap", 0o755),
("bin/guard", 0o755),
("lib/ld-linux-aarch64.so.1", 0o755),
("lib/libc.so.6", 0o644),
] {
assert_eq!(
fs::metadata(output.join("sandbox/linux-aarch64").join(path))
fs::metadata(output.join("process-runtime/linux-aarch64").join(path))
.unwrap()
.permissions()
.mode()
@@ -349,7 +345,7 @@ fn sandbox_cross_assets_preserve_loader_mode_and_validate_deployment_target() {
}
files[1].1[18..20].copy_from_slice(&62u16.to_le_bytes());
assert!(
dever_sandbox::validate_assets_for_target(&files, "linux-aarch64")
dever_process::validate_assets_for_target(&files, "linux-aarch64")
.unwrap_err()
.contains("incompatible target")
);
@@ -2,6 +2,45 @@ use super::*;
use dever_cli::toolchain::runtime_pack::RuntimePack;
use dever_runtime::config::RuntimeProfile;
#[test]
#[cfg(target_arch = "x86_64")]
fn target_resource_namespaces_are_bound_to_the_signed_release_version() {
use dever_cli::toolchain::{
Artifact, Extension, ExtensionKind, ReleaseManifest, Version, validate_catalog,
};
let mut manifest = ReleaseManifest::new(
Version::parse("0.1.4").unwrap(),
vec![Artifact {
path: "dever-core".into(),
bytes: 1,
sha256: "0".repeat(64),
}],
);
manifest.extensions.push(Extension {
kind: ExtensionKind::Target,
target: BuildTarget::LinuxAarch64,
artifacts: vec![Artifact {
path: "sandbox/linux-aarch64/lib/libc.so.6".into(),
bytes: 1,
sha256: "0".repeat(64),
}],
});
validate_catalog(&manifest).unwrap();
manifest.version = Version::parse("0.1.5").unwrap();
assert!(
validate_catalog(&manifest)
.unwrap_err()
.contains("resource namespace")
);
manifest.extensions[0].artifacts[0].path = "process-runtime/linux-aarch64/lib/libc.so.6".into();
validate_catalog(&manifest).unwrap();
manifest.version = Version::parse("0.1.4").unwrap();
assert!(
validate_catalog(&manifest)
.unwrap_err()
.contains("resource namespace")
);
}
#[cfg(target_arch = "x86_64")]
pub(super) fn cross_author() -> Author {
let mut author = Author::new();