extension_bump.rs

  1use gh_workflow::*;
  2use indoc::indoc;
  3
  4use crate::tasks::workflows::{
  5    extension_tests::{self},
  6    runners,
  7    steps::{self, CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, NamedJob, named},
  8    vars::{
  9        JobOutput, StepOutput, WorkflowInput, WorkflowSecret, one_workflow_per_non_main_branch,
 10    },
 11};
 12
 13const BUMPVERSION_CONFIG: &str = indoc! {r#"
 14    [bumpversion]
 15    current_version = "$OLD_VERSION"
 16
 17    [bumpversion:file:Cargo.toml]
 18
 19    [bumpversion:file:extension.toml]
 20    "#
 21};
 22
 23const VERSION_CHECK: &str = r#"sed -n 's/version = \"\(.*\)\"/\1/p' < extension.toml"#;
 24
 25// This is used by various extensions repos in the zed-extensions org to bump extension versions.
 26pub(crate) fn extension_bump() -> Workflow {
 27    let bump_type = WorkflowInput::string("bump-type", Some("patch".to_owned()));
 28
 29    let app_id = WorkflowSecret::new("app-id", "The app ID used to create the PR");
 30    let app_secret =
 31        WorkflowSecret::new("app-secret", "The app secret for the corresponding app ID");
 32
 33    let test_extension = extension_tests::check_extension();
 34    let (check_bump_needed, needs_bump) = check_bump_needed();
 35    let bump_version = bump_extension_version(
 36        &[&test_extension, &check_bump_needed],
 37        &bump_type,
 38        needs_bump.as_job_output(&check_bump_needed),
 39        &app_id,
 40        &app_secret,
 41    );
 42
 43    named::workflow()
 44        .add_event(
 45            Event::default().workflow_call(
 46                WorkflowCall::default()
 47                    .add_input(bump_type.name, bump_type.call_input())
 48                    .secrets([
 49                        (app_id.name.to_owned(), app_id.secret_configuration()),
 50                        (
 51                            app_secret.name.to_owned(),
 52                            app_secret.secret_configuration(),
 53                        ),
 54                    ]),
 55            ),
 56        )
 57        .concurrency(one_workflow_per_non_main_branch())
 58        .add_env(("CARGO_TERM_COLOR", "always"))
 59        .add_env(("RUST_BACKTRACE", 1))
 60        .add_env(("CARGO_INCREMENTAL", 0))
 61        .add_env((
 62            "ZED_EXTENSION_CLI_SHA",
 63            extension_tests::ZED_EXTENSION_CLI_SHA,
 64        ))
 65        .add_job(test_extension.name, test_extension.job)
 66        .add_job(check_bump_needed.name, check_bump_needed.job)
 67        .add_job(bump_version.name, bump_version.job)
 68}
 69
 70fn check_bump_needed() -> (NamedJob, StepOutput) {
 71    let (compare_versions, version_changed) = compare_versions();
 72
 73    let job = Job::default()
 74        .with_repository_owner_guard()
 75        .outputs([(version_changed.name.to_owned(), version_changed.to_string())])
 76        .runs_on(runners::LINUX_SMALL)
 77        .timeout_minutes(1u32)
 78        .add_step(steps::checkout_repo().add_with(("fetch-depth", 10)))
 79        .add_step(compare_versions);
 80
 81    (named::job(job), version_changed)
 82}
 83
 84/// Compares the current and previous commit and checks whether versions changed inbetween.
 85fn compare_versions() -> (Step<Run>, StepOutput) {
 86    let check_needs_bump = named::bash(format!(
 87        indoc! {
 88            r#"
 89        CURRENT_VERSION="$({})"
 90
 91        git checkout "$(git log -1 --format=%H)"~1
 92
 93        PREV_COMMIT_VERSION="$({})"
 94
 95        [[ "$CURRENT_VERSION" == "$PREV_COMMIT_VERSION" ]] && \
 96          echo "needs_bump=true" >> "$GITHUB_OUTPUT" || \
 97          echo "needs_bump=false" >> "$GITHUB_OUTPUT"
 98
 99        "#
100        },
101        VERSION_CHECK, VERSION_CHECK
102    ))
103    .id("compare-versions-check");
104
105    let needs_bump = StepOutput::new(&check_needs_bump, "needs_bump");
106
107    (check_needs_bump, needs_bump)
108}
109
110fn bump_extension_version(
111    dependencies: &[&NamedJob],
112    bump_type: &WorkflowInput,
113    needs_bump: JobOutput,
114    app_id: &WorkflowSecret,
115    app_secret: &WorkflowSecret,
116) -> NamedJob {
117    let (generate_token, generated_token) = generate_token(app_id, app_secret);
118    let (bump_version, old_version, new_version) = bump_version(bump_type);
119
120    let job = steps::dependant_job(dependencies)
121        .cond(Expression::new(format!(
122            "{DEFAULT_REPOSITORY_OWNER_GUARD} && {} == 'true'",
123            needs_bump.expr(),
124        )))
125        .runs_on(runners::LINUX_LARGE)
126        .timeout_minutes(1u32)
127        .add_step(generate_token)
128        .add_step(steps::checkout_repo())
129        .add_step(install_bump_2_version())
130        .add_step(bump_version)
131        .add_step(create_pull_request(
132            old_version,
133            new_version,
134            generated_token,
135        ));
136
137    named::job(job)
138}
139
140fn generate_token(app_id: &WorkflowSecret, app_secret: &WorkflowSecret) -> (Step<Use>, StepOutput) {
141    let step = named::uses("actions", "create-github-app-token", "v2")
142        .id("generate-token")
143        .add_with(
144            Input::default()
145                .add("app-id", app_id.to_string())
146                .add("private-key", app_secret.to_string()),
147        );
148
149    let generated_token = StepOutput::new(&step, "token");
150
151    (step, generated_token)
152}
153
154fn install_bump_2_version() -> Step<Run> {
155    named::run(runners::Platform::Linux, "pip install bump2version")
156}
157
158fn bump_version(bump_type: &WorkflowInput) -> (Step<Run>, StepOutput, StepOutput) {
159    let step = named::bash(format!(
160        indoc! {r#"
161            OLD_VERSION="$({})"
162
163            cat <<EOF > .bumpversion.cfg
164            {}
165            EOF
166
167            bump2version --verbose {}
168            NEW_VERSION="$({})"
169            cargo update --workspace
170
171            rm .bumpversion.cfg
172
173            echo "old_version=${{OLD_VERSION}}" >> "$GITHUB_OUTPUT"
174            echo "new_version=${{NEW_VERSION}}" >> "$GITHUB_OUTPUT"
175            "#
176        },
177        VERSION_CHECK, BUMPVERSION_CONFIG, bump_type, VERSION_CHECK
178    ))
179    .id("bump-version");
180
181    let old_version = StepOutput::new(&step, "old_version");
182    let new_version = StepOutput::new(&step, "new_version");
183    (step, old_version, new_version)
184}
185
186fn create_pull_request(
187    old_version: StepOutput,
188    new_version: StepOutput,
189    generated_token: StepOutput,
190) -> Step<Use> {
191    let formatted_version = format!("v{}", new_version);
192
193    named::uses("peter-evans", "create-pull-request", "v7").with(
194        Input::default()
195            .add("title", format!("Bump version to {}", new_version))
196            .add(
197                "body",
198                format!(
199                    "This PR bumps the version of this extension to {}",
200                    formatted_version
201                ),
202            )
203            .add(
204                "commit-message",
205                format!("Bump version to {}", formatted_version),
206            )
207            .add("branch", format!("bump-from-{}", old_version))
208            .add(
209                "committer",
210                "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>",
211            )
212            .add("base", "main")
213            .add("delete-branch", true)
214            .add("token", generated_token.to_string())
215            .add("sign-commits", true),
216    )
217}