use std::{fs, path::Path}; const CRATE_DIR: &str = env!("CARGO_MANIFEST_DIR"); #[test] fn desktop_packages_have_native_icons_and_cargo_commands() { let assets = Path::new(CRATE_DIR).join("assets/app-icons"); let png = fs::read(assets.join("bds.png")).expect("Linux PNG icon"); let ico = fs::read(assets.join("bds.ico")).expect("Windows ICO icon"); let icns = fs::read(assets.join("bds.icns")).expect("macOS ICNS icon"); assert_eq!(&png[..8], b"\x89PNG\r\n\x1a\n"); assert_eq!(u32::from_be_bytes(png[16..20].try_into().unwrap()), 1024); assert_eq!(u32::from_be_bytes(png[20..24].try_into().unwrap()), 1024); assert_eq!(&ico[..4], &[0, 0, 1, 0]); assert!(u16::from_le_bytes(ico[4..6].try_into().unwrap()) >= 6); assert_eq!(&icns[..4], b"icns"); let manifest = fs::read_to_string(Path::new(CRATE_DIR).join("Cargo.toml")).unwrap(); for required in [ "[package.metadata.packager]", "assets/app-icons/bds.png", "assets/app-icons/bds.ico", "assets/app-icons/bds.icns", "identifier = \"de.rfc1437.ruds\"", "deep-link-protocols = [{ schemes = [\"ruds\"] }]", "minimum-system-version = \"26.0\"", "[package.metadata.packager.deb]", "libgtk-3-0t64 | libgtk-3-0", "libwebkit2gtk-4.1-0", "libxdo3", "../../target/release/bds-runtime/*", "cargo run --quiet -p bds-package-runtime", "cargo run --quiet -p bds-package-runtime -- sign-app-for-dmg", "{ path = \"bds-mcp\" }", ] { assert!(manifest.contains(required), "missing {required}"); } assert!(!manifest.contains("signing-identity")); assert!(manifest.contains("authors.workspace = true")); let cargo_config = fs::read_to_string(Path::new(CRATE_DIR).join("../../.cargo/config.toml")) .expect("Cargo packaging aliases"); for alias in ["bundle-macos", "bundle-windows", "bundle-linux"] { assert!(cargo_config.contains(alias), "missing cargo {alias}"); } assert!(cargo_config.contains("prefer-dynamic")); assert!(cargo_config.contains("-mmacosx-version-min=26.0")); assert!(cargo_config.contains("@executable_path/../Resources")); assert!(cargo_config.contains("$ORIGIN:$ORIGIN/../lib/bds-ui")); let main = fs::read_to_string(Path::new(CRATE_DIR).join("src/main.rs")).unwrap(); assert!(main.contains("assets/app-icons/bds.png")); let build = fs::read_to_string(Path::new(CRATE_DIR).join("build.rs")).unwrap(); assert!(build.contains("set_icon(\"assets/app-icons/bds.ico\")")); let workspace_manifest = fs::read_to_string(Path::new(CRATE_DIR).join("../../Cargo.toml")).unwrap(); assert!(workspace_manifest.contains("Georg Bauer ")); assert!(workspace_manifest.contains("strip = \"symbols\"")); assert!(workspace_manifest.contains("ort-download-binaries-rustls-tls")); assert!(workspace_manifest.contains("\"copy-dylibs\"")); assert!(workspace_manifest.contains("\"sync-secret-service\", \"vendored\"")); assert!( workspace_manifest .contains("libwebp-sys = { version = \"0.9.6\", default-features = false }") ); let core_manifest = fs::read_to_string(Path::new(CRATE_DIR).join("../bds-core/Cargo.toml")).unwrap(); assert!(core_manifest.contains("crate-type = [\"dylib\"]")); assert!(core_manifest.contains("[target.'cfg(target_os = \"windows\")'.dependencies]")); assert!(core_manifest.contains("libwebp-sys = { workspace = true, features = [\"sse41\"] }")); let core_build = fs::read_to_string(Path::new(CRATE_DIR).join("../bds-core/build.rs")).unwrap(); assert!(core_build.contains("cargo:rustc-link-lib=framework=CoreML")); let server_manifest = fs::read_to_string(Path::new(CRATE_DIR).join("../bds-server/Cargo.toml")).unwrap(); assert!(server_manifest.contains("crate-type = [\"dylib\"]")); assert!(!manifest.contains("{ path = \"bds-server\" }")); assert!(!manifest.contains("-p bds-server")); assert!( !Path::new(CRATE_DIR) .join("../bds-server/src/main.rs") .exists() ); let packager = fs::read_to_string(Path::new(CRATE_DIR).join("../bds-package-runtime/src/main.rs")) .unwrap(); assert!(packager.contains("Command::new(\"codesign\")")); assert!(packager.contains("CARGO_PACKAGER_FORMAT")); assert!(!packager.contains("--options")); } #[test] fn tagged_releases_are_built_and_packaged_with_rust_tools() { let workspace = Path::new(CRATE_DIR).join("../.."); let manifest = fs::read_to_string(workspace.join("Cargo.toml")).unwrap(); assert!(manifest.contains("\"crates/bds-release\"")); for dependency in [ "apple-bundles = \"0.21\"", "apple-codesign = { version = \"0.29\", default-features = false }", "apple-dmg = \"0.5\"", ] { assert!(manifest.contains(dependency), "missing {dependency}"); } let release_manifest = fs::read_to_string(workspace.join("crates/bds-release/Cargo.toml")).unwrap(); for dependency in [ "apple-bundles.workspace = true", "apple-codesign.workspace = true", "apple-dmg.workspace = true", "flate2", "tar", "zip", ] { assert!( release_manifest.contains(dependency), "release packager is missing {dependency}" ); } let workflow = fs::read_to_string(workspace.join(".gitea/workflows/release.yml")).unwrap(); serde_yaml::from_str::(&workflow).expect("valid release workflow YAML"); for required in [ "tags:", "runs-on: linux-arm64", "cargo-zigbuild", "macos-linker-alias", "-Lnative=target/macos-linker", "-Wl,-U,_OBJC_CLASS_$_MLComputePlan", "-Wl,-U,_OBJC_CLASS_$_MLOptimizationHints", "aarch64-apple-darwin", "x86_64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "cargo-xwin", "windows-runtime", "cargo +${RUST_TOOLCHAIN} xwin cache xwin", "/root/.cache/cargo-xwin/xwin/sdk/lib/um/x86_64", "-Lnative=target/x86_64-pc-windows-msvc/release", "apt-get install --no-install-recommends --yes nasm", "x86_64-pc-windows-msvc", "bds-release", "REBUILD_PUBLISHED_RELEASE: \"true\"", "prepare", "package", "publish", "PKG_CONFIG_ALLOW_CROSS", "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER", "CC_x86_64-unknown-linux-gnu=$PWD/.gitea/scripts/x86_64-linux-gnu-gcc-pic", "CXX_x86_64-unknown-linux-gnu=$PWD/.gitea/scripts/x86_64-linux-gnu-g++-pic", "unset ZSTD_SYS_USE_PKG_CONFIG", ] { assert!( workflow.contains(required), "release workflow is missing {required}" ); } for forbidden in ["python", "curl ", "hdiutil", "makensis", "tar -"] { assert!( !workflow.contains(forbidden), "release workflow must not use host tool {forbidden}" ); } assert!( !workflow.contains("target-feature=+crt-static"), "Windows and downloaded ONNX Runtime must use the same dynamic CRT" ); assert!( !workflow.contains("run --release --locked --package bds-release"), "the release helper itself must stay unoptimized so cold runner jobs remain practical" ); assert_eq!( workflow .matches("actions-rust-lang/setup-rust-toolchain@v1") .count(), 4, "every release job using the Ubuntu runner image must bootstrap Rust" ); assert_eq!( workflow .matches("needs: [prepare-release, build-linux-x64]") .count(), 3, "the expensive platform builds must wait for the Linux x64 cross-build smoke test" ); let linux_dependencies = fs::read_to_string(workspace.join(".gitea/scripts/install-linux-build-dependencies.sh")) .unwrap(); for required in [ "libwebkit2gtk-4.1-dev", "libgtk-3-dev", "libxdo-dev", "libwebkit2gtk-4.1-dev:amd64", "gcc-x86-64-linux-gnu", "Architectures: arm64", "--download-only", "dpkg-deb --extract", "libc.so.6 libm.so.6 libmvec.so.1", "cp -a \"$sysroot/usr/lib/x86_64-linux-gnu/$library\"", "cp -a \"$sysroot/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2\"", "/usr/share/pkgconfig/.", "/opt/ruds-x64-sysroot", ] { assert!( linux_dependencies.contains(required), "Linux dependency setup is missing {required}" ); } let multiarch = linux_dependencies .find("dpkg --add-architecture amd64") .expect("amd64 architecture setup"); assert!( !linux_dependencies.contains("apt-get remove"), "cross-build setup must not replace native runner packages" ); let native_source_filter = linux_dependencies .find("'/^Architectures:/d; /^Types: deb$/a Architectures: arm64'") .expect("native apt architecture filter"); let native_cross_compiler = linux_dependencies .find("install_packages build-essential cmake gcc-x86-64-linux-gnu") .expect("native cross compiler setup"); assert!( linux_dependencies[native_source_filter..native_cross_compiler] .contains("sudo apt-get update"), "native apt indexes must be refreshed after constraining the source architecture" ); assert!( native_cross_compiler < multiarch, "the native cross compiler must be installed before enabling multiarch" ); assert!( linux_dependencies.find("--download-only").unwrap() > multiarch, "foreign libraries must only be downloaded after enabling multiarch" ); assert!(workflow.contains("PKG_CONFIG_SYSROOT_DIR: /opt/ruds-x64-sysroot")); assert!( workflow.contains("link-arg=--sysroot=/opt/ruds-x64-sysroot"), "the cross-linker must resolve absolute libc linker-script paths inside the x64 sysroot" ); assert!(!workflow.contains("export CC=\"")); assert!(!workflow.contains("export CXX=\"")); let workspace_manifest = fs::read_to_string(workspace.join("Cargo.toml")).unwrap(); let core_manifest = fs::read_to_string(workspace.join("crates/bds-core/Cargo.toml")).unwrap(); assert!( workspace_manifest .contains("pagefind = { version = \"1.5.2\", default-features = false }"), "Pagefind's unused Actix serving stack must stay disabled" ); assert!( workspace_manifest.contains("usearch = { version = \"2.26\", default-features = false }"), "USearch SIMD backends must be selected explicitly per target" ); assert!( core_manifest.contains("[target.'cfg(target_os = \"macos\")'.dependencies]") && core_manifest.contains("[target.'cfg(not(target_os = \"macos\"))'.dependencies]") && core_manifest.contains("usearch = { workspace = true, features = [\"numkong\"] }"), "macOS must use portable USearch while native targets keep NumKong SIMD" ); let release_source = fs::read_to_string(workspace.join("crates/bds-release/src/packaging.rs")).unwrap(); assert!(release_source.contains("LC_ID_DYLIB")); assert!(release_source.contains("rewrite_macho_rpaths")); assert!(!release_source.contains("Linux releases contain the portable CLI tools")); } #[test] fn x64_cross_compiler_wrappers_append_pic_last() { for (script, compiler) in [ ("x86_64-linux-gnu-gcc-pic", "x86_64-linux-gnu-gcc"), ("x86_64-linux-gnu-g++-pic", "x86_64-linux-gnu-g++"), ] { let contents = fs::read_to_string( Path::new(CRATE_DIR) .join("../../.gitea/scripts") .join(script), ) .unwrap(); assert_eq!( contents, format!("#!/bin/sh\nexec {compiler} \"$@\" -fPIC\n") ); } }