1use gh_workflow::*;
2
3use crate::tasks::workflows::{
4 release::{self, notify_on_failure},
5 runners,
6 steps::{CommonJobConditions, NamedJob, checkout_repo, dependant_job, named},
7 vars::{self, StepOutput, WorkflowInput},
8};
9
10const TAG_NAME: &str = "${{ github.event.release.tag_name || inputs.tag_name }}";
11const IS_PRERELEASE: &str = "${{ github.event.release.prerelease || inputs.prerelease }}";
12const RELEASE_BODY: &str = "${{ github.event.release.body || inputs.body }}";
13
14pub fn after_release() -> Workflow {
15 let tag_name = WorkflowInput::string("tag_name", None);
16 let prerelease = WorkflowInput::bool("prerelease", None);
17 let body = WorkflowInput::string("body", Some(String::new()));
18
19 let refresh_zed_dev = rebuild_releases_page();
20 let post_to_discord = post_to_discord(&[&refresh_zed_dev]);
21 let publish_winget = publish_winget();
22 let create_sentry_release = create_sentry_release();
23 let notify_on_failure = notify_on_failure(&[
24 &refresh_zed_dev,
25 &post_to_discord,
26 &publish_winget,
27 &create_sentry_release,
28 ]);
29
30 named::workflow()
31 .on(Event::default()
32 .release(Release::default().types(vec![ReleaseType::Published]))
33 .workflow_dispatch(
34 WorkflowDispatch::default()
35 .add_input(tag_name.name, tag_name.input())
36 .add_input(prerelease.name, prerelease.input())
37 .add_input(body.name, body.input()),
38 ))
39 .add_job(refresh_zed_dev.name, refresh_zed_dev.job)
40 .add_job(post_to_discord.name, post_to_discord.job)
41 .add_job(publish_winget.name, publish_winget.job)
42 .add_job(create_sentry_release.name, create_sentry_release.job)
43 .add_job(notify_on_failure.name, notify_on_failure.job)
44}
45
46fn rebuild_releases_page() -> NamedJob {
47 fn refresh_cloud_releases() -> Step<Run> {
48 named::bash(format!(
49 "curl -fX POST https://cloud.zed.dev/releases/refresh?expect_tag={TAG_NAME}"
50 ))
51 }
52
53 fn redeploy_zed_dev() -> Step<Run> {
54 named::bash("npm exec --yes -- vercel@37 --token=\"$VERCEL_TOKEN\" --scope zed-industries redeploy https://zed.dev")
55 .add_env(("VERCEL_TOKEN", vars::VERCEL_TOKEN))
56 }
57
58 named::job(
59 Job::default()
60 .runs_on(runners::LINUX_SMALL)
61 .with_repository_owner_guard()
62 .add_step(refresh_cloud_releases())
63 .add_step(redeploy_zed_dev()),
64 )
65}
66
67fn post_to_discord(deps: &[&NamedJob]) -> NamedJob {
68 fn get_release_url() -> Step<Run> {
69 named::bash(format!(
70 r#"if [ "{IS_PRERELEASE}" == "true" ]; then
71 URL="https://zed.dev/releases/preview"
72else
73 URL="https://zed.dev/releases/stable"
74fi
75
76echo "URL=$URL" >> "$GITHUB_OUTPUT"
77"#
78 ))
79 .id("get-release-url")
80 }
81
82 fn get_content() -> Step<Use> {
83 named::uses(
84 "2428392",
85 "gh-truncate-string-action",
86 "b3ff790d21cf42af3ca7579146eedb93c8fb0757", // v1.4.1
87 )
88 .id("get-content")
89 .add_with((
90 "stringToTruncate",
91 format!(
92 "📣 Zed [{TAG_NAME}](<${{{{ steps.get-release-url.outputs.URL }}}}>) was just released!\n\n{RELEASE_BODY}\n"
93 ),
94 ))
95 .add_with(("maxLength", 2000))
96 .add_with(("truncationSymbol", "..."))
97 }
98
99 fn discord_webhook_action() -> Step<Use> {
100 named::uses(
101 "tsickert",
102 "discord-webhook",
103 "c840d45a03a323fbc3f7507ac7769dbd91bfb164", // v5.3.0
104 )
105 .add_with(("webhook-url", vars::DISCORD_WEBHOOK_RELEASE_NOTES))
106 .add_with(("content", "${{ steps.get-content.outputs.string }}"))
107 }
108 let job = dependant_job(deps)
109 .runs_on(runners::LINUX_SMALL)
110 .with_repository_owner_guard()
111 .add_step(get_release_url())
112 .add_step(get_content())
113 .add_step(discord_webhook_action());
114 named::job(job)
115}
116
117fn publish_winget() -> NamedJob {
118 fn set_package_name() -> (Step<Run>, StepOutput) {
119 let script = format!(
120 r#"if ("{IS_PRERELEASE}" -eq "true") {{
121 $PACKAGE_NAME = "ZedIndustries.Zed.Preview"
122}} else {{
123 $PACKAGE_NAME = "ZedIndustries.Zed"
124}}
125
126echo "PACKAGE_NAME=$PACKAGE_NAME" >> $env:GITHUB_OUTPUT
127"#
128 );
129 let step = named::pwsh(&script).id("set-package-name");
130
131 let output = StepOutput::new(&step, "PACKAGE_NAME");
132 (step, output)
133 }
134
135 fn winget_releaser(package_name: &StepOutput) -> Step<Use> {
136 named::uses(
137 "vedantmgoyal9",
138 "winget-releaser",
139 "19e706d4c9121098010096f9c495a70a7518b30f", // v2
140 )
141 .add_with(("identifier", package_name.to_string()))
142 .add_with(("release-tag", TAG_NAME))
143 .add_with(("max-versions-to-keep", 5))
144 .add_with(("token", vars::WINGET_TOKEN))
145 }
146
147 let (set_package_name, package_name) = set_package_name();
148
149 named::job(
150 Job::default()
151 .runs_on(runners::WINDOWS_DEFAULT)
152 .add_step(set_package_name)
153 .add_step(winget_releaser(&package_name)),
154 )
155}
156
157fn create_sentry_release() -> NamedJob {
158 let job = Job::default()
159 .runs_on(runners::LINUX_SMALL)
160 .with_repository_owner_guard()
161 .add_step(checkout_repo())
162 .add_step(release::create_sentry_release());
163 named::job(job)
164}