lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 85d10cae9070a26cd05dc6dd70c89b1c9140c21d
parent c08891446f9ee8b2531125fc16b37f240cac426f
Author: triesap <tyson@radroots.org>
Date:   Thu, 12 Mar 2026 20:17:15 +0000

build: add flake-managed nix workspace

- add a pinned flake, lock file, shells, apps, and pure check surfaces
- make libsodium and libclang explicit for rust, wasm, and coverage workflows
- let xtask coverage and workspace resolution honor nix-managed overrides
- verify cargo check, cargo test, dockerized flake lock generation, and flake eval

Diffstat:
A.envrc | 1+
M.gitignore | 3+++
Mcrates/xtask/src/coverage.rs | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mcrates/xtask/src/main.rs | 22+++++++++++++++++++++-
Aflake.lock | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.nix | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anix/apps.nix | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anix/checks.nix | 37+++++++++++++++++++++++++++++++++++++
Anix/common.nix | 289++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anix/devshells.nix | 29+++++++++++++++++++++++++++++
Anix/toolchains.nix | 26++++++++++++++++++++++++++
Atreefmt.nix | 13+++++++++++++
12 files changed, 795 insertions(+), 14 deletions(-)

diff --git a/.envrc b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore @@ -45,3 +45,6 @@ prompt*.txt # Dev .local +.direnv/ +.treefmt-cache/ +result diff --git a/crates/xtask/src/coverage.rs b/crates/xtask/src/coverage.rs @@ -591,13 +591,24 @@ fn parse_bool_flag(args: &[String], name: &str) -> bool { args.iter().any(|arg| arg == &flag) } -fn workspace_root() -> PathBuf { +fn workspace_root_with_override(override_root: Option<&str>) -> PathBuf { + if let Some(raw) = override_root { + let trimmed = raw.trim(); + if !trimmed.is_empty() { + return PathBuf::from(trimmed); + } + } let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let crates_dir = manifest_dir.parent().unwrap_or(manifest_dir); let root = crates_dir.parent().unwrap_or(crates_dir); root.to_path_buf() } +fn workspace_root() -> PathBuf { + let override_root = std::env::var("RADROOTS_WORKSPACE_ROOT").ok(); + workspace_root_with_override(override_root.as_deref()) +} + fn run_command(mut command: Command, name: &str) -> Result<(), String> { let status = match command.status() { Ok(status) => status, @@ -618,6 +629,30 @@ fn apply_coverage_profile_flags(command: &mut Command, profile: &CoverageProfile } } +fn coverage_cargo_command_with_override(override_binary: Option<&str>) -> Command { + if let Some(binary) = override_binary { + return Command::new(binary); + } + + let mut cmd = Command::new("rustup"); + cmd.arg("run").arg("nightly").arg("cargo"); + cmd +} + +fn coverage_cargo_command() -> Command { + let override_binary = std::env::var("RADROOTS_COVERAGE_CARGO") + .ok() + .map(|raw| raw.trim().to_string()) + .filter(|raw| !raw.is_empty()); + coverage_cargo_command_with_override(override_binary.as_deref()) +} + +fn coverage_llvm_cov_command() -> Command { + let mut cmd = coverage_cargo_command(); + cmd.arg("llvm-cov"); + cmd +} + fn run_crate_with_runner_at_root( args: &[String], workspace_root: &Path, @@ -643,12 +678,8 @@ fn run_crate_with_runner_at_root( runner( { - let mut cmd = Command::new("rustup"); - cmd.arg("run") - .arg("nightly") - .arg("cargo") - .arg("llvm-cov") - .arg("clean") + let mut cmd = coverage_llvm_cov_command(); + cmd.arg("clean") .arg("--workspace") .current_dir(workspace_root); cmd @@ -658,8 +689,7 @@ fn run_crate_with_runner_at_root( runner( { - let mut cmd = Command::new("rustup"); - cmd.arg("run").arg("nightly").arg("cargo").arg("llvm-cov"); + let mut cmd = coverage_llvm_cov_command(); cmd.arg("-p").arg(&crate_name); apply_coverage_profile_flags(&mut cmd, &profile); cmd.arg("--no-report") @@ -675,8 +705,7 @@ fn run_crate_with_runner_at_root( let summary_path = out_dir.join("coverage-summary.json"); runner( { - let mut cmd = Command::new("rustup"); - cmd.arg("run").arg("nightly").arg("cargo").arg("llvm-cov"); + let mut cmd = coverage_llvm_cov_command(); cmd.arg("report").arg("-p").arg(&crate_name); cmd.arg("--json") .arg("--summary-only") @@ -692,8 +721,7 @@ fn run_crate_with_runner_at_root( let lcov_path = out_dir.join("coverage-lcov.info"); runner( { - let mut cmd = Command::new("rustup"); - cmd.arg("run").arg("nightly").arg("cargo").arg("llvm-cov"); + let mut cmd = coverage_llvm_cov_command(); cmd.arg("report").arg("-p").arg(&crate_name); cmd.arg("--lcov") .arg("--branch") @@ -1522,6 +1550,46 @@ test_threads = 0 } #[test] + fn coverage_cargo_command_defaults_to_rustup_nightly() { + let cmd = coverage_cargo_command_with_override(None); + let args = cmd + .get_args() + .map(|arg| arg.to_string_lossy().to_string()) + .collect::<Vec<_>>(); + + assert_eq!(cmd.get_program().to_string_lossy(), "rustup"); + assert_eq!( + args, + vec![ + "run".to_string(), + "nightly".to_string(), + "cargo".to_string() + ] + ); + } + + #[test] + fn coverage_cargo_command_uses_override_binary_when_present() { + let cmd = coverage_cargo_command_with_override(Some("/tmp/nightly-cargo")); + let args = cmd + .get_args() + .map(|arg| arg.to_string_lossy().to_string()) + .collect::<Vec<_>>(); + + assert_eq!(cmd.get_program().to_string_lossy(), "/tmp/nightly-cargo"); + assert!(args.is_empty()); + } + + #[test] + fn workspace_root_override_takes_precedence() { + let root = workspace_root_with_override(Some("/tmp/radroots-coverage-root")); + assert_eq!(root, PathBuf::from("/tmp/radroots-coverage-root")); + + let fallback = workspace_root_with_override(Some("")); + assert!(fallback.join("Cargo.toml").exists()); + } + + #[test] fn run_crate_with_runner_uses_default_output_dir_when_out_is_missing() { let args = vec!["--crate".to_string(), "radroots-core".to_string()]; let mut output_path_seen = false; diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs @@ -26,13 +26,24 @@ fn usage() { ); } -fn workspace_root() -> PathBuf { +fn workspace_root_with_override(override_root: Option<&str>) -> PathBuf { + if let Some(raw) = override_root { + let trimmed = raw.trim(); + if !trimmed.is_empty() { + return PathBuf::from(trimmed); + } + } let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let crates_dir = manifest_dir.parent().unwrap_or(manifest_dir); let root = crates_dir.parent().unwrap_or(crates_dir); root.to_path_buf() } +fn workspace_root() -> PathBuf { + let override_root = env::var("RADROOTS_WORKSPACE_ROOT").ok(); + workspace_root_with_override(override_root.as_deref()) +} + fn parse_out_dir(args: &[String], workspace_root: &Path) -> Result<PathBuf, String> { if args.is_empty() { return Ok(workspace_root.join("target").join("sdk-export")); @@ -255,6 +266,15 @@ mod tests { } #[test] + fn workspace_root_override_takes_precedence() { + let root = workspace_root_with_override(Some("/tmp/radroots-test-root")); + assert_eq!(root, PathBuf::from("/tmp/radroots-test-root")); + + let fallback = workspace_root_with_override(Some(" ")); + assert!(fallback.join("Cargo.toml").exists()); + } + + #[test] fn run_release_and_dispatchers_cover_error_paths() { let unknown_release = run_release(&["unknown".to_string()]).expect_err("unknown release subcommand"); diff --git a/flake.lock b/flake.lock @@ -0,0 +1,119 @@ +{ + "nodes": { + "crane": { + "locked": { + "lastModified": 1773189535, + "narHash": "sha256-E1G/Or6MWeP+L6mpQ0iTFLpzSzlpGrITfU2220Gq47g=", + "owner": "ipetkov", + "repo": "crane", + "rev": "6fa2fb4cf4a89ba49fc9dd5a3eb6cde99d388269", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1773222311, + "narHash": "sha256-BHoB/XpbqoZkVYZCfXJXfkR+GXFqwb/4zbWnOr2cRcU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0590cd39f728e129122770c029970378a79d076a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", + "treefmt-nix": "treefmt-nix" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773284828, + "narHash": "sha256-0qI+a9z7KpYCBbp4ENN32b2tf0VmC3MhgFw1KAroqxQ=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "2b18fe48d9a8a4ff3850d56b67cfe72f2a589237", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773297127, + "narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "71b125cd05fbfd78cab3e070b73544abe24c5016", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix @@ -0,0 +1,68 @@ +{ + description = "radroots core libraries"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-parts.url = "github:hercules-ci/flake-parts"; + crane.url = "github:ipetkov/crane"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ inputs.treefmt-nix.flakeModule ]; + systems = [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-darwin" + "x86_64-linux" + ]; + + perSystem = + { + config, + lib, + system, + ... + }: + let + pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ inputs.rust-overlay.overlays.default ]; + }; + toolchains = import ./nix/toolchains.nix { inherit pkgs; }; + common = import ./nix/common.nix { + crane = inputs.crane; + inherit lib pkgs toolchains; + }; + in + { + treefmt = import ./treefmt.nix; + + apps = import ./nix/apps.nix { + inherit common config pkgs toolchains; + }; + + checks = lib.filterAttrs (_: value: value != null) ( + import ./nix/checks.nix { + inherit common pkgs; + } + ); + + devShells = import ./nix/devshells.nix { + inherit common pkgs toolchains; + }; + + packages = { + xtask = common.xtaskPackage; + }; + }; + }; +} diff --git a/nix/apps.nix b/nix/apps.nix @@ -0,0 +1,108 @@ +{ common, config, pkgs, toolchains }: +let + stablePath = "export PATH=${toolchains.stable}/bin:$PATH"; + coveragePath = "export PATH=${toolchains.stable}/bin:${toolchains.coverage}/bin:$PATH"; + mkRepoApp = + { + name, + runtimeInputs, + command, + env ? common.exportSharedEnv, + pathPrefix ? stablePath, + }: + let + script = pkgs.writeShellApplication { + inherit name runtimeInputs; + text = '' + set -euo pipefail + + repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" + cd "$repo_root" + + ${common.ensureRepoRoot} + ${env} + ${pathPrefix} + + ${command} + ''; + }; + in + { + type = "app"; + program = "${script}/bin/${name}"; + }; +in +{ + check = mkRepoApp { + name = "check"; + runtimeInputs = common.runtimeInputs.stable; + command = common.checkCommand; + }; + + contract = mkRepoApp { + name = "contract"; + runtimeInputs = common.runtimeInputs.stable; + command = common.contractCommand; + }; + + coverage-report = mkRepoApp { + name = "coverage-report"; + runtimeInputs = common.runtimeInputs.coverage; + command = common.coverageReportCommand; + env = common.exportCoverageEnv; + pathPrefix = coveragePath; + }; + + export-ts = mkRepoApp { + name = "export-ts"; + runtimeInputs = common.runtimeInputs.stable; + command = '' + cargo run -q -p xtask -- sdk export-ts "$@" + ''; + }; + + fmt = mkRepoApp { + name = "fmt"; + runtimeInputs = common.runtimeInputs.stable ++ [ + config.treefmt.build.wrapper + ]; + command = '' + cargo fmt --all + ${config.treefmt.build.wrapper}/bin/treefmt + ''; + }; + + publish-crates = mkRepoApp { + name = "publish-crates"; + runtimeInputs = common.runtimeInputs.release; + command = '' + ./publish-crates.sh "$@" + ''; + env = common.exportCoverageEnv; + pathPrefix = coveragePath; + }; + + publish-dry-run = mkRepoApp { + name = "publish-dry-run"; + runtimeInputs = common.runtimeInputs.release; + command = '' + ./publish-crates.sh --dry-run "$@" + ''; + env = common.exportCoverageEnv; + pathPrefix = coveragePath; + }; + + release-preflight = mkRepoApp { + name = "release-preflight"; + runtimeInputs = common.runtimeInputs.coverage; + command = common.releasePreflightCommand; + env = common.exportCoverageEnv; + pathPrefix = coveragePath; + }; + + wasm-builds = mkRepoApp { + name = "wasm-builds"; + runtimeInputs = common.runtimeInputs.wasm; + command = common.wasmBuildsCommand; + }; +} diff --git a/nix/checks.nix b/nix/checks.nix @@ -0,0 +1,37 @@ +{ common, pkgs }: +let + cargoFmt = common.craneLib.cargoFmt common.commonCraneArgs; + cargoCheck = common.craneLib.cargoCheck ( + common.commonCraneArgs + // { + inherit (common) cargoArtifacts; + cargoExtraArgs = common.sdkContractCargoArgs; + } + ); + cargoTest = common.craneLib.cargoTest ( + common.commonCraneArgs + // { + inherit (common) cargoArtifacts; + cargoExtraArgs = common.sdkContractCargoArgs; + } + ); +in +{ + cargo-fmt = cargoFmt; + cargo-check = cargoCheck; + cargo-test = cargoTest; + + guards = common.mkRepoCheck { + name = "repo-guards"; + runtimeInputs = [ + pkgs.coreutils + pkgs.gitMinimal + pkgs.gnugrep + ]; + initGit = true; + command = '' + ./scripts/ci/guard_committed_ts_artifacts.sh + ./scripts/ci/guard_no_legacy_identifiers.sh + ''; + }; +} diff --git a/nix/common.nix b/nix/common.nix @@ -0,0 +1,289 @@ +{ crane, lib, pkgs, toolchains }: +let + root = ../.; + cargoToml = builtins.fromTOML (builtins.readFile ../Cargo.toml); + version = cargoToml.workspace.package.version; + darwinBuildInputs = lib.optionals pkgs.stdenv.isDarwin [ + pkgs.libiconv + ]; + repoSource = lib.sources.cleanSource root; + cargoSource = lib.fileset.toSource { + root = root; + fileset = lib.fileset.intersection + (lib.fileset.fromSource repoSource) + (lib.fileset.unions [ + ../Cargo.toml + ../Cargo.lock + ../Makefile + ../README.md + ../publish-crates.sh + ../rust-toolchain.toml + ../conformance + ../contract + ../crates + ../scripts + ]); + }; + sharedEnv = { + CARGO_TERM_COLOR = "always"; + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + SODIUM_USE_PKG_CONFIG = "1"; + } // lib.optionalAttrs pkgs.stdenv.isDarwin { + SDKROOT = "${pkgs.darwin.apple_sdk.sdk}"; + }; + coverageEnv = sharedEnv // { + RADROOTS_COVERAGE_CARGO = "${toolchains.coverage}/bin/cargo"; + }; + exportEnv = + env: + lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: value: "export ${name}=${lib.escapeShellArg value}") env + ); + stableRuntimeInputs = with pkgs; [ + toolchains.stable + clang + coreutils + curl + findutils + gawk + gitMinimal + gnugrep + gnumake + gnused + jq + libsodium + llvmPackages.libclang + pkg-config + python3 + ] ++ darwinBuildInputs; + coverageRuntimeInputs = stableRuntimeInputs ++ [ + toolchains.coverage + pkgs.cargo-llvm-cov + ]; + wasmRuntimeInputs = stableRuntimeInputs ++ [ + pkgs.wasm-pack + ]; + releaseRuntimeInputs = coverageRuntimeInputs ++ [ + pkgs.wasm-pack + ]; + sdkContractCrates = [ + "xtask" + "radroots-core" + "radroots-types" + "radroots-events" + "radroots-trade" + "radroots-identity" + "radroots-replica-db-schema" + "radroots-events-codec" + "radroots-events-codec-wasm" + ]; + sdkContractCargoArgs = lib.concatStringsSep " " (map (crate: "-p ${crate}") sdkContractCrates); + craneLib = (crane.mkLib pkgs).overrideToolchain toolchains.stable; + commonCraneArgs = { + inherit version; + pname = "radroots"; + src = cargoSource; + strictDeps = true; + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.clang + pkgs.llvmPackages.libclang + ]; + buildInputs = [ + pkgs.libsodium + ] ++ darwinBuildInputs; + inherit (sharedEnv) + CARGO_TERM_COLOR + LIBCLANG_PATH + SODIUM_USE_PKG_CONFIG; + }; + cargoArtifacts = craneLib.buildDepsOnly commonCraneArgs; + xtaskPackage = craneLib.buildPackage ( + commonCraneArgs + // { + inherit cargoArtifacts; + pname = "xtask"; + cargoExtraArgs = "-p xtask"; + doCheck = false; + } + ); + initGitRepo = '' + git init -q . + git config user.email "nix-check@example.invalid" + git config user.name "nix check" + git add -A . + ''; + mkRepoCheck = + { + name, + runtimeInputs, + command, + env ? sharedEnv, + initGit ? false, + linuxOnly ? false, + }: + if linuxOnly && !pkgs.stdenv.isLinux then + null + else + pkgs.runCommand name { nativeBuildInputs = runtimeInputs; } '' + export HOME="$TMPDIR/home" + mkdir -p "$HOME" + + cp -R ${repoSource} "$TMPDIR/repo" + chmod -R u+w "$TMPDIR/repo" + cd "$TMPDIR/repo" + export RADROOTS_WORKSPACE_ROOT="$PWD" + + ${exportEnv env} + ${lib.optionalString initGit initGitRepo} + + ${command} + + touch "$out" + ''; + ensureRepoRoot = '' + if [ ! -f Cargo.toml ] || [ ! -f flake.nix ]; then + echo "run this command from the radroots workspace checkout" >&2 + exit 1 + fi + export RADROOTS_WORKSPACE_ROOT="$PWD" + ''; + checkCommand = '' + cargo check --workspace + ''; + contractCommand = '' + ./scripts/ci/guard_committed_ts_artifacts.sh + ./scripts/ci/guard_no_legacy_identifiers.sh + cargo check -q ${sdkContractCargoArgs} + cargo test -q ${sdkContractCargoArgs} + cargo run -q -p xtask -- sdk validate + cargo run -q -p xtask -- sdk export-ts --out target/sdk-export-ci + test -f target/sdk-export-ci/ts/export-manifest.json + ''; + wasmBuildsCommand = '' + make build + ''; + releasePreflightCommand = '' + ./scripts/ci/release_preflight.sh + ''; + coverageReportCommand = '' + mkdir -p target/sdk-coverage + : > target/sdk-coverage/coverage-report-status.txt + + workspace_crates_file="$(mktemp)" + required_crates_file="$(mktemp)" + trap 'rm -f "$workspace_crates_file" "$required_crates_file"' EXIT + + cargo run -q -p xtask -- sdk coverage workspace-crates > "$workspace_crates_file" + while IFS= read -r crate; do + [ -n "''${crate}" ] || continue + safe_crate="''${crate//-/_}" + run_dir="target/sdk-coverage/''${safe_crate}" + mkdir -p "''${run_dir}" + status="ok" + + if ! cargo run -q -p xtask -- sdk coverage run-crate --crate "''${crate}" --out "''${run_dir}" --test-threads 1; then + status="run-failed" + fi + + if [ "''${status}" = "ok" ] && ! cargo run -q -p xtask -- sdk coverage report \ + --scope "''${crate}" \ + --summary "''${run_dir}/coverage-summary.json" \ + --lcov "''${run_dir}/coverage-lcov.info" \ + --out "''${run_dir}/coverage-gate-summary.json" \ + --fail-under-exec-lines 0 \ + --fail-under-functions 0 \ + --fail-under-regions 0 \ + --fail-under-branches 0; then + status="report-failed" + fi + + if [ "''${status}" != "ok" ]; then + cat > "''${run_dir}/coverage-gate-summary.json" <<EOF + { + "scope": "''${crate}", + "thresholds": { + "executable_lines": 0, + "functions": 0, + "regions": 0, + "branches": 0, + "branches_required": false + }, + "measured": { + "executable_lines_percent": 0, + "executable_lines_source": "da", + "functions_percent": 0, + "branches_percent": null, + "branches_available": false, + "summary_lines_percent": 0, + "summary_regions_percent": 0 + }, + "counts": { + "executable_lines": { + "covered": 0, + "total": 0 + }, + "branches": { + "covered": 0, + "total": 0 + } + }, + "result": { + "pass": false, + "fail_reasons": [ + "''${status}" + ] + } + } +EOF + fi + + echo "''${crate}:''${status}" >> target/sdk-coverage/coverage-report-status.txt + done < "$workspace_crates_file" + + cargo run -q -p xtask -- sdk coverage required-crates > "$required_crates_file" + while IFS= read -r crate; do + [ -n "''${crate}" ] || continue + safe_crate="''${crate//-/_}" + crate_dir="target/sdk-coverage/''${safe_crate}" + cargo run -q -p xtask -- sdk coverage report \ + --scope "''${crate}-blocking" \ + --summary "''${crate_dir}/coverage-summary.json" \ + --lcov "''${crate_dir}/coverage-lcov.info" \ + --out "''${crate_dir}/coverage-gate-blocking.json" \ + --fail-under-exec-lines 100 \ + --fail-under-functions 100 \ + --fail-under-regions 100 \ + --fail-under-branches 100 \ + --require-branches + done < "$required_crates_file" + ''; +in +{ + inherit + cargoArtifacts + checkCommand + commonCraneArgs + contractCommand + coverageEnv + coverageReportCommand + craneLib + ensureRepoRoot + mkRepoCheck + releasePreflightCommand + sdkContractCargoArgs + sharedEnv + version + wasmBuildsCommand + xtaskPackage; + + exportCoverageEnv = exportEnv coverageEnv; + exportSharedEnv = exportEnv sharedEnv; + + runtimeInputs = { + stable = stableRuntimeInputs; + coverage = coverageRuntimeInputs; + release = releaseRuntimeInputs; + wasm = wasmRuntimeInputs; + }; +} diff --git a/nix/devshells.nix b/nix/devshells.nix @@ -0,0 +1,29 @@ +{ common, pkgs, toolchains }: +let + defaultHook = '' + ${common.exportSharedEnv} + export PATH=${toolchains.stable}/bin:$PATH + ''; + coverageHook = '' + ${common.exportCoverageEnv} + export PATH=${toolchains.stable}/bin:${toolchains.coverage}/bin:$PATH + ''; +in +{ + default = pkgs.mkShell { + packages = common.runtimeInputs.wasm ++ [ + pkgs.cargo-llvm-cov + ]; + shellHook = defaultHook; + }; + + coverage = pkgs.mkShell { + packages = common.runtimeInputs.release; + shellHook = coverageHook; + }; + + release = pkgs.mkShell { + packages = common.runtimeInputs.release; + shellHook = coverageHook; + }; +} diff --git a/nix/toolchains.nix b/nix/toolchains.nix @@ -0,0 +1,26 @@ +{ pkgs }: +let + toolchain = builtins.fromTOML (builtins.readFile ../rust-toolchain.toml); + stableVersion = toolchain.toolchain.channel; + stableTargets = toolchain.toolchain.targets or []; + extensions = [ + "clippy" + "rust-analyzer" + "rust-src" + "rustfmt" + ]; +in +{ + stable = pkgs.rust-bin.stable.${stableVersion}.default.override { + inherit extensions; + targets = stableTargets; + }; + + coverage = pkgs.rust-bin.selectLatestNightlyWith ( + nightly: + nightly.default.override { + inherit extensions; + targets = stableTargets; + } + ); +} diff --git a/treefmt.nix b/treefmt.nix @@ -0,0 +1,13 @@ +{ + projectRootFile = "flake.nix"; + + settings.global.excludes = [ + ".direnv/**" + "target/**" + "crates/*/bindings/**" + ]; + + programs.nixfmt.enable = true; + programs.shfmt.enable = true; + programs.taplo.enable = true; +}