//! 只在测试自己的目录中,用 dev Pod 已安装的绝对路径 Go 工具制作签名 fixture。 use super::*; use ring::signature::{Ed25519KeyPair, KeyPair}; use std::{ fs, os::unix::fs::{DirBuilderExt, PermissionsExt}, path::{Path, PathBuf}, process::Command, }; pub struct Fixture { pub root: PathBuf, pub signed: SignedResources, pub files: BTreeMap>, pub descriptor: serde_json::Value, } pub fn write(root: &Path, path: &str, bytes: impl AsRef<[u8]>) { let path = root.join(path); fs::create_dir_all(path.parent().unwrap()).unwrap(); for directory in path .parent() .unwrap() .ancestors() .take_while(|path| path.starts_with(root)) { fs::set_permissions(directory, fs::Permissions::from_mode(0o700)).unwrap(); } fs::write(&path, bytes).unwrap(); fs::set_permissions(path, fs::Permissions::from_mode(0o600)).unwrap(); } fn go(root: &Path, arguments: &[&str]) -> String { let result = Command::new("/usr/local/go/bin/go") .args(arguments) .current_dir(root) .env_clear() .env("GOTOOLCHAIN", "local") .env("GOPROXY", "off") .env("GOSUMDB", "off") .env("CGO_ENABLED", "0") .env("GO111MODULE", "off") .env("GOCACHE", root.join("go-cache")) .output() .unwrap(); assert!( result.status.success(), "{}", String::from_utf8_lossy(&result.stderr) ); String::from_utf8(result.stdout).unwrap() } impl Fixture { pub fn new() -> Self { let root = std::env::temp_dir().join(format!( "dever-go-acceptance-{}-{}", std::process::id(), std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_nanos() )); fs::DirBuilder::new().mode(0o700).create(&root).unwrap(); let version = go(&root, &["version"]); let target = BuildTarget::host().unwrap(); let arch = if target == BuildTarget::LinuxX86_64 { "amd64" } else { "arm64" }; let version_number = version .split_whitespace() .nth(2) .unwrap() .strip_prefix("go") .unwrap(); write(&root, "selector.go", include_bytes!("selector.go")); go( &root, &["build", "-trimpath", "-o", "selector", "selector.go"], ); let mut files = BTreeMap::new(); files.insert( "bin/analyze".into(), fs::read(root.join("selector")).unwrap(), ); for tool in ["compile", "link"] { files.insert( format!("bin/{tool}"), fs::read(format!("/usr/local/go/pkg/tool/linux_{arch}/{tool}")).unwrap(), ); } let exports = go( &root, &[ "list", "-deps", "-export", "-f", "{{if .Export}}{{.ImportPath}}={{.Export}}{{end}}", "bytes", "context", "encoding/binary", "encoding/json", "errors", "fmt", "io", "math", "os", "regexp", "sort", "strconv", "strings", "time", "unicode/utf8", "embed", ], ); let mut cfg = String::new(); for line in exports.lines().filter(|line| !line.is_empty()) { let (name, source) = line.split_once('=').unwrap(); let path = format!("stdlib/{name}.a"); cfg.push_str(&format!("packagefile {name}={path}\n")); files.insert(path, fs::read(source).unwrap()); } files.insert("stdlib/importcfg".into(), cfg.into_bytes()); let descriptor = serde_json::json!({"host":target.name(), "compiler":"bin/compile", "linker":"bin/link", "analyzer":"bin/analyze", "stdlib_importcfg":"stdlib/importcfg", "goos":"linux", "goarch":arch, "go_version":version.trim()}); files.insert("dever-runtime.json".into(), serde_json::to_vec(&serde_json::json!({"format":"dever-worker-runtime-v1", "ecosystem":"go", "target":target.name(), "executable":null, "arguments":[], "build":descriptor, "python_markers":null, "python_wheel_tags":[]})).unwrap()); let archive = archive(&files); let metadata = serde_json::to_vec(&serde_json::json!({"format":"dever-registry-runtime-v1", "runtime":{"pack":{"name":"go", "version":version_number, "sha256":crate::compiler::project::dependency::digest(&archive)}, "target":target.name(), "python_markers":null, "python_wheel_tags":[]}})).unwrap(); let loader = if arch == "amd64" { "ld-linux-x86-64.so.2" } else { "ld-linux-aarch64.so.1" }; let loader_source = if arch == "amd64" { "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" } else { "/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1" }; let artifacts = BTreeMap::from([ ( format!("runtime/go/{}/runtime.pack", target.name()), archive, ), ( format!("runtime/go/{}/manifest.json", target.name()), metadata, ), ( format!("process-runtime/{}/lib/{loader}", target.name()), fs::read(loader_source).unwrap(), ), ( "sdk/go/component.go".into(), fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("../sdk/go/component.go")) .unwrap(), ), ]); let signed = sign(&root.join("installation"), &artifacts); Self { root, signed, files, descriptor, } } } pub fn archive(files: &BTreeMap>) -> Vec { let gzip = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::fast()); let mut tar = tar::Builder::new(gzip); for (name, bytes) in files { let mut header = tar::Header::new_gnu(); header.set_size(bytes.len() as u64); header.set_mode(0o644); header.set_cksum(); tar.append_data(&mut header, name, bytes.as_slice()) .unwrap(); } tar.into_inner().unwrap().finish().unwrap() } pub fn sign(root: &Path, artifacts: &BTreeMap>) -> SignedResources { let key = Ed25519KeyPair::from_seed_unchecked(&[93; 32]).unwrap(); let hex = |bytes: &[u8]| { bytes .iter() .map(|byte| format!("{byte:02x}")) .collect::() }; write( root, "state/trusted-release-key", hex(key.public_key().as_ref()), ); let entries = artifacts.iter().map(|(path, bytes)| { write(root, &format!("versions/0.0.1/{path}"), bytes); serde_json::json!({"path":path, "bytes":bytes.len(), "sha256":crate::compiler::project::dependency::digest(bytes)}) }).collect::>(); let manifest = serde_json::to_vec(&serde_json::json!({"format":"dever-release-v2", "version":"0.0.1", "platform":BuildTarget::host().unwrap().name(), "artifacts":entries, "extensions":[]})).unwrap(); write(root, "versions/0.0.1/manifest.json", &manifest); write( root, "versions/0.0.1/manifest.sig", hex(key.sign(&manifest).as_ref()), ); SignedResources::load(root, "0.0.1").unwrap() } impl Drop for Fixture { fn drop(&mut self) { fs::remove_dir_all(&self.root).unwrap(); } }