use gh_workflow::*; use crate::tasks::workflows::{ runners, steps::{self, CommonPermissionSets, named}, vars::{self, StepOutput, WorkflowInput}, }; pub fn bump_zed_version() -> Workflow { let target = WorkflowInput::string("target", Some("all".to_string())) .description("Which channels to bump: all, main, preview, or stable"); let (versions_job, outputs) = resolve_versions(); let bump_main_job = bump_main(&target, &versions_job, &outputs); let preview_job = create_preview_branch(&target, &versions_job, &outputs); let stable_job = promote_to_stable(&target, &versions_job, &outputs); named::workflow() .with_minimal_permissions() .on(Event::default() .workflow_dispatch(WorkflowDispatch::default().add_input(target.name, target.input()))) .add_job(versions_job.name, versions_job.job) .add_job(bump_main_job.name, bump_main_job.job) .add_job(preview_job.name, preview_job.job) .add_job(stable_job.name, stable_job.job) } struct ResolvedOutputs { next_version: vars::JobOutput, pr_branch: vars::JobOutput, preview_branch: vars::JobOutput, preview_tag: vars::JobOutput, stable_branch: vars::JobOutput, } fn resolve_versions() -> (steps::NamedJob, ResolvedOutputs) { fn extract_versions() -> Step { named::bash(indoc::indoc! {r#" version=$(script/get-crate-version zed) major=$(echo "$version" | cut -d. -f1) minor=$(echo "$version" | cut -d. -f2) channel=$(cat crates/zed/RELEASE_CHANNEL) if [[ "$channel" != "dev" && "$channel" != "nightly" ]]; then echo "::error::release channel on main should be dev or nightly, found: $channel" exit 1 fi # Next main version after bump next_version="${major}.$((minor + 1)).0" next_major=$(echo "$next_version" | cut -d. -f1) next_minor=$(echo "$next_version" | cut -d. -f2) pr_branch="bump-zed-to-v${next_major}.${next_minor}.0" # New preview branch from current main preview_branch="v${major}.${minor}.x" preview_tag="v${version}-pre" # Current preview to promote to stable — derive branch from released preview version released_preview=$(script/get-released-version preview) if [[ -z "$released_preview" ]]; then echo "::error::could not determine released preview version" exit 1 fi stable_major=$(echo "$released_preview" | cut -d. -f1) stable_minor=$(echo "$released_preview" | cut -d. -f2) stable_branch="v${stable_major}.${stable_minor}.x" # Final validation for var in next_version pr_branch preview_branch preview_tag stable_branch; do if [[ -z "${!var}" ]]; then echo "::error::failed to compute $var" exit 1 fi done { echo "next_version=$next_version" echo "pr_branch=$pr_branch" echo "preview_branch=$preview_branch" echo "preview_tag=$preview_tag" echo "stable_branch=$stable_branch" } >> "$GITHUB_OUTPUT" echo "Resolved: next=$next_version preview=$preview_branch($preview_tag) stable=$stable_branch pr=$pr_branch" "#}) .id("versions") } let (authenticate, token) = steps::authenticate_as_zippy() .for_repository(steps::RepositoryTarget::current()) .with_permissions([(steps::TokenPermissions::Contents, Level::Read)]) .into(); let versions_step = extract_versions(); let next_version = StepOutput::new(&versions_step, "next_version"); let pr_branch = StepOutput::new(&versions_step, "pr_branch"); let preview_branch = StepOutput::new(&versions_step, "preview_branch"); let preview_tag = StepOutput::new(&versions_step, "preview_tag"); let stable_branch = StepOutput::new(&versions_step, "stable_branch"); let job = named::job( Job::default() .cond(Expression::new( "github.repository_owner == 'zed-industries'", )) .runs_on(runners::LINUX_XL) .add_step(authenticate) .add_step(steps::checkout_repo().with_token(&token).with_ref("main")) .add_step(versions_step) .outputs([ (next_version.name.to_owned(), next_version.to_string()), (pr_branch.name.to_owned(), pr_branch.to_string()), (preview_branch.name.to_owned(), preview_branch.to_string()), (preview_tag.name.to_owned(), preview_tag.to_string()), (stable_branch.name.to_owned(), stable_branch.to_string()), ]), ); let outputs = ResolvedOutputs { next_version: next_version.as_job_output(&job), pr_branch: pr_branch.as_job_output(&job), preview_branch: preview_branch.as_job_output(&job), preview_tag: preview_tag.as_job_output(&job), stable_branch: stable_branch.as_job_output(&job), }; (job, outputs) } fn bump_main( target: &WorkflowInput, versions_job: &steps::NamedJob, outputs: &ResolvedOutputs, ) -> steps::NamedJob { fn bump_version() -> Step { named::bash("cargo set-version -p zed --bump minor") } let (authenticate, token) = steps::authenticate_as_zippy() .for_repository(steps::RepositoryTarget::current()) .with_permissions([ (steps::TokenPermissions::Contents, Level::Write), (steps::TokenPermissions::PullRequests, Level::Write), ]) .into(); named::job( Job::default() .cond(Expression::new(format!( "{} == 'all' || {} == 'main'", target.expr(), target.expr(), ))) .permissions( Permissions::default() .contents(Level::Write) .pull_requests(Level::Write), ) .needs(vec![versions_job.name.clone()]) .runs_on(runners::LINUX_DEFAULT) .add_step(authenticate) .add_step(steps::checkout_repo().with_token(&token).with_ref("main")) .add_step(steps::install_cargo_edit()) .add_step(bump_version()) .add_step(steps::CreatePrStep::new( format!("Bump Zed to v{}", outputs.next_version), &outputs.pr_branch, &token, )), ) } fn create_preview_branch( target: &WorkflowInput, versions_job: &steps::NamedJob, outputs: &ResolvedOutputs, ) -> steps::NamedJob { fn promote_to_preview() -> Step { named::bash("echo -n preview > crates/zed/RELEASE_CHANNEL") } fn get_main_sha() -> Step { named::bash("echo \"main_sha=$(git rev-parse HEAD)\" >> \"$GITHUB_OUTPUT\"").id("main-sha") } let (authenticate, token) = steps::authenticate_as_zippy() .for_repository(steps::RepositoryTarget::current()) .with_permissions([ (steps::TokenPermissions::Contents, Level::Write), (steps::TokenPermissions::Workflows, Level::Write), ]) .into(); let main_sha_step = get_main_sha(); let main_sha = StepOutput::new(&main_sha_step, "main_sha"); let commit_step: Step = steps::BotCommitStep::new( format!( "{} preview for @${{{{ github.actor }}}}", outputs.preview_branch ), &outputs.preview_branch, &token, ) .with_files("crates/zed/RELEASE_CHANNEL") .into(); let commit_sha = StepOutput::new_unchecked(&commit_step, "commit"); named::job( Job::default() .cond(Expression::new(format!( "{} == 'all' || {} == 'preview'", target.expr(), target.expr(), ))) .permissions(Permissions::default().contents(Level::Write)) .needs(vec![versions_job.name.clone()]) .runs_on(runners::LINUX_DEFAULT) .add_step(authenticate) .add_step(steps::checkout_repo().with_token(&token).with_ref("main")) .add_step(main_sha_step) .add_step(promote_to_preview()) .add_step(steps::create_ref( steps::GitRef::branch(&outputs.preview_branch), &main_sha, &token, )) .add_step(commit_step) .add_step(steps::create_ref( steps::GitRef::tag(&outputs.preview_tag), &commit_sha, &token, )), ) } fn promote_to_stable( target: &WorkflowInput, versions_job: &steps::NamedJob, outputs: &ResolvedOutputs, ) -> steps::NamedJob { let (authenticate, token) = steps::authenticate_as_zippy() .for_repository(steps::RepositoryTarget::current()) .with_permissions([ (steps::TokenPermissions::Contents, Level::Write), (steps::TokenPermissions::Workflows, Level::Write), ]) .into(); let read_version_step = named::bash(indoc::indoc! {r#" stable_version=$(script/get-crate-version zed) { echo "stable_tag=v${stable_version}" } >> "$GITHUB_OUTPUT" "#}) .id("stable-info"); let stable_tag = StepOutput::new(&read_version_step, "stable_tag"); let write_channel = named::bash("echo -n stable > crates/zed/RELEASE_CHANNEL"); let commit_step: Step = steps::BotCommitStep::new( format!( "{} stable for @${{{{ github.actor }}}}", outputs.stable_branch ), &outputs.stable_branch, &token, ) .with_files("crates/zed/RELEASE_CHANNEL") .into(); let commit_sha = StepOutput::new_unchecked(&commit_step, "commit"); named::job( Job::default() .cond(Expression::new(format!( "{} == 'all' || {} == 'stable'", target.expr(), target.expr(), ))) .permissions(Permissions::default().contents(Level::Write)) .needs(vec![versions_job.name.clone()]) .runs_on(runners::LINUX_DEFAULT) .add_step(authenticate) .add_step( steps::checkout_repo() .with_token(&token) .with_ref(outputs.stable_branch.to_string()), ) .add_step(read_version_step) .add_step(write_channel) .add_step(commit_step) .add_step(steps::create_ref( steps::GitRef::tag(&stable_tag), &commit_sha, &token, )), ) }