1use std::path::Path;
2
3use crate::tasks::workflows::{
4 nix_build::build_nix,
5 release::ReleaseBundleJobs,
6 runners::{Arch, Platform, ReleaseChannel},
7 steps::{DEFAULT_REPOSITORY_OWNER_GUARD, FluentBuilder, NamedJob, dependant_job, named},
8 vars::{assets, bundle_envs},
9};
10
11use super::{runners, steps};
12use gh_workflow::*;
13use indoc::indoc;
14
15pub fn run_bundling() -> Workflow {
16 let bundle = ReleaseBundleJobs {
17 linux_aarch64: bundle_linux(Arch::AARCH64, None, &[]),
18 linux_x86_64: bundle_linux(Arch::X86_64, None, &[]),
19 mac_aarch64: bundle_mac(Arch::AARCH64, None, &[]),
20 mac_x86_64: bundle_mac(Arch::X86_64, None, &[]),
21 windows_aarch64: bundle_windows(Arch::AARCH64, None, &[]),
22 windows_x86_64: bundle_windows(Arch::X86_64, None, &[]),
23 };
24 let nix_linux_x86_64 = nix_job(Platform::Linux, Arch::X86_64);
25 let nix_mac_aarch64 = nix_job(Platform::Mac, Arch::AARCH64);
26 named::workflow()
27 .on(Event::default().pull_request(
28 PullRequest::default().types([PullRequestType::Labeled, PullRequestType::Synchronize]),
29 ))
30 .concurrency(
31 Concurrency::new(Expression::new(
32 "${{ github.workflow }}-${{ github.head_ref || github.ref }}",
33 ))
34 .cancel_in_progress(true),
35 )
36 .add_env(("CARGO_TERM_COLOR", "always"))
37 .add_env(("RUST_BACKTRACE", "1"))
38 .map(|mut workflow| {
39 for job in bundle.into_jobs() {
40 workflow = workflow.add_job(job.name, job.job);
41 }
42 workflow
43 })
44 .add_job(nix_linux_x86_64.name, nix_linux_x86_64.job)
45 .add_job(nix_mac_aarch64.name, nix_mac_aarch64.job)
46}
47
48fn nix_job(platform: Platform, arch: Arch) -> NamedJob {
49 let mut job = build_nix(
50 platform,
51 arch,
52 "default",
53 // don't push PR builds to the cache
54 Some("-zed-editor-[0-9.]*"),
55 &[],
56 );
57 job.job = job.job.cond(Expression::new(format!(
58 "{} && ((github.event.action == 'labeled' && github.event.label.name == 'run-bundling') || \
59 (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling')))",
60 DEFAULT_REPOSITORY_OWNER_GUARD
61 )));
62 job
63}
64
65fn bundle_job(deps: &[&NamedJob]) -> Job {
66 dependant_job(deps)
67 .when(deps.len() == 0, |job|
68 job.cond(Expression::new(
69 indoc! {
70 r#"(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
71 (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))"#,
72 })))
73 .timeout_minutes(60u32)
74}
75
76pub(crate) fn bundle_mac(
77 arch: Arch,
78 release_channel: Option<ReleaseChannel>,
79 deps: &[&NamedJob],
80) -> NamedJob {
81 pub fn bundle_mac(arch: Arch) -> Step<Run> {
82 named::bash(&format!("./script/bundle-mac {arch}-apple-darwin"))
83 }
84 let platform = Platform::Mac;
85 let artifact_name = match arch {
86 Arch::X86_64 => assets::MAC_X86_64,
87 Arch::AARCH64 => assets::MAC_AARCH64,
88 };
89 let remote_server_artifact_name = match arch {
90 Arch::X86_64 => assets::REMOTE_SERVER_MAC_X86_64,
91 Arch::AARCH64 => assets::REMOTE_SERVER_MAC_AARCH64,
92 };
93 NamedJob {
94 name: format!("bundle_mac_{arch}"),
95 job: bundle_job(deps)
96 .runs_on(runners::MAC_DEFAULT)
97 .envs(bundle_envs(platform))
98 .add_step(steps::checkout_repo())
99 .when_some(release_channel, |job, release_channel| {
100 job.add_step(set_release_channel(platform, release_channel))
101 })
102 .add_step(steps::setup_node())
103 .add_step(steps::setup_sentry())
104 .add_step(steps::clear_target_dir_if_large(runners::Platform::Mac))
105 .add_step(bundle_mac(arch))
106 .add_step(upload_artifact(&format!(
107 "target/{arch}-apple-darwin/release/{artifact_name}"
108 )))
109 .add_step(upload_artifact(&format!(
110 "target/{remote_server_artifact_name}"
111 ))),
112 }
113}
114
115pub fn upload_artifact(path: &str) -> Step<Use> {
116 let name = Path::new(path).file_name().unwrap().to_str().unwrap();
117 Step::new(format!("@actions/upload-artifact {}", name))
118 .uses(
119 "actions",
120 "upload-artifact",
121 "330a01c490aca151604b8cf639adc76d48f6c5d4", // v5
122 )
123 // N.B. "name" is the name for the asset. The uploaded
124 // file retains its filename.
125 .add_with(("name", name))
126 .add_with(("path", path))
127 .add_with(("if-no-files-found", "error"))
128}
129
130pub(crate) fn bundle_linux(
131 arch: Arch,
132 release_channel: Option<ReleaseChannel>,
133 deps: &[&NamedJob],
134) -> NamedJob {
135 let platform = Platform::Linux;
136 let artifact_name = match arch {
137 Arch::X86_64 => assets::LINUX_X86_64,
138 Arch::AARCH64 => assets::LINUX_AARCH64,
139 };
140 let remote_server_artifact_name = match arch {
141 Arch::X86_64 => assets::REMOTE_SERVER_LINUX_X86_64,
142 Arch::AARCH64 => assets::REMOTE_SERVER_LINUX_AARCH64,
143 };
144 NamedJob {
145 name: format!("bundle_linux_{arch}"),
146 job: bundle_job(deps)
147 .runs_on(arch.linux_bundler())
148 .envs(bundle_envs(platform))
149 .add_step(steps::checkout_repo())
150 .when_some(release_channel, |job, release_channel| {
151 job.add_step(set_release_channel(platform, release_channel))
152 })
153 .add_step(steps::setup_sentry())
154 .map(steps::install_linux_dependencies)
155 .add_step(steps::script("./script/bundle-linux"))
156 .add_step(upload_artifact(&format!("target/release/{artifact_name}")))
157 .add_step(upload_artifact(&format!(
158 "target/{remote_server_artifact_name}"
159 ))),
160 }
161}
162
163pub(crate) fn bundle_windows(
164 arch: Arch,
165 release_channel: Option<ReleaseChannel>,
166 deps: &[&NamedJob],
167) -> NamedJob {
168 let platform = Platform::Windows;
169 pub fn bundle_windows(arch: Arch) -> Step<Run> {
170 let step = match arch {
171 Arch::X86_64 => named::pwsh("script/bundle-windows.ps1 -Architecture x86_64"),
172 Arch::AARCH64 => named::pwsh("script/bundle-windows.ps1 -Architecture aarch64"),
173 };
174 step.working_directory("${{ env.ZED_WORKSPACE }}")
175 }
176 let artifact_name = match arch {
177 Arch::X86_64 => assets::WINDOWS_X86_64,
178 Arch::AARCH64 => assets::WINDOWS_AARCH64,
179 };
180 let remote_server_artifact_name = match arch {
181 Arch::X86_64 => assets::REMOTE_SERVER_WINDOWS_X86_64,
182 Arch::AARCH64 => assets::REMOTE_SERVER_WINDOWS_AARCH64,
183 };
184 NamedJob {
185 name: format!("bundle_windows_{arch}"),
186 job: bundle_job(deps)
187 .runs_on(runners::WINDOWS_DEFAULT)
188 .envs(bundle_envs(platform))
189 .add_step(steps::checkout_repo())
190 .when_some(release_channel, |job, release_channel| {
191 job.add_step(set_release_channel(platform, release_channel))
192 })
193 .add_step(steps::setup_sentry())
194 .add_step(bundle_windows(arch))
195 .add_step(upload_artifact(&format!("target/{artifact_name}")))
196 .add_step(upload_artifact(&format!(
197 "target/{remote_server_artifact_name}"
198 ))),
199 }
200}
201
202fn set_release_channel(platform: Platform, release_channel: ReleaseChannel) -> Step<Run> {
203 match release_channel {
204 ReleaseChannel::Nightly => set_release_channel_to_nightly(platform),
205 }
206}
207
208fn set_release_channel_to_nightly(platform: Platform) -> Step<Run> {
209 match platform {
210 Platform::Linux | Platform::Mac => named::bash(indoc::indoc! {r#"
211 set -eu
212 version=$(git rev-parse --short HEAD)
213 echo "Publishing version: ${version} on release channel nightly"
214 echo "nightly" > crates/zed/RELEASE_CHANNEL
215 "#}),
216 Platform::Windows => named::pwsh(indoc::indoc! {r#"
217 $ErrorActionPreference = "Stop"
218 $version = git rev-parse --short HEAD
219 Write-Host "Publishing version: $version on release channel nightly"
220 "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
221 "#})
222 .working_directory("${{ env.ZED_WORKSPACE }}"),
223 }
224}