Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,260 @@
|
||||
use super::{
|
||||
Fixture,
|
||||
fixture::{archive, hash, signed},
|
||||
};
|
||||
use dever::compiler::project::dependency::{
|
||||
Lock, Store,
|
||||
library::{
|
||||
Spec,
|
||||
lock::{Artifact, Library, Runtime},
|
||||
npm::Environment,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
// Fixture 按既有 v6 协议编码,独立于实现中的私有 Receipt 类型。
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct Inputs {
|
||||
format: String,
|
||||
toolchain: String,
|
||||
libs: Vec<Library>,
|
||||
workers: Vec<Value>,
|
||||
npm: Vec<Environment>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct Tools {
|
||||
format: String,
|
||||
ecosystem: String,
|
||||
target: String,
|
||||
runtime_sha256: String,
|
||||
python_runtime_sha256: Option<String>,
|
||||
sha256: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Receipt {
|
||||
ecosystem: String,
|
||||
roots: Vec<Spec>,
|
||||
sources: Vec<Artifact>,
|
||||
graph: Option<Environment>,
|
||||
runtime: Runtime,
|
||||
auxiliary_runtime: Option<Runtime>,
|
||||
native_entries: BTreeSet<String>,
|
||||
native_rejections: BTreeMap<String, String>,
|
||||
python_markers: Option<Value>,
|
||||
tools: Tools,
|
||||
frontend: String,
|
||||
python: Option<Value>,
|
||||
dynamic_requires: Vec<String>,
|
||||
inputs: Inputs,
|
||||
config_settings: BTreeMap<String, Value>,
|
||||
output: Artifact,
|
||||
}
|
||||
|
||||
fn prepare(fixture: &mut Fixture, addons: bool) -> Receipt {
|
||||
let node_pack = archive(&BTreeMap::from([(
|
||||
"fixture/node.txt".into(),
|
||||
b"author fixture runtime".to_vec(),
|
||||
)]));
|
||||
let python_pack = archive(&BTreeMap::from([(
|
||||
"fixture/python.txt".into(),
|
||||
b"author fixture auxiliary runtime".to_vec(),
|
||||
)]));
|
||||
let build_pack = archive(&BTreeMap::from([(
|
||||
"fixture/tools.txt".into(),
|
||||
b"already prepared output; no hooks run".to_vec(),
|
||||
)]));
|
||||
fixture.metadata.pack.sha256 = hash(&node_pack);
|
||||
let mut extra = BTreeMap::new();
|
||||
if addons {
|
||||
extra.insert(
|
||||
"package/foreign.node".into(),
|
||||
b"prebuilt foreign format".to_vec(),
|
||||
);
|
||||
}
|
||||
fixture.package(
|
||||
json!({"name":"built","version":"1.0.0","scripts":{"install":"must never run"}}),
|
||||
extra,
|
||||
);
|
||||
fixture.resolve(&["npm:built@1.0.0"]).unwrap();
|
||||
let graph = fixture.graph();
|
||||
let mut output = BTreeMap::from([
|
||||
(
|
||||
"node_modules/built/package.json".into(),
|
||||
serde_json::to_vec(
|
||||
&json!({"name":"built","version":"1.0.0","scripts":{"install":"must never run"}}),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
(
|
||||
"node_modules/built/generated.js".into(),
|
||||
b"module.exports=42".to_vec(),
|
||||
),
|
||||
]);
|
||||
if addons {
|
||||
output.insert(
|
||||
"node_modules/built/foreign.node".into(),
|
||||
b"prebuilt foreign format".to_vec(),
|
||||
);
|
||||
}
|
||||
let bytes = archive(&output);
|
||||
fixture.put(&bytes).unwrap();
|
||||
let python = Runtime {
|
||||
name: "python".into(),
|
||||
version: "3.12.1".into(),
|
||||
sha256: hash(&python_pack),
|
||||
};
|
||||
let tools = Tools {
|
||||
format: "dever-registry-build-v1".into(),
|
||||
ecosystem: "npm".into(),
|
||||
target: fixture.target.name().into(),
|
||||
runtime_sha256: fixture.metadata.pack.sha256.clone(),
|
||||
python_runtime_sha256: Some(python.sha256.clone()),
|
||||
sha256: hash(&build_pack),
|
||||
};
|
||||
let target = fixture.target.name();
|
||||
let files=BTreeMap::from([
|
||||
(format!("runtime/npm/{target}/runtime.pack"),node_pack),
|
||||
(format!("runtime/npm/{target}/manifest.json"),serde_json::to_vec(&json!({"format":"dever-registry-runtime-v1","runtime":{"pack":fixture.metadata.pack,"target":target,"python_markers":null,"python_wheel_tags":[],"npm_libc":"glibc"}})).unwrap()),
|
||||
(format!("runtime/pip/{target}/runtime.pack"),python_pack),
|
||||
(format!("runtime/pip/{target}/manifest.json"),serde_json::to_vec(&json!({"format":"dever-registry-runtime-v1","runtime":{"pack":python,"target":target,"python_markers":{"implementation_name":"cpython","implementation_version":"3.12.1","os_name":"posix","platform_machine":"x86_64","platform_python_implementation":"CPython","platform_release":"","platform_system":"Linux","platform_version":"","python_full_version":"3.12.1","python_version":"3.12","sys_platform":"linux"},"python_wheel_tags":["py3-none-any"]}})).unwrap()),
|
||||
(format!("build/npm/{target}/build.pack"),build_pack),
|
||||
(format!("build/npm/{target}/manifest.json"),serde_json::to_vec(&tools).unwrap()),
|
||||
]);
|
||||
fixture.signed = signed(&fixture.directory, &files);
|
||||
Receipt {
|
||||
ecosystem: "npm".into(),
|
||||
roots: graph.roots.clone(),
|
||||
sources: fixture
|
||||
.lock
|
||||
.libs
|
||||
.iter()
|
||||
.flat_map(|lib| lib.artifacts.clone())
|
||||
.collect(),
|
||||
graph: Some(graph),
|
||||
runtime: fixture.metadata.pack.clone(),
|
||||
auxiliary_runtime: Some(python),
|
||||
native_entries: BTreeSet::new(),
|
||||
native_rejections: if addons {
|
||||
BTreeMap::from([("node_modules/built/foreign.node".into(), "format".into())])
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
},
|
||||
python_markers: None,
|
||||
tools,
|
||||
frontend: "cfe67ebbc939762cd4ba7328943bf97a72d06a4b79071f056ebd8e5ab566a721".into(),
|
||||
python: None,
|
||||
dynamic_requires: vec![],
|
||||
inputs: serde_json::from_value(serde_json::to_value(&fixture.lock).unwrap()).unwrap(),
|
||||
config_settings: BTreeMap::new(),
|
||||
output: Artifact {
|
||||
target: target.into(),
|
||||
path: format!("build/npm/{}/installation.tgz", hash(&bytes)),
|
||||
bytes: bytes.len() as u64,
|
||||
sha256: hash(&bytes),
|
||||
source: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn attach(fixture: &mut Fixture, receipt: &Receipt) {
|
||||
fixture.lock.npm[0]["build"] = json!(hash(&serde_json::to_vec(receipt).unwrap()));
|
||||
fixture.lock.builds = vec![serde_json::to_value(receipt).unwrap()];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn npm_lifecycle_receipt_consumes_signed_inputs_without_network_or_hooks() {
|
||||
let mut fixture = Fixture::new();
|
||||
let receipt = prepare(&mut fixture, false);
|
||||
attach(&mut fixture, &receipt);
|
||||
fixture.documents.clear();
|
||||
let calls = fixture.calls.borrow().len();
|
||||
let files = fixture.install().unwrap();
|
||||
assert_eq!(
|
||||
files["node_modules/built/generated.js"],
|
||||
b"module.exports=42"
|
||||
);
|
||||
assert_eq!(fixture.calls.borrow().len(), calls);
|
||||
let text = fixture.lock.encode().unwrap();
|
||||
let reparsed = Lock::decode(&text).unwrap();
|
||||
fixture.lock = reparsed;
|
||||
fixture.install().unwrap();
|
||||
fixture.lock.builds[0]["frontend"] = json!("0".repeat(64));
|
||||
assert!(fixture.install().unwrap_err().contains("receipt"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn npm_native_receipt_accounts_for_all_addons_and_never_admits_failed_build() {
|
||||
let mut fixture = Fixture::new();
|
||||
let mut receipt = prepare(&mut fixture, true);
|
||||
attach(&mut fixture, &receipt);
|
||||
assert!(
|
||||
!fixture
|
||||
.install()
|
||||
.unwrap()
|
||||
.keys()
|
||||
.any(|path| path.ends_with(".node"))
|
||||
);
|
||||
receipt.native_rejections.clear();
|
||||
attach(&mut fixture, &receipt);
|
||||
assert!(fixture.install().unwrap_err().contains("admission"));
|
||||
receipt
|
||||
.native_entries
|
||||
.insert("node_modules/built/foreign.node".into());
|
||||
attach(&mut fixture, &receipt);
|
||||
assert!(fixture.install().unwrap_err().contains("ELF"));
|
||||
receipt.native_entries.clear();
|
||||
receipt
|
||||
.native_rejections
|
||||
.insert("node_modules/built/new.node".into(), "unloadable".into());
|
||||
attach(&mut fixture, &receipt);
|
||||
assert!(fixture.install().unwrap_err().contains("admission"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn npm_replay_rejects_malformed_binding_before_fetching_or_executing() {
|
||||
let mut fixture = Fixture::new();
|
||||
let mut receipt = prepare(&mut fixture, false);
|
||||
receipt.tools.runtime_sha256 = "0".repeat(64);
|
||||
attach(&mut fixture, &receipt);
|
||||
fixture.cache.borrow_mut().clear();
|
||||
let calls = fixture.calls.borrow().len();
|
||||
let error = dever::compiler::project::dependency::library::npm::restore(
|
||||
&fixture.lock,
|
||||
&fixture.graph(),
|
||||
fixture.target,
|
||||
&fixture.signed,
|
||||
&fixture,
|
||||
&fixture,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(error.contains("binding differs"), "{error}");
|
||||
assert_eq!(fixture.calls.borrow().len(), calls);
|
||||
assert!(fixture.cache.borrow().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn npm_cached_receipt_rejects_tampering_but_needs_no_build_tools() {
|
||||
let mut fixture = Fixture::new();
|
||||
let receipt = prepare(&mut fixture, false);
|
||||
attach(&mut fixture, &receipt);
|
||||
let original = fixture.cache.borrow()[&receipt.output.sha256].clone();
|
||||
fixture
|
||||
.cache
|
||||
.borrow_mut()
|
||||
.insert(receipt.output.sha256.clone(), b"tampered".to_vec());
|
||||
assert!(fixture.install().unwrap_err().contains("digest"));
|
||||
fixture
|
||||
.cache
|
||||
.borrow_mut()
|
||||
.insert(receipt.output.sha256.clone(), original);
|
||||
std::fs::remove_file(fixture.directory.0.join(format!(
|
||||
"signed/versions/0.0.1/build/npm/{}/build.pack",
|
||||
fixture.target.name()
|
||||
)))
|
||||
.unwrap();
|
||||
fixture.install().unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user