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