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}