nix_build.rs

  1use crate::tasks::workflows::{
  2    runners::{Arch, Platform},
  3    steps::{CommonJobConditions, NamedJob},
  4};
  5
  6use super::{runners, steps, steps::named, vars};
  7use gh_workflow::*;
  8
  9pub(crate) fn build_nix(
 10    platform: Platform,
 11    arch: Arch,
 12    flake_output: &str,
 13    cachix_filter: Option<&str>,
 14    deps: &[&NamedJob],
 15) -> NamedJob {
 16    pub fn install_nix() -> Step<Use> {
 17        named::uses(
 18            "cachix",
 19            "install-nix-action",
 20            "02a151ada4993995686f9ed4f1be7cfbb229e56f", // v31
 21        )
 22        .add_with(("github_access_token", vars::GITHUB_TOKEN))
 23    }
 24
 25    pub fn cachix_action(cachix_filter: Option<&str>) -> Step<Use> {
 26        let mut step = named::uses(
 27            "cachix",
 28            "cachix-action",
 29            "0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad", // v16
 30        )
 31        .add_with(("name", "zed"))
 32        .add_with(("authToken", vars::CACHIX_AUTH_TOKEN))
 33        .add_with(("cachixArgs", "-v"));
 34        if let Some(cachix_filter) = cachix_filter {
 35            step = step.add_with(("pushFilter", cachix_filter));
 36        }
 37        step
 38    }
 39
 40    pub fn build(flake_output: &str) -> Step<Run> {
 41        named::bash(&format!(
 42            "nix build .#{} -L --accept-flake-config",
 43            flake_output
 44        ))
 45    }
 46
 47    // After install-nix, register ~/nix-cache as a local binary cache
 48    // substituter so nix pulls from it on demand during builds (no bulk
 49    // import). Also restart the daemon so it picks up the new config.
 50    pub fn configure_local_nix_cache() -> Step<Run> {
 51        named::bash(indoc::indoc! {r#"
 52            mkdir -p ~/nix-cache
 53            echo "extra-substituters = file://$HOME/nix-cache?priority=10" | sudo tee -a /etc/nix/nix.conf
 54            echo "require-sigs = false" | sudo tee -a /etc/nix/nix.conf
 55            sudo launchctl kickstart -k system/org.nixos.nix-daemon
 56        "#})
 57    }
 58
 59    // Incrementally copy only new store paths from the build result's
 60    // closure into the local binary cache for the next run.
 61    pub fn export_to_local_nix_cache() -> Step<Run> {
 62        named::bash(indoc::indoc! {r#"
 63            if [ -L result ]; then
 64              echo "Copying build closure to local binary cache..."
 65              nix copy --to "file://$HOME/nix-cache" ./result || echo "Warning: nix copy to local cache failed"
 66            else
 67              echo "No build result found, skipping cache export."
 68            fi
 69        "#})
 70        .if_condition(Expression::new("always()"))
 71    }
 72
 73    let runner = match platform {
 74        Platform::Windows => unimplemented!(),
 75        Platform::Linux => runners::LINUX_X86_BUNDLER,
 76        Platform::Mac => runners::MAC_DEFAULT,
 77    };
 78    let mut job = Job::default()
 79        .timeout_minutes(60u32)
 80        .continue_on_error(true)
 81        .with_repository_owner_guard()
 82        .runs_on(runner)
 83        .add_env(("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED))
 84        .add_env(("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT))
 85        .add_env((
 86            "ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON",
 87            vars::ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON,
 88        ))
 89        .add_env(("GIT_LFS_SKIP_SMUDGE", "1")) // breaks the livekit rust sdk examples which we don't actually depend on
 90        .add_step(steps::checkout_repo());
 91
 92    if deps.len() > 0 {
 93        job = job.needs(deps.iter().map(|d| d.name.clone()).collect::<Vec<String>>());
 94    }
 95
 96    // On Linux, `cache: nix` uses bind-mounts so the /nix store is available
 97    // before install-nix-action runs — no extra steps needed.
 98    //
 99    // On macOS, `/nix` lives on a read-only root filesystem and the nscloud
100    // cache action cannot mount or symlink there. Instead we cache a
101    // user-writable directory (~/nix-cache) as a local binary cache and
102    // register it as a nix substituter. Nix then pulls paths from it on
103    // demand during builds (zero-copy at startup), and after building we
104    // incrementally copy new paths into the cache for the next run.
105    job = match platform {
106        Platform::Linux => job
107            .add_step(steps::cache_nix_dependencies_namespace())
108            .add_step(install_nix())
109            .add_step(cachix_action(cachix_filter))
110            .add_step(build(&flake_output)),
111        Platform::Mac => job
112            .add_step(steps::cache_nix_store_macos())
113            .add_step(install_nix())
114            .add_step(configure_local_nix_cache())
115            .add_step(cachix_action(cachix_filter))
116            .add_step(build(&flake_output))
117            .add_step(export_to_local_nix_cache()),
118        Platform::Windows => unimplemented!(),
119    };
120
121    NamedJob {
122        name: format!("build_nix_{platform}_{arch}"),
123        job,
124    }
125}