From 511d1974775d7ce102b799f623ee0b33ed26300f Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Thu, 11 Jun 2026 01:51:59 +0200 Subject: [PATCH] Enforce adding a message to extension CLI bumps (#58786) This slightly reworks the extension CLI bump workflow - instead of triggering on label push, it now triggers on workflow dispatch with a message enforced to be added there. This primarily allows us to add a message to these bumps to better communicate what changes with that version of the CLI. Furthermore, we can soon restrict the label to be only created by that workflow, which has the advantage that it can only be based off of main. Also, it has the nice side-effect that we actually only ever update the label if everything worked properly. Release Notes: - N/A --- .github/workflows/publish_extension_cli.yml | 38 ++++++- script/bump-extension-cli | 35 +++++- .../tasks/workflows/publish_extension_cli.rs | 104 +++++++++++++----- 3 files changed, 142 insertions(+), 35 deletions(-) diff --git a/.github/workflows/publish_extension_cli.yml b/.github/workflows/publish_extension_cli.yml index 397e8f0731b..b2d8e96fcea 100644 --- a/.github/workflows/publish_extension_cli.yml +++ b/.github/workflows/publish_extension_cli.yml @@ -5,12 +5,15 @@ env: CARGO_TERM_COLOR: always CARGO_INCREMENTAL: '0' on: - push: - tags: - - extension-cli + workflow_dispatch: + inputs: + message: + description: Describe why the extension CLI is being bumped and/or what changes are included. + required: true + type: string jobs: publish_job: - if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main' runs-on: namespace-profile-16x32-ubuntu-2204 steps: - name: steps::checkout_repo @@ -31,10 +34,29 @@ jobs: env: DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }} DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }} + - id: generate-token + name: steps::authenticate_as_zippy + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 + with: + app-id: ${{ secrets.ZED_ZIPPY_APP_ID }} + private-key: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }} + permission-contents: write + - name: steps::update_tag + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b + with: + script: | + github.rest.git.updateRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'tags/extension-cli', + sha: context.sha, + force: true + }) + github-token: ${{ steps.generate-token.outputs.token }} update_sha_in_zed: needs: - publish_job - if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main' runs-on: namespace-profile-8x16-ubuntu-2204 steps: - id: generate-token @@ -69,6 +91,8 @@ jobs: body: | This PR bumps the extension CLI version used in the extension workflows to `${{ github.sha }}`. + ${{ inputs.message }} + Release Notes: - N/A @@ -84,7 +108,7 @@ jobs: update_sha_in_extensions: needs: - publish_job - if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main' runs-on: namespace-profile-2x4-ubuntu-2404 steps: - id: generate-token @@ -114,6 +138,8 @@ jobs: title: Bump extension CLI version to `${{ steps.short-sha.outputs.sha_short }}` body: | This PR bumps the extension CLI version to https://github.com/zed-industries/zed/commit/${{ github.sha }}. + + ${{ inputs.message }} commit-message: Bump extension CLI version to `${{ steps.short-sha.outputs.sha_short }}` branch: update-extension-cli-sha committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> diff --git a/script/bump-extension-cli b/script/bump-extension-cli index ee7ea6f8c41..84055c33ee7 100755 --- a/script/bump-extension-cli +++ b/script/bump-extension-cli @@ -1,7 +1,34 @@ #!/usr/bin/env bash -set -e +set -eu -git pull --ff-only origin main -git tag -f extension-cli -git push -f origin extension-cli +usage() { + echo "Usage: $0 " + echo "" + echo "Triggers the publish_extension_cli workflow on main to build a new" + echo "extension CLI binary, bump the 'extension-cli' tag after a successful" + echo "build, and open PRs that update the SHA used by the zed and" + echo "zed-industries/extensions repositories." + echo "" + echo "Arguments:" + echo " message Describes why the extension CLI is being bumped /" + echo " what the changes include. Included in the PR bodies." + exit 1 +} + +if [[ $# -lt 1 || -z "${1:-}" ]]; then + echo "error: a message describing the bump is required" >&2 + echo "" >&2 + usage >&2 +fi + +which gh > /dev/null 2>&1 || { + echo "error: GitHub CLI (gh) is required but not installed." >&2 + echo "Install it with: brew install gh" >&2 + exit 1 +} + +gh workflow run publish_extension_cli.yml --ref main -f message="$1" + +echo "Workflow triggered. Monitor progress at:" +echo " https://github.com/zed-industries/zed/actions/workflows/publish_extension_cli.yml" diff --git a/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs b/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs index ea1266d03a9..cbe656e734e 100644 --- a/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs +++ b/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs @@ -1,19 +1,30 @@ use gh_workflow::*; -use indoc::indoc; +use indoc::{formatdoc, indoc}; use crate::tasks::workflows::{ runners, - steps::{self, CommonJobConditions, NamedJob, RepositoryTarget, generate_token, named}, - vars::{self, StepOutput}, + steps::{ + self, DEFAULT_REPOSITORY_OWNER_GUARD, GitRef, NamedJob, RefSha, RepositoryTarget, + TokenPermissions, generate_token, named, + }, + vars::{self, StepOutput, WorkflowInput}, }; +const EXTENSION_CLI_TAG: &str = "extension-cli"; + pub fn publish_extension_cli() -> Workflow { + let message = WorkflowInput::string("message", None).description( + "Describe why the extension CLI is being bumped and/or what changes are included.", + ); + let publish = publish_job(); - let update_sha_in_zed = update_sha_in_zed(&publish); - let update_sha_in_extensions = update_sha_in_extensions(&publish); + let update_sha_in_zed = update_sha_in_zed(&publish, &message); + let update_sha_in_extensions = update_sha_in_extensions(&publish, &message); named::workflow() - .on(Event::default().push(Push::default().tags(vec!["extension-cli".to_string()]))) + .on(Event::default().workflow_dispatch( + WorkflowDispatch::default().add_input(message.name, message.input()), + )) .add_env(("CARGO_TERM_COLOR", "always")) .add_env(("CARGO_INCREMENTAL", 0)) .add_job(publish.name, publish.job) @@ -21,6 +32,16 @@ pub fn publish_extension_cli() -> Workflow { .add_job(update_sha_in_extensions.name, update_sha_in_extensions.job) } +// `workflow_dispatch` can be triggered from any branch where this workflow file +// exists, so we additionally guard the jobs to only run when dispatched from +// `main`. Jobs that depend on `publish_job` inherit this guard transitively +// because they are skipped when `publish_job` is skipped. +fn dispatched_from_main_guard() -> Expression { + Expression::new(format!( + "{DEFAULT_REPOSITORY_OWNER_GUARD} && github.ref == 'refs/heads/main'" + )) +} + fn publish_job() -> NamedJob { fn build_extension_cli() -> Step { named::bash("cargo build --release --package extension_cli") @@ -38,19 +59,31 @@ fn publish_job() -> NamedJob { )) } + let (authenticate, token) = steps::authenticate_as_zippy() + .for_repository(RepositoryTarget::current()) + .with_permissions([(TokenPermissions::Contents, Level::Write)]) + .into(); + named::job( Job::default() - .with_repository_owner_guard() + .cond(dispatched_from_main_guard()) .runs_on(runners::LINUX_DEFAULT) .add_step(steps::checkout_repo()) .add_step(steps::cache_rust_dependencies_namespace()) .add_step(steps::setup_linux()) .add_step(build_extension_cli()) - .add_step(upload_binary()), + .add_step(upload_binary()) + .add_step(authenticate) + .add_step(steps::update_ref( + GitRef::tag(EXTENSION_CLI_TAG), + RefSha::Context, + &token, + true, + )), ) } -fn update_sha_in_zed(publish_job: &NamedJob) -> NamedJob { +fn update_sha_in_zed(publish_job: &NamedJob, message: &WorkflowInput) -> NamedJob { let (generate_token, generated_token) = generate_token(vars::ZED_ZIPPY_APP_ID, vars::ZED_ZIPPY_APP_PRIVATE_KEY).into(); @@ -69,7 +102,7 @@ fn update_sha_in_zed(publish_job: &NamedJob) -> NamedJob { named::job( Job::default() - .with_repository_owner_guard() + .cond(dispatched_from_main_guard()) .needs(vec![publish_job.name.clone()]) .runs_on(runners::LINUX_LARGE) .add_step(generate_token) @@ -78,28 +111,40 @@ fn update_sha_in_zed(publish_job: &NamedJob) -> NamedJob { .add_step(get_short_sha_step) .add_step(replace_sha()) .add_step(regenerate_workflows()) - .add_step(create_pull_request_zed(&generated_token, &short_sha)), + .add_step(create_pull_request_zed( + &generated_token, + &short_sha, + message, + )), ) } -fn create_pull_request_zed(generated_token: &StepOutput, short_sha: &StepOutput) -> Step { +fn create_pull_request_zed( + generated_token: &StepOutput, + short_sha: &StepOutput, + message: &WorkflowInput, +) -> Step { let title = format!( "extension_ci: Bump extension CLI version to `{}`", short_sha ); + let body = formatdoc! {r#" + This PR bumps the extension CLI version used in the extension workflows to `${{{{ github.sha }}}}`. + + {message} + + Release Notes: + + - N/A + "#}; + steps::CreatePrStep::new(title, "update-extension-cli-sha", generated_token) - .with_body(indoc::indoc! {r#" - This PR bumps the extension CLI version used in the extension workflows to `${{ github.sha }}`. - - Release Notes: - - - N/A - "#}) + .with_body(body) .into() } -fn update_sha_in_extensions(publish_job: &NamedJob) -> NamedJob { +fn update_sha_in_extensions(publish_job: &NamedJob, message: &WorkflowInput) -> NamedJob { let extensions_repo = RepositoryTarget::new("zed-industries", &["extensions"]); let (generate_token, generated_token) = generate_token(vars::ZED_ZIPPY_APP_ID, vars::ZED_ZIPPY_APP_PRIVATE_KEY) @@ -127,27 +172,36 @@ fn update_sha_in_extensions(publish_job: &NamedJob) -> NamedJob { named::job( Job::default() - .with_repository_owner_guard() + .cond(dispatched_from_main_guard()) .needs(vec![publish_job.name.clone()]) .runs_on(runners::LINUX_SMALL) .add_step(generate_token) .add_step(get_short_sha_step) .add_step(checkout_extensions_repo(&generated_token)) .add_step(replace_sha()) - .add_step(create_pull_request_extensions(&generated_token, &short_sha)), + .add_step(create_pull_request_extensions( + &generated_token, + &short_sha, + message, + )), ) } fn create_pull_request_extensions( generated_token: &StepOutput, short_sha: &StepOutput, + message: &WorkflowInput, ) -> Step { let title = format!("Bump extension CLI version to `{}`", short_sha); + let body = formatdoc! {r#" + This PR bumps the extension CLI version to https://github.com/zed-industries/zed/commit/${{{{ github.sha }}}}. + + {message} + "#}; + steps::CreatePrStep::new(title, "update-extension-cli-sha", generated_token) - .with_body(indoc::indoc! {r#" - This PR bumps the extension CLI version to https://github.com/zed-industries/zed/commit/${{ github.sha }}. - "#}) + .with_body(body) .with_labels("allow-no-extension") .into() }