1use gh_workflow::*;
2
3use crate::tasks::workflows::{
4 runners,
5 steps::{self, named},
6 vars::{self, StepOutput, WorkflowInput},
7};
8
9pub fn bump_zed_version() -> Workflow {
10 let target = WorkflowInput::string("target", Some("all".to_string()))
11 .description("Which channels to bump: all, main, preview, or stable");
12
13 let (versions_job, outputs) = resolve_versions();
14
15 let bump_main_job = bump_main(&target, &versions_job, &outputs);
16 let preview_job = create_preview_branch(&target, &versions_job, &outputs);
17 let stable_job = promote_to_stable(&target, &versions_job, &outputs);
18
19 named::workflow()
20 .on(Event::default()
21 .workflow_dispatch(WorkflowDispatch::default().add_input(target.name, target.input())))
22 .add_job(versions_job.name, versions_job.job)
23 .add_job(bump_main_job.name, bump_main_job.job)
24 .add_job(preview_job.name, preview_job.job)
25 .add_job(stable_job.name, stable_job.job)
26}
27
28struct ResolvedOutputs {
29 next_version: vars::JobOutput,
30 pr_branch: vars::JobOutput,
31 preview_branch: vars::JobOutput,
32 preview_tag: vars::JobOutput,
33 stable_branch: vars::JobOutput,
34}
35
36fn resolve_versions() -> (steps::NamedJob, ResolvedOutputs) {
37 fn extract_versions() -> Step<Run> {
38 named::bash(indoc::indoc! {r#"
39 version=$(script/get-crate-version zed)
40 major=$(echo "$version" | cut -d. -f1)
41 minor=$(echo "$version" | cut -d. -f2)
42
43 channel=$(cat crates/zed/RELEASE_CHANNEL)
44 if [[ "$channel" != "dev" && "$channel" != "nightly" ]]; then
45 echo "::error::release channel on main should be dev or nightly, found: $channel"
46 exit 1
47 fi
48
49 # Next main version after bump
50 next_version="${major}.$((minor + 1)).0"
51 next_major=$(echo "$next_version" | cut -d. -f1)
52 next_minor=$(echo "$next_version" | cut -d. -f2)
53 pr_branch="bump-zed-to-v${next_major}.${next_minor}.0"
54
55 # New preview branch from current main
56 preview_branch="v${major}.${minor}.x"
57 preview_tag="v${version}-pre"
58
59 # Current preview to promote to stable — derive branch from released preview version
60 released_preview=$(script/get-released-version preview)
61 if [[ -z "$released_preview" ]]; then
62 echo "::error::could not determine released preview version"
63 exit 1
64 fi
65 stable_major=$(echo "$released_preview" | cut -d. -f1)
66 stable_minor=$(echo "$released_preview" | cut -d. -f2)
67 stable_branch="v${stable_major}.${stable_minor}.x"
68
69 # Final validation
70 for var in next_version pr_branch preview_branch preview_tag stable_branch; do
71 if [[ -z "${!var}" ]]; then
72 echo "::error::failed to compute $var"
73 exit 1
74 fi
75 done
76
77 {
78 echo "next_version=$next_version"
79 echo "pr_branch=$pr_branch"
80 echo "preview_branch=$preview_branch"
81 echo "preview_tag=$preview_tag"
82 echo "stable_branch=$stable_branch"
83 } >> "$GITHUB_OUTPUT"
84
85 echo "Resolved: next=$next_version preview=$preview_branch($preview_tag) stable=$stable_branch pr=$pr_branch"
86 "#})
87 .id("versions")
88 }
89
90 let (authenticate, token) = steps::authenticate_as_zippy().into();
91 let versions_step = extract_versions();
92 let next_version = StepOutput::new(&versions_step, "next_version");
93 let pr_branch = StepOutput::new(&versions_step, "pr_branch");
94 let preview_branch = StepOutput::new(&versions_step, "preview_branch");
95 let preview_tag = StepOutput::new(&versions_step, "preview_tag");
96 let stable_branch = StepOutput::new(&versions_step, "stable_branch");
97
98 let job = named::job(
99 Job::default()
100 .cond(Expression::new(
101 "github.repository_owner == 'zed-industries'",
102 ))
103 .runs_on(runners::LINUX_XL)
104 .add_step(authenticate)
105 .add_step(steps::checkout_repo().with_token(&token).with_ref("main"))
106 .add_step(versions_step)
107 .outputs([
108 (next_version.name.to_owned(), next_version.to_string()),
109 (pr_branch.name.to_owned(), pr_branch.to_string()),
110 (preview_branch.name.to_owned(), preview_branch.to_string()),
111 (preview_tag.name.to_owned(), preview_tag.to_string()),
112 (stable_branch.name.to_owned(), stable_branch.to_string()),
113 ]),
114 );
115
116 let outputs = ResolvedOutputs {
117 next_version: next_version.as_job_output(&job),
118 pr_branch: pr_branch.as_job_output(&job),
119 preview_branch: preview_branch.as_job_output(&job),
120 preview_tag: preview_tag.as_job_output(&job),
121 stable_branch: stable_branch.as_job_output(&job),
122 };
123
124 (job, outputs)
125}
126
127fn bump_main(
128 target: &WorkflowInput,
129 versions_job: &steps::NamedJob,
130 outputs: &ResolvedOutputs,
131) -> steps::NamedJob {
132 fn bump_version() -> Step<Run> {
133 named::bash("cargo set-version -p zed --bump minor")
134 }
135
136 let (authenticate, token) = steps::authenticate_as_zippy().into();
137
138 named::job(
139 Job::default()
140 .cond(Expression::new(format!(
141 "{} == 'all' || {} == 'main'",
142 target.expr(),
143 target.expr(),
144 )))
145 .needs(vec![versions_job.name.clone()])
146 .runs_on(runners::LINUX_DEFAULT)
147 .add_step(authenticate)
148 .add_step(steps::checkout_repo().with_token(&token).with_ref("main"))
149 .add_step(steps::install_cargo_edit())
150 .add_step(bump_version())
151 .add_step(steps::CreatePrStep::new(
152 format!("Bump Zed to v{}", outputs.next_version),
153 &outputs.pr_branch,
154 &token,
155 )),
156 )
157}
158
159fn create_preview_branch(
160 target: &WorkflowInput,
161 versions_job: &steps::NamedJob,
162 outputs: &ResolvedOutputs,
163) -> steps::NamedJob {
164 fn promote_to_preview() -> Step<Run> {
165 named::bash("echo -n preview > crates/zed/RELEASE_CHANNEL")
166 }
167
168 fn get_main_sha() -> Step<Run> {
169 named::bash("echo \"main_sha=$(git rev-parse HEAD)\" >> \"$GITHUB_OUTPUT\"").id("main-sha")
170 }
171
172 let (authenticate, token) = steps::authenticate_as_zippy().into();
173
174 let main_sha_step = get_main_sha();
175 let main_sha = StepOutput::new(&main_sha_step, "main_sha");
176
177 let commit_step: Step<Use> = steps::BotCommitStep::new(
178 format!("{} preview", outputs.preview_branch),
179 &outputs.preview_branch,
180 &token,
181 )
182 .with_files("crates/zed/RELEASE_CHANNEL")
183 .into();
184 let commit_sha = StepOutput::new_unchecked(&commit_step, "commit");
185
186 named::job(
187 Job::default()
188 .cond(Expression::new(format!(
189 "{} == 'all' || {} == 'preview'",
190 target.expr(),
191 target.expr(),
192 )))
193 .needs(vec![versions_job.name.clone()])
194 .runs_on(runners::LINUX_DEFAULT)
195 .add_step(authenticate)
196 .add_step(steps::checkout_repo().with_token(&token).with_ref("main"))
197 .add_step(main_sha_step)
198 .add_step(promote_to_preview())
199 .add_step(steps::create_ref(
200 steps::GitRef::branch(&outputs.preview_branch),
201 &main_sha,
202 &token,
203 ))
204 .add_step(commit_step)
205 .add_step(steps::create_ref(
206 steps::GitRef::tag(&outputs.preview_tag),
207 &commit_sha,
208 &token,
209 )),
210 )
211}
212
213fn promote_to_stable(
214 target: &WorkflowInput,
215 versions_job: &steps::NamedJob,
216 outputs: &ResolvedOutputs,
217) -> steps::NamedJob {
218 let (authenticate, token) = steps::authenticate_as_zippy().into();
219
220 let read_version_step = named::bash(indoc::indoc! {r#"
221 stable_version=$(script/get-crate-version zed)
222 {
223 echo "stable_tag=v${stable_version}"
224 } >> "$GITHUB_OUTPUT"
225 "#})
226 .id("stable-info");
227 let stable_tag = StepOutput::new(&read_version_step, "stable_tag");
228
229 let write_channel = named::bash("echo -n stable > crates/zed/RELEASE_CHANNEL");
230
231 let commit_step: Step<Use> = steps::BotCommitStep::new(
232 format!("{} stable", outputs.stable_branch),
233 &outputs.stable_branch,
234 &token,
235 )
236 .with_files("crates/zed/RELEASE_CHANNEL")
237 .into();
238 let commit_sha = StepOutput::new_unchecked(&commit_step, "commit");
239
240 named::job(
241 Job::default()
242 .cond(Expression::new(format!(
243 "{} == 'all' || {} == 'stable'",
244 target.expr(),
245 target.expr(),
246 )))
247 .needs(vec![versions_job.name.clone()])
248 .runs_on(runners::LINUX_DEFAULT)
249 .add_step(authenticate)
250 .add_step(
251 steps::checkout_repo()
252 .with_token(&token)
253 .with_ref(outputs.stable_branch.to_string()),
254 )
255 .add_step(read_version_step)
256 .add_step(write_channel)
257 .add_step(commit_step)
258 .add_step(steps::create_ref(
259 steps::GitRef::tag(&stable_tag),
260 &commit_sha,
261 &token,
262 )),
263 )
264}