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() }