1use crate::tasks::workflows::{
  2    nix_build::build_nix,
  3    run_bundling::bundle_mac,
  4    runners::{Arch, Platform},
  5    steps::NamedJob,
  6    vars::{mac_bundle_envs, windows_bundle_envs},
  7};
  8
  9use super::{runners, steps, steps::named, vars};
 10use gh_workflow::*;
 11use indexmap::IndexMap;
 12
 13/// Generates the release_nightly.yml workflow
 14pub fn release_nightly() -> Workflow {
 15    let env: IndexMap<_, _> = [
 16        ("CARGO_TERM_COLOR", "always"),
 17        ("CARGO_INCREMENTAL", "0"),
 18        ("RUST_BACKTRACE", "1"),
 19        ("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED),
 20        ("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT),
 21        (
 22            "DIGITALOCEAN_SPACES_ACCESS_KEY",
 23            vars::DIGITALOCEAN_SPACES_ACCESS_KEY,
 24        ),
 25        (
 26            "DIGITALOCEAN_SPACES_SECRET_KEY",
 27            vars::DIGITALOCEAN_SPACES_SECRET_KEY,
 28        ),
 29    ]
 30    .into_iter()
 31    .map(|(key, value)| (key.into(), value.into()))
 32    .collect();
 33
 34    let style = check_style();
 35    let tests = run_tests(Platform::Mac);
 36    let windows_tests = run_tests(Platform::Windows);
 37    let bundle_mac_x86 = bundle_mac_nightly(Arch::X86_64, &[&style, &tests]);
 38    let bundle_mac_arm = bundle_mac_nightly(Arch::ARM64, &[&style, &tests]);
 39    let linux_x86 = bundle_linux_nightly(Arch::X86_64, &[&style, &tests]);
 40    let linux_arm = bundle_linux_nightly(Arch::ARM64, &[&style, &tests]);
 41    let windows_x86 = bundle_windows_nightly(Arch::X86_64, &[&style, &windows_tests]);
 42    let windows_arm = bundle_windows_nightly(Arch::ARM64, &[&style, &windows_tests]);
 43
 44    let nix_linux_x86 = build_nix(
 45        Platform::Linux,
 46        Arch::X86_64,
 47        "default",
 48        None,
 49        &[&style, &tests],
 50    );
 51    let nix_mac_arm = build_nix(
 52        Platform::Mac,
 53        Arch::ARM64,
 54        "default",
 55        None,
 56        &[&style, &tests],
 57    );
 58    let update_nightly_tag = update_nightly_tag_job(&[
 59        &bundle_mac_x86,
 60        &bundle_mac_arm,
 61        &linux_x86,
 62        &linux_arm,
 63        &windows_x86,
 64        &windows_arm,
 65    ]);
 66
 67    named::workflow()
 68        .on(Event::default()
 69            // Fire every day at 7:00am UTC (Roughly before EU workday and after US workday)
 70            .schedule([Schedule::new("0 7 * * *")])
 71            .push(Push::default().add_tag("nightly")))
 72        .envs(env)
 73        .add_job(style.name, style.job)
 74        .add_job(tests.name, tests.job)
 75        .add_job(windows_tests.name, windows_tests.job)
 76        .add_job(bundle_mac_x86.name, bundle_mac_x86.job)
 77        .add_job(bundle_mac_arm.name, bundle_mac_arm.job)
 78        .add_job(linux_x86.name, linux_x86.job)
 79        .add_job(linux_arm.name, linux_arm.job)
 80        .add_job(windows_x86.name, windows_x86.job)
 81        .add_job(windows_arm.name, windows_arm.job)
 82        .add_job(nix_linux_x86.name, nix_linux_x86.job)
 83        .add_job(nix_mac_arm.name, nix_mac_arm.job)
 84        .add_job(update_nightly_tag.name, update_nightly_tag.job)
 85}
 86
 87fn check_style() -> NamedJob {
 88    let job = release_job(&[])
 89        .runs_on(runners::MAC_DEFAULT)
 90        .add_step(
 91            steps::checkout_repo()
 92                .add_with(("clean", false))
 93                .add_with(("fetch-depth", 0)),
 94        )
 95        .add_step(steps::cargo_fmt())
 96        .add_step(steps::script("./script/clippy"));
 97
 98    named::job(job)
 99}
100
101fn release_job(deps: &[&NamedJob]) -> Job {
102    let job = Job::default()
103        .cond(Expression::new(
104            "github.repository_owner == 'zed-industries'",
105        ))
106        .timeout_minutes(60u32);
107    if deps.len() > 0 {
108        job.needs(deps.iter().map(|j| j.name.clone()).collect::<Vec<_>>())
109    } else {
110        job
111    }
112}
113
114fn run_tests(platform: Platform) -> NamedJob {
115    let runner = match platform {
116        Platform::Windows => runners::WINDOWS_DEFAULT,
117        Platform::Linux => runners::LINUX_DEFAULT,
118        Platform::Mac => runners::MAC_DEFAULT,
119    };
120    NamedJob {
121        name: format!("run_tests_{platform}"),
122        job: release_job(&[])
123            .runs_on(runner)
124            .add_step(steps::checkout_repo())
125            .add_step(steps::setup_cargo_config(platform))
126            .add_step(steps::setup_node())
127            .add_step(steps::cargo_install_nextest(platform))
128            .add_step(steps::clear_target_dir_if_large(platform))
129            .add_step(steps::cargo_nextest(platform))
130            .add_step(steps::cleanup_cargo_config(platform)),
131    }
132}
133
134fn bundle_mac_nightly(arch: Arch, deps: &[&NamedJob]) -> NamedJob {
135    let platform = Platform::Mac;
136    NamedJob {
137        name: format!("bundle_mac_nightly_{arch}"),
138        job: release_job(deps)
139            .runs_on(runners::MAC_DEFAULT)
140            .envs(mac_bundle_envs())
141            .add_step(steps::checkout_repo())
142            .add_step(steps::setup_node())
143            .add_step(steps::setup_sentry())
144            .add_step(steps::clear_target_dir_if_large(platform))
145            .add_step(set_release_channel_to_nightly(platform))
146            .add_step(bundle_mac(arch))
147            .add_step(upload_zed_nightly(platform, arch)),
148    }
149}
150
151fn bundle_linux_nightly(arch: Arch, deps: &[&NamedJob]) -> NamedJob {
152    let platform = Platform::Linux;
153    let mut job = release_job(deps)
154        .runs_on(arch.linux_bundler())
155        .add_step(steps::checkout_repo())
156        .add_step(steps::setup_sentry())
157        .add_step(add_rust_to_path())
158        .add_step(steps::script("./script/linux"));
159
160    // todo(ci) can we do this on arm too?
161    if arch == Arch::X86_64 {
162        job = job.add_step(steps::script("./script/install-mold"));
163    }
164    job = job
165        .add_step(steps::clear_target_dir_if_large(platform))
166        .add_step(set_release_channel_to_nightly(platform))
167        .add_step(steps::script("./script/bundle-linux"))
168        .add_step(upload_zed_nightly(platform, arch));
169    NamedJob {
170        name: format!("bundle_linux_nightly_{arch}"),
171        job,
172    }
173}
174
175fn bundle_windows_nightly(arch: Arch, deps: &[&NamedJob]) -> NamedJob {
176    let platform = Platform::Windows;
177    NamedJob {
178        name: format!("bundle_windows_nightly_{arch}"),
179        job: release_job(deps)
180            .runs_on(runners::WINDOWS_DEFAULT)
181            .envs(windows_bundle_envs())
182            .add_step(steps::checkout_repo())
183            .add_step(steps::setup_sentry())
184            .add_step(set_release_channel_to_nightly(platform))
185            .add_step(build_zed_installer(arch))
186            .add_step(upload_zed_nightly_windows(arch)),
187    }
188}
189
190fn update_nightly_tag_job(deps: &[&NamedJob]) -> NamedJob {
191    NamedJob {
192        name: "update_nightly_tag".to_owned(),
193        job: release_job(deps)
194            .runs_on(runners::LINUX_CHEAP)
195            .add_step(steps::checkout_repo().add_with(("fetch-depth", 0)))
196            .add_step(update_nightly_tag())
197            .add_step(create_sentry_release()),
198    }
199}
200
201fn set_release_channel_to_nightly(platform: Platform) -> Step<Run> {
202    match platform {
203        Platform::Linux | Platform::Mac => named::bash(indoc::indoc! {r#"
204            set -eu
205            version=$(git rev-parse --short HEAD)
206            echo "Publishing version: ${version} on release channel nightly"
207            echo "nightly" > crates/zed/RELEASE_CHANNEL
208        "#}),
209        Platform::Windows => named::pwsh(indoc::indoc! {r#"
210            $ErrorActionPreference = "Stop"
211            $version = git rev-parse --short HEAD
212            Write-Host "Publishing version: $version on release channel nightly"
213            "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
214        "#})
215        .working_directory("${{ env.ZED_WORKSPACE }}"),
216    }
217}
218
219fn add_rust_to_path() -> Step<Run> {
220    named::bash(r#"echo "$HOME/.cargo/bin" >> "$GITHUB_PATH""#)
221}
222
223fn upload_zed_nightly(platform: Platform, arch: Arch) -> Step<Run> {
224    match platform {
225        Platform::Linux => named::bash(&format!("script/upload-nightly linux-targz {arch}")),
226        Platform::Mac => named::bash(&format!("script/upload-nightly macos {arch}")),
227        Platform::Windows => {
228            let cmd = match arch {
229                Arch::X86_64 => "script/upload-nightly.ps1 -Architecture x86_64",
230                Arch::ARM64 => "script/upload-nightly.ps1 -Architecture aarch64",
231            };
232            named::pwsh(cmd).working_directory("${{ env.ZED_WORKSPACE }}")
233        }
234    }
235}
236
237fn build_zed_installer(arch: Arch) -> Step<Run> {
238    let cmd = match arch {
239        Arch::X86_64 => "script/bundle-windows.ps1 -Architecture x86_64",
240        Arch::ARM64 => "script/bundle-windows.ps1 -Architecture aarch64",
241    };
242    named::pwsh(cmd).working_directory("${{ env.ZED_WORKSPACE }}")
243}
244
245fn upload_zed_nightly_windows(arch: Arch) -> Step<Run> {
246    let cmd = match arch {
247        Arch::X86_64 => "script/upload-nightly.ps1 -Architecture x86_64",
248        Arch::ARM64 => "script/upload-nightly.ps1 -Architecture aarch64",
249    };
250    named::pwsh(cmd).working_directory("${{ env.ZED_WORKSPACE }}")
251}
252
253fn update_nightly_tag() -> Step<Run> {
254    named::bash(indoc::indoc! {r#"
255        if [ "$(git rev-parse nightly)" = "$(git rev-parse HEAD)" ]; then
256          echo "Nightly tag already points to current commit. Skipping tagging."
257          exit 0
258        fi
259        git config user.name github-actions
260        git config user.email github-actions@github.com
261        git tag -f nightly
262        git push origin nightly --force
263    "#})
264}
265
266fn create_sentry_release() -> Step<Use> {
267    named::uses(
268        "getsentry",
269        "action-release",
270        "526942b68292201ac6bbb99b9a0747d4abee354c", // v3
271    )
272    .add_env(("SENTRY_ORG", "zed-dev"))
273    .add_env(("SENTRY_PROJECT", "zed"))
274    .add_env(("SENTRY_AUTH_TOKEN", vars::SENTRY_AUTH_TOKEN))
275    .add_with(("environment", "production"))
276}