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!(
179 "{} preview for @${{{{ github.actor }}}}",
180 outputs.preview_branch
181 ),
182 &outputs.preview_branch,
183 &token,
184 )
185 .with_files("crates/zed/RELEASE_CHANNEL")
186 .into();
187 let commit_sha = StepOutput::new_unchecked(&commit_step, "commit");
188
189 named::job(
190 Job::default()
191 .cond(Expression::new(format!(
192 "{} == 'all' || {} == 'preview'",
193 target.expr(),
194 target.expr(),
195 )))
196 .needs(vec![versions_job.name.clone()])
197 .runs_on(runners::LINUX_DEFAULT)
198 .add_step(authenticate)
199 .add_step(steps::checkout_repo().with_token(&token).with_ref("main"))
200 .add_step(main_sha_step)
201 .add_step(promote_to_preview())
202 .add_step(steps::create_ref(
203 steps::GitRef::branch(&outputs.preview_branch),
204 &main_sha,
205 &token,
206 ))
207 .add_step(commit_step)
208 .add_step(steps::create_ref(
209 steps::GitRef::tag(&outputs.preview_tag),
210 &commit_sha,
211 &token,
212 )),
213 )
214}
215
216fn promote_to_stable(
217 target: &WorkflowInput,
218 versions_job: &steps::NamedJob,
219 outputs: &ResolvedOutputs,
220) -> steps::NamedJob {
221 let (authenticate, token) = steps::authenticate_as_zippy().into();
222
223 let read_version_step = named::bash(indoc::indoc! {r#"
224 stable_version=$(script/get-crate-version zed)
225 {
226 echo "stable_tag=v${stable_version}"
227 } >> "$GITHUB_OUTPUT"
228 "#})
229 .id("stable-info");
230 let stable_tag = StepOutput::new(&read_version_step, "stable_tag");
231
232 let write_channel = named::bash("echo -n stable > crates/zed/RELEASE_CHANNEL");
233
234 let commit_step: Step<Use> = steps::BotCommitStep::new(
235 format!(
236 "{} stable for @${{{{ github.actor }}}}",
237 outputs.stable_branch
238 ),
239 &outputs.stable_branch,
240 &token,
241 )
242 .with_files("crates/zed/RELEASE_CHANNEL")
243 .into();
244 let commit_sha = StepOutput::new_unchecked(&commit_step, "commit");
245
246 named::job(
247 Job::default()
248 .cond(Expression::new(format!(
249 "{} == 'all' || {} == 'stable'",
250 target.expr(),
251 target.expr(),
252 )))
253 .needs(vec![versions_job.name.clone()])
254 .runs_on(runners::LINUX_DEFAULT)
255 .add_step(authenticate)
256 .add_step(
257 steps::checkout_repo()
258 .with_token(&token)
259 .with_ref(outputs.stable_branch.to_string()),
260 )
261 .add_step(read_version_step)
262 .add_step(write_channel)
263 .add_step(commit_step)
264 .add_step(steps::create_ref(
265 steps::GitRef::tag(&stable_tag),
266 &commit_sha,
267 &token,
268 )),
269 )
270}