release.rs

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