1use gh_workflow::{Event, Expression, Push, Run, Step, Use, Workflow};
2
3use crate::tasks::workflows::{
4 run_bundling::{bundle_linux, bundle_mac, bundle_windows},
5 run_tests,
6 runners::{self, Arch},
7 steps::{self, FluentBuilder, NamedJob, dependant_job, named, release_job},
8 vars,
9};
10
11pub(crate) fn release() -> Workflow {
12 let macos_tests = run_tests::run_platform_tests(runners::Platform::Mac);
13 let linux_tests = run_tests::run_platform_tests(runners::Platform::Linux);
14 let windows_tests = run_tests::run_platform_tests(runners::Platform::Windows);
15 let check_scripts = run_tests::check_scripts();
16
17 let create_draft_release = create_draft_release();
18
19 let bundle = ReleaseBundleJobs {
20 linux_aarch64: bundle_linux(Arch::AARCH64, None, &[&linux_tests, &check_scripts]),
21 linux_x86_64: bundle_linux(Arch::X86_64, None, &[&linux_tests, &check_scripts]),
22 mac_aarch64: bundle_mac(Arch::AARCH64, None, &[&macos_tests, &check_scripts]),
23 mac_x86_64: bundle_mac(Arch::X86_64, None, &[&macos_tests, &check_scripts]),
24 windows_aarch64: bundle_windows(Arch::AARCH64, None, &[&windows_tests, &check_scripts]),
25 windows_x86_64: bundle_windows(Arch::X86_64, None, &[&windows_tests, &check_scripts]),
26 };
27
28 let upload_release_assets = upload_release_assets(&[&create_draft_release], &bundle);
29
30 let auto_release_preview = auto_release_preview(&[&upload_release_assets]);
31
32 named::workflow()
33 .on(Event::default().push(Push::default().tags(vec!["v*".to_string()])))
34 .concurrency(vars::one_workflow_per_non_main_branch())
35 .add_env(("CARGO_TERM_COLOR", "always"))
36 .add_env(("RUST_BACKTRACE", "1"))
37 .add_job(macos_tests.name, macos_tests.job)
38 .add_job(linux_tests.name, linux_tests.job)
39 .add_job(windows_tests.name, windows_tests.job)
40 .add_job(check_scripts.name, check_scripts.job)
41 .add_job(create_draft_release.name, create_draft_release.job)
42 .map(|mut workflow| {
43 for job in bundle.into_jobs() {
44 workflow = workflow.add_job(job.name, job.job);
45 }
46 workflow
47 })
48 .add_job(upload_release_assets.name, upload_release_assets.job)
49 .add_job(auto_release_preview.name, auto_release_preview.job)
50}
51
52pub(crate) struct ReleaseBundleJobs {
53 pub linux_aarch64: NamedJob,
54 pub linux_x86_64: NamedJob,
55 pub mac_aarch64: NamedJob,
56 pub mac_x86_64: NamedJob,
57 pub windows_aarch64: NamedJob,
58 pub windows_x86_64: NamedJob,
59}
60
61impl ReleaseBundleJobs {
62 pub fn jobs(&self) -> Vec<&NamedJob> {
63 vec![
64 &self.linux_aarch64,
65 &self.linux_x86_64,
66 &self.mac_aarch64,
67 &self.mac_x86_64,
68 &self.windows_aarch64,
69 &self.windows_x86_64,
70 ]
71 }
72
73 pub fn into_jobs(self) -> Vec<NamedJob> {
74 vec![
75 self.linux_aarch64,
76 self.linux_x86_64,
77 self.mac_aarch64,
78 self.mac_x86_64,
79 self.windows_aarch64,
80 self.windows_x86_64,
81 ]
82 }
83}
84
85fn auto_release_preview(deps: &[&NamedJob; 1]) -> NamedJob {
86 fn create_sentry_release() -> Step<Use> {
87 named::uses(
88 "getsentry",
89 "action-release",
90 "526942b68292201ac6bbb99b9a0747d4abee354c", // v3
91 )
92 .add_env(("SENTRY_ORG", "zed-dev"))
93 .add_env(("SENTRY_PROJECT", "zed"))
94 .add_env(("SENTRY_AUTH_TOKEN", "${{ secrets.SENTRY_AUTH_TOKEN }}"))
95 .add_with(("environment", "production"))
96 }
97
98 named::job(
99 dependant_job(deps)
100 .runs_on(runners::LINUX_SMALL)
101 .cond(Expression::new(indoc::indoc!(
102 r#"
103 false
104 && startsWith(github.ref, 'refs/tags/v')
105 && endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
106 "# // todo(ci-release) enable
107 )))
108 .add_step(
109 steps::script(
110 r#"gh release edit "$GITHUB_REF_NAME" --repo=zed-industries/zed --draft=false"#,
111 )
112 .add_env(("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")),
113 )
114 .add_step(create_sentry_release()),
115 )
116}
117
118pub(crate) fn download_workflow_artifacts() -> Step<Use> {
119 named::uses(
120 "actions",
121 "download-artifact",
122 "018cc2cf5baa6db3ef3c5f8a56943fffe632ef53", // v6.0.0
123 )
124 .add_with(("path", "./artifacts/"))
125}
126
127pub(crate) fn prep_release_artifacts(bundle: &ReleaseBundleJobs) -> Step<Run> {
128 let assets = [
129 (&bundle.mac_x86_64.name, "zed", "Zed-x86_64.dmg"),
130 (&bundle.mac_aarch64.name, "zed", "Zed-aarch64.dmg"),
131 (&bundle.windows_x86_64.name, "zed", "Zed-x86_64.exe"),
132 (&bundle.windows_aarch64.name, "zed", "Zed-aarch64.exe"),
133 (
134 &bundle.linux_aarch64.name,
135 "zed",
136 "zed-linux-aarch64.tar.gz",
137 ),
138 (&bundle.linux_x86_64.name, "zed", "zed-linux-x86_64.tar.gz"),
139 (
140 &bundle.linux_x86_64.name,
141 "remote-server",
142 "zed-remote-server-linux-x86_64.gz",
143 ),
144 (
145 &bundle.linux_aarch64.name,
146 "remote-server",
147 "zed-remote-server-linux-aarch64.gz",
148 ),
149 (
150 &bundle.mac_x86_64.name,
151 "remote-server",
152 "zed-remote-server-macos-x86_64.gz",
153 ),
154 (
155 &bundle.mac_aarch64.name,
156 "remote-server",
157 "zed-remote-server-macos-aarch64.gz",
158 ),
159 ];
160
161 let mut script_lines = vec!["mkdir -p release-artifacts/\n".to_string()];
162 for (job_name, artifact_kind, release_artifact_name) in assets {
163 let artifact_path = ["${{ needs.", job_name, ".outputs.", artifact_kind, " }}"].join("");
164 let mv_command =
165 format!("mv ./artifacts/{artifact_path}/* release-artifacts/{release_artifact_name}");
166 script_lines.push(mv_command)
167 }
168
169 named::bash(&script_lines.join("\n"))
170}
171
172fn upload_release_assets(deps: &[&NamedJob], bundle: &ReleaseBundleJobs) -> NamedJob {
173 let mut deps = deps.to_vec();
174 deps.extend(bundle.jobs());
175
176 named::job(
177 dependant_job(&deps)
178 .runs_on(runners::LINUX_MEDIUM)
179 .add_step(download_workflow_artifacts())
180 .add_step(steps::script("ls -lR ./artifacts"))
181 .add_step(prep_release_artifacts(bundle))
182 .add_step(
183 steps::script("gh release upload \"$GITHUB_REF_NAME\" --repo=zed-industries/zed release-artifacts/*")
184 .add_env(("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")),
185 ),
186 )
187}
188
189fn create_draft_release() -> NamedJob {
190 fn generate_release_notes() -> Step<Run> {
191 named::bash(
192 r#"node --redirect-warnings=/dev/null ./script/draft-release-notes "$RELEASE_VERSION" "$RELEASE_CHANNEL" > target/release-notes.md"#,
193 )
194 }
195
196 fn create_release() -> Step<Run> {
197 named::bash("script/create-draft-release target/release-notes.md")
198 .add_env(("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}"))
199 }
200
201 named::job(
202 release_job(&[])
203 .runs_on(runners::LINUX_SMALL)
204 // We need to fetch more than one commit so that `script/draft-release-notes`
205 // is able to diff between the current and previous tag.
206 //
207 // 25 was chosen arbitrarily.
208 .add_step(
209 steps::checkout_repo()
210 .add_with(("fetch-depth", 25))
211 .add_with(("clean", false))
212 .add_with(("ref", "${{ github.ref }}")),
213 )
214 .add_step(steps::script("script/determine-release-channel"))
215 .add_step(steps::script("mkdir -p target/"))
216 .add_step(generate_release_notes())
217 .add_step(create_release()),
218 )
219}