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:
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;
+}