bump_zed_version.rs

  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}