//! Owned signed CLI and relocated output; the tool is an ordinary dynamic ELF. use super::*; use dever_cli::libs::LockFile; use dever_cli::packages::LockedPackage; use dever_cli::toolchain::artifact_put; use sha2::{Digest, Sha256}; use std::io::{Cursor, Write}; fn project(root: &Path, workspace: &Path) { write( root, "module/media/tool/port.dever", "execute(args: List, stdin: Bytes?) (output: dever.process.Output) fails dever.process.Error\n", ); write( root, "module/media/tool/adapter.dever", "external command \"bin/tool\" {}\n", ); write( root, "module/media/tool/app.dever", r#"execute() (passed: Bool) { input = dever.bytes.from_ints([0, 255, 128, 10]) output = port.execute(["a b", "$(literal);*"], input) passed = output.code == 23 and output.stdout == input and dever.bytes.to_text(output.stderr) == "ordinary command" } "#, ); write( root, "module/media/tool/api.dever", "cmd execute = app.execute\n", ); write(root, "config/setting.json", "{}"); let directory = root.join("module/media/tool/bin"); fs::create_dir_all(directory.join("lib")).unwrap(); command( Command::new("/usr/bin/cc") .arg("-B/usr/bin/") .args(["-shared", "-fPIC", "-Wl,-soname,libcommand.so", "-o"]) .arg(directory.join("lib/libcommand.so")) .arg(workspace.join("test/dever-cli-tests/support/command_library.c")), ); command( Command::new("/usr/bin/cc") .arg("-B/usr/bin/") .arg(workspace.join("test/dever-cli-tests/support/command_tool.c")) .arg("-L") .arg(directory.join("lib")) .arg("-lcommand") .arg("-o") .arg(directory.join("tool")), ); } fn packaged_project(root: &Path, source: &Path, layout: &Layout) { let manifest = br#"{"format":"dever-package-v1","name":"media","version":"1.0.0","dependencies":{},"lib":[]}"#; let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new())); zip.start_file( "dever-package.json", zip::write::SimpleFileOptions::default(), ) .unwrap(); zip.write_all(manifest).unwrap(); for file in [ "port.dever", "adapter.dever", "app.dever", "api.dever", "bin/tool", "bin/lib/libcommand.so", ] { let name = format!("module/media/tool/{file}"); zip.start_file(&name, zip::write::SimpleFileOptions::default()) .unwrap(); zip.write_all(&fs::read(source.join(name)).unwrap()) .unwrap(); } let archive = zip.finish().unwrap().into_inner(); let receipt = artifact_put(layout, &archive).unwrap(); fs::create_dir_all(root.join("module")).unwrap(); write( root, "config/setting.json", r#"{"package":{"registry":"https://packages.example.test","use":["media@1.0.0"]}}"#, ); let mut lock = LockFile::new(Vec::new()).unwrap(); lock.packages.push(LockedPackage { name: "media".into(), version: "1.0.0".into(), sha256: receipt.sha256, bytes: archive.len() as u64, dependencies: Vec::new(), manifest_sha256: format!("{:x}", Sha256::digest(manifest)), }); lock.write_atomic(root).unwrap(); } #[test] #[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 = inputs::workspace(); let author = TemporaryDirectory::new(); let key = author_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); let compiler = 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 source = TemporaryDirectory::new(); project(source.path(), &workspace); command(Command::new(&compiler).arg("check").arg(source.path())); assert_response(command( Command::new(&compiler).arg("run").arg(source.path()).args([ "--", "media.tool.execute", "{}", ]), )); assert!(!source.path().join("dever.lock").exists()); let deployment = TemporaryDirectory::new(); let executable = deployment.path().join("program"); command( Command::new(&compiler) .arg("build") .arg(source.path()) .arg("--output") .arg(&executable), ); assert_eq!(cache_status(&layout).unwrap().entries, 1); let package_source = TemporaryDirectory::new(); packaged_project(package_source.path(), source.path(), &layout); assert_response(command( Command::new(&compiler) .arg("run") .arg(package_source.path()) .args(["--", "media.tool.execute", "{}"]), )); let package_executable = deployment.path().join("package-program"); command( Command::new(&compiler) .arg("build") .arg(package_source.path()) .arg("--output") .arg(&package_executable), ); write(deployment.path(), "config/setting.json", "{}"); daemon.stop(); drop(source); drop(package_source); drop(author); drop(machine); assert_response(command( Command::new(&executable) .args(["media.tool.execute", "{}"]) .current_dir(deployment.path()), )); assert_response(command( Command::new(&package_executable) .args(["media.tool.execute", "{}"]) .current_dir(deployment.path()), )); let cache = deployment.path().join("data/cache/lib"); let bundle = fs::read_dir(cache).unwrap().next().unwrap().unwrap().path(); let commands = bundle.join("commands"); let tree = fs::read_dir(commands) .unwrap() .next() .unwrap() .unwrap() .path(); assert!(tree.join("lib/libcommand.so").is_file()); fs::write(tree.join("lib/libcommand.so"), b"tampered").unwrap(); let capture = deployment.path().join("tamper.stderr"); let status = process::status( Command::new(&executable) .env_clear() .current_dir(deployment.path()) .args(["media.tool.execute", "{}"]) .stdout(Stdio::null()) .stderr(Stdio::from(fs::File::create(&capture).unwrap())), Duration::from_secs(10), ) .unwrap(); assert!(!status.success()); assert!(fs::read_to_string(capture).unwrap().contains("resource")); }