diff --git a/.github/workflows/bump_patch_version.yml b/.github/workflows/bump_patch_version.yml index 60584cf43467effae5292b478eb3047bad5e1bc1..a12a1f5e43f52b5dba48488f7ce978a771accb85 100644 --- a/.github/workflows/bump_patch_version.yml +++ b/.github/workflows/bump_patch_version.yml @@ -10,7 +10,7 @@ on: type: string jobs: run_bump_patch_version: - if: github.repository_owner == 'zed-industries' + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') runs-on: namespace-profile-16x32-ubuntu-2204 steps: - id: generate-token @@ -25,8 +25,8 @@ jobs: clean: false ref: ${{ inputs.branch }} token: ${{ steps.generate-token.outputs.token }} - - id: bump-version - name: bump_patch_version::run_bump_patch_version::bump_version + - id: channel + name: bump_patch_version::run_bump_patch_version::read_channel run: | channel="$(cat crates/zed/RELEASE_CHANNEL)" @@ -38,30 +38,53 @@ jobs: tag_suffix="-pre" ;; *) - echo "this must be run on either of stable|preview release branches" >&2 + echo "::error::must be run on a stable or preview release branch" exit 1 ;; esac - which cargo-set-version > /dev/null || cargo install cargo-edit -f --no-default-features --features "set-version" + + version=$(script/get-crate-version zed) + + { + echo "channel=$channel" + echo "version=$version" + echo "tag_suffix=$tag_suffix" + } >> "$GITHUB_OUTPUT" + - name: bump_patch_version::run_bump_patch_version::verify_prior_release_exists + run: | + status=$(curl -s -o /dev/null -w '%{http_code}' "https://cloud.zed.dev/releases/$CHANNEL/$VERSION/asset?asset=zed&os=macos&arch=aarch64") + if [[ "$status" != "200" ]]; then + echo "::error::version $VERSION has not been released on $CHANNEL yet (HTTP $status) — bump the patch version only after the current version is released" + exit 1 + fi + env: + CHANNEL: ${{ steps.channel.outputs.channel }} + VERSION: ${{ steps.channel.outputs.version }} + - name: steps::install_cargo_edit + uses: taiki-e/install-action@02cc5f8ca9f2301050c0c099055816a41ee05507 + with: + tool: cargo-edit + - id: bump-version + name: bump_patch_version::run_bump_patch_version::bump_version + run: | version="$(cargo set-version -p zed --bump patch 2>&1 | sed 's/.* //')" echo "version=$version" >> "$GITHUB_OUTPUT" - echo "tag_suffix=$tag_suffix" >> "$GITHUB_OUTPUT" - id: commit - name: bump_patch_version::run_bump_patch_version::commit_changes + name: steps::bot_commit uses: IAreKyleW00t/verified-bot-commit@126a6a11889ab05bcff72ec2403c326cd249b84c with: message: Bump to ${{ steps.bump-version.outputs.version }} for @${{ github.actor }} ref: refs/heads/${{ inputs.branch }} files: '**' token: ${{ steps.generate-token.outputs.token }} - - name: bump_patch_version::run_bump_patch_version::create_version_tag + - name: steps::create_tag uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b with: script: | github.rest.git.createRef({ owner: context.repo.owner, repo: context.repo.repo, - ref: 'refs/tags/v${{ steps.bump-version.outputs.version }}${{ steps.bump-version.outputs.tag_suffix }}', + ref: 'refs/tags/v${{ steps.bump-version.outputs.version }}${{ steps.channel.outputs.tag_suffix }}', sha: '${{ steps.commit.outputs.commit }}' }) github-token: ${{ steps.generate-token.outputs.token }} diff --git a/.github/workflows/bump_zed_version.yml b/.github/workflows/bump_zed_version.yml new file mode 100644 index 0000000000000000000000000000000000000000..02159b8ad249ab340cca4b6c2569b49c5e7eb41d --- /dev/null +++ b/.github/workflows/bump_zed_version.yml @@ -0,0 +1,226 @@ +# Generated from xtask::workflows::bump_zed_version +# Rebuild with `cargo xtask workflows`. +name: bump_zed_version +on: + workflow_dispatch: + inputs: + target: + description: 'Which channels to bump: all, main, preview, or stable' + type: string + default: all +jobs: + resolve_versions: + if: github.repository_owner == 'zed-industries' + runs-on: namespace-profile-16x32-ubuntu-2204 + steps: + - 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 }} + - name: steps::checkout_repo + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + with: + clean: false + ref: main + token: ${{ steps.generate-token.outputs.token }} + - id: versions + name: bump_zed_version::resolve_versions::extract_versions + run: | + 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" + outputs: + next_version: ${{ steps.versions.outputs.next_version }} + pr_branch: ${{ steps.versions.outputs.pr_branch }} + preview_branch: ${{ steps.versions.outputs.preview_branch }} + preview_tag: ${{ steps.versions.outputs.preview_tag }} + stable_branch: ${{ steps.versions.outputs.stable_branch }} + bump_main: + needs: + - resolve_versions + if: inputs.target == 'all' || inputs.target == 'main' + runs-on: namespace-profile-16x32-ubuntu-2204 + steps: + - 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 }} + - name: steps::checkout_repo + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + with: + clean: false + ref: main + token: ${{ steps.generate-token.outputs.token }} + - name: steps::install_cargo_edit + uses: taiki-e/install-action@02cc5f8ca9f2301050c0c099055816a41ee05507 + with: + tool: cargo-edit + - name: bump_zed_version::bump_main::bump_version + run: cargo set-version -p zed --bump minor + - name: steps::create_pull_request + uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 + with: + title: Bump Zed to v${{ needs.resolve_versions.outputs.next_version }} + body: |- + Release Notes: + + - N/A + commit-message: Bump Zed to v${{ needs.resolve_versions.outputs.next_version }} + branch: ${{ needs.resolve_versions.outputs.pr_branch }} + committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> + author: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> + base: main + delete-branch: true + token: ${{ steps.generate-token.outputs.token }} + sign-commits: true + assignees: ${{ github.actor }} + create_preview_branch: + needs: + - resolve_versions + if: inputs.target == 'all' || inputs.target == 'preview' + runs-on: namespace-profile-16x32-ubuntu-2204 + steps: + - 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 }} + - name: steps::checkout_repo + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + with: + clean: false + ref: main + token: ${{ steps.generate-token.outputs.token }} + - id: main-sha + name: bump_zed_version::create_preview_branch::get_main_sha + run: echo "main_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + - name: bump_zed_version::create_preview_branch::promote_to_preview + run: echo -n preview > crates/zed/RELEASE_CHANNEL + - name: steps::create_branch + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/heads/${{ needs.resolve_versions.outputs.preview_branch }}', + sha: '${{ steps.main-sha.outputs.main_sha }}' + }) + github-token: ${{ steps.generate-token.outputs.token }} + - id: commit + name: steps::bot_commit + uses: IAreKyleW00t/verified-bot-commit@126a6a11889ab05bcff72ec2403c326cd249b84c + with: + message: ${{ needs.resolve_versions.outputs.preview_branch }} preview + ref: refs/heads/${{ needs.resolve_versions.outputs.preview_branch }} + files: crates/zed/RELEASE_CHANNEL + token: ${{ steps.generate-token.outputs.token }} + - name: steps::create_tag + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/${{ needs.resolve_versions.outputs.preview_tag }}', + sha: '${{ steps.commit.outputs.commit }}' + }) + github-token: ${{ steps.generate-token.outputs.token }} + promote_to_stable: + needs: + - resolve_versions + if: inputs.target == 'all' || inputs.target == 'stable' + runs-on: namespace-profile-16x32-ubuntu-2204 + steps: + - 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 }} + - name: steps::checkout_repo + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + with: + clean: false + ref: ${{ needs.resolve_versions.outputs.stable_branch }} + token: ${{ steps.generate-token.outputs.token }} + - id: stable-info + name: bump_zed_version::promote_to_stable + run: | + stable_version=$(script/get-crate-version zed) + { + echo "stable_tag=v${stable_version}" + } >> "$GITHUB_OUTPUT" + - name: bump_zed_version::promote_to_stable + run: echo -n stable > crates/zed/RELEASE_CHANNEL + - id: commit + name: steps::bot_commit + uses: IAreKyleW00t/verified-bot-commit@126a6a11889ab05bcff72ec2403c326cd249b84c + with: + message: ${{ needs.resolve_versions.outputs.stable_branch }} stable + ref: refs/heads/${{ needs.resolve_versions.outputs.stable_branch }} + files: crates/zed/RELEASE_CHANNEL + token: ${{ steps.generate-token.outputs.token }} + - name: steps::create_tag + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/${{ steps.stable-info.outputs.stable_tag }}', + sha: '${{ steps.commit.outputs.commit }}' + }) + github-token: ${{ steps.generate-token.outputs.token }} +defaults: + run: + shell: bash -euxo pipefail {0} diff --git a/.github/workflows/extension_bump.yml b/.github/workflows/extension_bump.yml index b4cbac4ec8c0ab37ebad73eb96c2ee074ca969a6..083c6a7c9ed90cab0cf8a39cae0f1677914e4e72 100644 --- a/.github/workflows/extension_bump.yml +++ b/.github/workflows/extension_bump.yml @@ -137,7 +137,7 @@ jobs: OLD_VERSION: ${{ needs.check_version_changed.outputs.current_version }} BUMP_TYPE: ${{ inputs.bump-type }} WORKING_DIR: ${{ inputs.working-directory }} - - name: extension_bump::create_pull_request + - name: steps::create_pull_request uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 with: title: ${{ steps.bump-version.outputs.title }} @@ -145,6 +145,7 @@ jobs: commit-message: ${{ steps.bump-version.outputs.title }} branch: ${{ steps.bump-version.outputs.branch_name }} committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> + author: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> base: main delete-branch: true token: ${{ steps.generate-token.outputs.token }} diff --git a/.github/workflows/extension_workflow_rollout.yml b/.github/workflows/extension_workflow_rollout.yml index 5bb315a730d8f25f6e1eccbbe5e1734e1cda6d99..03767f48fb09c23b33f8cab92cc4008962357ac1 100644 --- a/.github/workflows/extension_workflow_rollout.yml +++ b/.github/workflows/extension_workflow_rollout.yml @@ -172,10 +172,9 @@ jobs: run: | echo "sha_short=$(echo "$GITHUB_SHA" | cut -c1-7)" >> "$GITHUB_OUTPUT" - id: create-pr - name: extension_workflow_rollout::rollout_workflows_to_extension::create_pull_request + name: steps::create_pull_request uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 with: - path: extension title: Update CI workflows to `${{ steps.short-sha.outputs.sha_short }}` body: | This PR updates the CI workflow files from the main Zed repository @@ -190,6 +189,8 @@ jobs: delete-branch: true token: ${{ steps.generate-token.outputs.token }} sign-commits: true + assignees: ${{ inputs.filter-repos != '' && github.actor || '' }} + path: extension - name: extension_workflow_rollout::rollout_workflows_to_extension::enable_auto_merge run: | if [ -n "$PR_NUMBER" ]; then diff --git a/.github/workflows/publish_extension_cli.yml b/.github/workflows/publish_extension_cli.yml index 17248cea11307d4604b05d5160212a4f38e2874a..397e8f0731b2d74ee7c23eba59863854980be85d 100644 --- a/.github/workflows/publish_extension_cli.yml +++ b/.github/workflows/publish_extension_cli.yml @@ -62,7 +62,7 @@ jobs: tooling/xtask/src/tasks/workflows/extension_tests.rs - name: publish_extension_cli::update_sha_in_zed::regenerate_workflows run: cargo xtask workflows - - name: publish_extension_cli::create_pull_request_zed + - name: steps::create_pull_request uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 with: title: 'extension_ci: Bump extension CLI version to `${{ steps.short-sha.outputs.sha_short }}`' @@ -75,6 +75,7 @@ jobs: commit-message: 'extension_ci: 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> + author: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> base: main delete-branch: true token: ${{ steps.generate-token.outputs.token }} @@ -107,7 +108,7 @@ jobs: run: | sed -i "s/ZED_EXTENSION_CLI_SHA: [a-f0-9]*/ZED_EXTENSION_CLI_SHA: $GITHUB_SHA/" \ .github/workflows/ci.yml - - name: publish_extension_cli::create_pull_request_extensions + - name: steps::create_pull_request uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 with: title: Bump extension CLI version to `${{ steps.short-sha.outputs.sha_short }}` @@ -116,12 +117,13 @@ jobs: 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> + author: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> base: main delete-branch: true token: ${{ steps.generate-token.outputs.token }} sign-commits: true - labels: allow-no-extension assignees: ${{ github.actor }} + labels: allow-no-extension defaults: run: shell: bash -euxo pipefail {0} diff --git a/.github/workflows/retag_release.yml b/.github/workflows/retag_release.yml new file mode 100644 index 0000000000000000000000000000000000000000..0cd3710ba358addea4d491afcb7924498b673d43 --- /dev/null +++ b/.github/workflows/retag_release.yml @@ -0,0 +1,88 @@ +# Generated from xtask::workflows::retag_release +# Rebuild with `cargo xtask workflows`. +name: retag_release +on: + workflow_dispatch: + inputs: + branch: + description: Release branch to re-tag (e.g. v0.180.x) + required: true + type: string +jobs: + run_retag_release: + if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') + runs-on: namespace-profile-16x32-ubuntu-2204 + steps: + - 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 }} + - name: steps::checkout_repo + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + with: + clean: false + ref: ${{ inputs.branch }} + token: ${{ steps.generate-token.outputs.token }} + - id: info + name: retag_release::run_retag_release::resolve_tag + run: | + if [[ ! "$BRANCH" =~ ^v[0-9]+\.[0-9]{1,3}\.x$ ]]; then + echo "::error::branch '$BRANCH' does not match the release branch pattern v[N].[N].x" + exit 1 + fi + + channel="$(cat crates/zed/RELEASE_CHANNEL)" + + tag_suffix="" + case $channel in + stable) + ;; + preview) + tag_suffix="-pre" + ;; + *) + echo "::error::must be run on a stable or preview release branch" + exit 1 + ;; + esac + + version=$(script/get-crate-version zed) + + { + echo "channel=$channel" + echo "version=$version" + echo "tag_suffix=$tag_suffix" + echo "head_sha=$(git rev-parse HEAD)" + } >> "$GITHUB_OUTPUT" + env: + BRANCH: ${{ inputs.branch }} + - name: retag_release::run_retag_release::verify_no_existing_release + run: | + status=$(curl -s -o /dev/null -w '%{http_code}' "https://cloud.zed.dev/releases/$CHANNEL/$VERSION/asset?asset=zed&os=macos&arch=aarch64") + if [[ "$status" == "200" ]]; then + echo "::error::version $VERSION is already released on $CHANNEL — cannot re-tag a released version" + exit 1 + fi + env: + CHANNEL: ${{ steps.info.outputs.channel }} + VERSION: ${{ steps.info.outputs.version }} + - 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/v${{ steps.info.outputs.version }}${{ steps.info.outputs.tag_suffix }}', + sha: '${{ steps.info.outputs.head_sha }}', + force: true + }) + github-token: ${{ steps.generate-token.outputs.token }} +concurrency: + group: ${{ github.workflow }}-${{ inputs.branch }} + cancel-in-progress: true +defaults: + run: + shell: bash -euxo pipefail {0} diff --git a/script/bump-zed-minor-versions b/script/bump-zed-minor-versions deleted file mode 100755 index 536dbb6244c7e9f99c4085ca95f667e43dd67ac3..0000000000000000000000000000000000000000 --- a/script/bump-zed-minor-versions +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -# Ensure cargo-edit is installed -which cargo-set-version > /dev/null || cargo install cargo-edit - -# Ensure we're in a clean state on an up-to-date `main` branch. -if [[ -n $(git status --short --untracked-files=no) ]]; then - echo "can't bump versions with uncommitted changes" - exit 1 -fi -if [[ $(git rev-parse --abbrev-ref HEAD) != "main" ]]; then - echo "this command must be run on main" - exit 1 -fi -git pull -q --ff-only origin main - -# Parse the current version -version=$(script/get-crate-version zed) -major=$(echo $version | cut -d. -f1) -minor=$(echo $version | cut -d. -f2) -patch=$(echo $version | cut -d. -f3) -prev_minor=$(expr $minor - 1) -next_minor=$(expr $minor + 1) - -minor_branch_name="v${major}.${minor}.x" -prev_minor_branch_name="v${major}.${prev_minor}.x" -next_minor_branch_name="v${major}.${next_minor}.x" -preview_tag_name="v${major}.${minor}.${patch}-pre" -bump_main_branch_name="set-minor-version-to-${major}.${next_minor}" - -git fetch origin ${prev_minor_branch_name}:${prev_minor_branch_name} -git fetch origin --tags -cargo check -q - -function cleanup { - git checkout -q main -} -trap cleanup EXIT - -echo "Checking invariants before taking any actions..." -if [[ $(cat crates/zed/RELEASE_CHANNEL) != dev && $(cat crates/zed/RELEASE_CHANNEL) != nightly ]]; then - echo "release channel on main should be dev or nightly" - exit 1 -fi -if git show-ref --quiet refs/tags/${preview_tag_name}; then - echo "tag ${preview_tag_name} already exists" - exit 1 -fi -if git show-ref --quiet refs/heads/${minor_branch_name}; then - echo "branch ${minor_branch_name} already exists" - exit 1 -fi -if ! git show-ref --quiet refs/heads/${prev_minor_branch_name}; then - echo "previous branch ${minor_branch_name} doesn't exist" - exit 1 -fi -if [[ $(git show ${prev_minor_branch_name}:crates/zed/RELEASE_CHANNEL) != preview ]]; then - echo "release channel on branch ${prev_minor_branch_name} should be preview" - exit 1 -fi - -echo "Promoting existing branch ${prev_minor_branch_name} to stable..." -git checkout -q ${prev_minor_branch_name} -git clean -q -dff -stable_tag_name="v$(script/get-crate-version zed)" -if git show-ref --quiet refs/tags/${stable_tag_name}; then - echo "tag ${stable_tag_name} already exists" - exit 1 -fi -old_prev_minor_sha=$(git rev-parse HEAD) -echo -n stable > crates/zed/RELEASE_CHANNEL -git commit -q --all --message "${prev_minor_branch_name} stable" -git tag ${stable_tag_name} - -echo "Creating new preview branch ${minor_branch_name}..." -git checkout -q main -git checkout -q -b ${minor_branch_name} -echo -n preview > crates/zed/RELEASE_CHANNEL -git commit -q --all --message "${minor_branch_name} preview" -git tag ${preview_tag_name} - -echo "Preparing main for version ${next_minor_branch_name}..." -git checkout -q main -git clean -q -dff -git checkout -q -b ${bump_main_branch_name} -cargo set-version --package zed --bump minor -cargo check -q - -git commit -q --all --message "${next_minor_branch_name} dev" - -git checkout -q main - -cat <&2 - exit 1 - ;; -esac - -exec script/lib/bump-version.sh zed v "$tag_suffix" patch diff --git a/script/bump-zed-version b/script/bump-zed-version new file mode 100755 index 0000000000000000000000000000000000000000..623cfed3910b41e35225312e5896896e141b63db --- /dev/null +++ b/script/bump-zed-version @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +set -eu + +usage() { + echo "Usage: $0 [target]" + echo "" + echo "Triggers the bump_zed_version workflow to perform a minor release version bump " + echo "and update the stable and preview versions." + echo "" + echo "Arguments:" + echo " target Which channels to bump: all (default), main, preview, or stable" + exit 1 +} + +target="${1:-all}" + +if [[ "$target" != "all" && "$target" != "main" && "$target" != "preview" && "$target" != "stable" ]]; then + echo "error: invalid target '$target'" >&2 + echo "Valid targets: all, main, preview, stable" >&2 + exit 1 +fi + +day_of_week=$(date +%u) +if [[ $day_of_week -ne 3 ]]; then + day_name=$(date +%A) + echo "Warning: Today is $day_name. Release version bumps are typically only done on Zednesdays." + read -r -p "Continue anyway? (y/N) " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "Aborted." + exit 0 + fi +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 +} + +echo "Triggering bump_zed_version workflow:" +echo " target: $target" +echo "" + +gh workflow run bump_zed_version.yml \ + -f target="$target" + +echo "" +echo "Workflow triggered. Monitor progress at:" +echo " https://github.com/zed-industries/zed/actions/workflows/bump_zed_version.yml" diff --git a/script/retag-release b/script/retag-release new file mode 100755 index 0000000000000000000000000000000000000000..f7097e19941ba084306d37dd614dd4e85c95e37f --- /dev/null +++ b/script/retag-release @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -eu + +usage() { + echo "Usage: $0 " + echo "" + echo "Re-tags the HEAD of a release branch by force-updating the tag." + echo "This is useful when commits were added to a release branch after" + echo "tagging but before the release was published." + echo "" + echo "Arguments:" + echo " branch Release branch name (e.g. v0.180.x)" + exit 1 +} + +branch="${1:-}" + +if [[ -z "$branch" ]]; then + usage +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 +} + +echo "Triggering retag_release workflow:" +echo " branch: $branch" +echo "" + +gh workflow run retag_release.yml \ + -f branch="$branch" + +echo "" +echo "Workflow triggered. Monitor progress at:" +echo " https://github.com/zed-industries/zed/actions/workflows/retag_release.yml" diff --git a/tooling/xtask/src/tasks/workflows.rs b/tooling/xtask/src/tasks/workflows.rs index 387c739a1ac12d4d65d11f33777525c59f05f7f2..69dd1b3d34ed244ced9514c033de03ab2c14cb2f 100644 --- a/tooling/xtask/src/tasks/workflows.rs +++ b/tooling/xtask/src/tasks/workflows.rs @@ -9,6 +9,7 @@ use crate::tasks::workflow_checks::{self}; mod after_release; mod autofix_pr; mod bump_patch_version; +mod bump_zed_version; mod cherry_pick; mod compare_perf; mod compliance_check; @@ -22,6 +23,7 @@ mod extensions; mod nix_build; mod publish_extension_cli; mod release_nightly; +mod retag_release; mod run_bundling; mod release; @@ -196,6 +198,7 @@ pub fn run_workflows(args: GenerateWorkflowArgs) -> Result<()> { WorkflowFile::zed(after_release::after_release), WorkflowFile::zed(autofix_pr::autofix_pr), WorkflowFile::zed(bump_patch_version::bump_patch_version), + WorkflowFile::zed(bump_zed_version::bump_zed_version), WorkflowFile::zed(cherry_pick::cherry_pick), WorkflowFile::zed(compare_perf::compare_perf), WorkflowFile::zed(compliance_check::compliance_check), @@ -208,6 +211,7 @@ pub fn run_workflows(args: GenerateWorkflowArgs) -> Result<()> { WorkflowFile::zed(publish_extension_cli::publish_extension_cli), WorkflowFile::zed(release::release), WorkflowFile::zed(release_nightly::release_nightly), + WorkflowFile::zed(retag_release::retag_release), WorkflowFile::zed(run_agent_evals::run_cron_unit_evals), WorkflowFile::zed(run_agent_evals::run_unit_evals), WorkflowFile::zed(run_bundling::run_bundling), diff --git a/tooling/xtask/src/tasks/workflows/autofix_pr.rs b/tooling/xtask/src/tasks/workflows/autofix_pr.rs index 400103b55e78ba32bfcd641802876be536a25af1..c2791aba0902fbeeda76126e503df9801bc78c74 100644 --- a/tooling/xtask/src/tasks/workflows/autofix_pr.rs +++ b/tooling/xtask/src/tasks/workflows/autofix_pr.rs @@ -61,12 +61,7 @@ fn run_autofix(pr_number: &WorkflowInput, run_clippy: &WorkflowInput) -> NamedJo } fn install_cargo_machete() -> Step { - named::uses( - "taiki-e", - "install-action", - "02cc5f8ca9f2301050c0c099055816a41ee05507", - ) - .add_with(("tool", "cargo-machete@0.7.0")) + steps::taiki_install_action("cargo-machete@0.7.0") } fn run_cargo_fmt() -> Step { diff --git a/tooling/xtask/src/tasks/workflows/bump_patch_version.rs b/tooling/xtask/src/tasks/workflows/bump_patch_version.rs index bf1df69bcf3447cf374779fc36ec99740322cee0..a657e77f3d0329274cc0c68114ed3cad9ebabd8a 100644 --- a/tooling/xtask/src/tasks/workflows/bump_patch_version.rs +++ b/tooling/xtask/src/tasks/workflows/bump_patch_version.rs @@ -2,7 +2,7 @@ use gh_workflow::*; use crate::tasks::workflows::{ runners, - steps::{self, CheckoutStep, named}, + steps::{self, CheckoutStep, CommonJobConditions, named}, vars::{StepOutput, WorkflowInput}, }; @@ -28,7 +28,7 @@ fn run_bump_patch_version(branch: &WorkflowInput) -> steps::NamedJob { .with_ref(branch.to_string()) } - fn bump_version() -> Step { + fn read_channel() -> Step { named::bash(indoc::indoc! {r#" channel="$(cat crates/zed/RELEASE_CHANNEL)" @@ -40,86 +40,68 @@ fn run_bump_patch_version(branch: &WorkflowInput) -> steps::NamedJob { tag_suffix="-pre" ;; *) - echo "this must be run on either of stable|preview release branches" >&2 + echo "::error::must be run on a stable or preview release branch" exit 1 ;; esac - which cargo-set-version > /dev/null || cargo install cargo-edit -f --no-default-features --features "set-version" - version="$(cargo set-version -p zed --bump patch 2>&1 | sed 's/.* //')" - echo "version=$version" >> "$GITHUB_OUTPUT" - echo "tag_suffix=$tag_suffix" >> "$GITHUB_OUTPUT" + + version=$(script/get-crate-version zed) + + { + echo "channel=$channel" + echo "version=$version" + echo "tag_suffix=$tag_suffix" + } >> "$GITHUB_OUTPUT" "#}) - .id("bump-version") + .id("channel") } - fn commit_changes( - version: &StepOutput, - token: &StepOutput, - branch: &WorkflowInput, - ) -> Step { - named::uses( - "IAreKyleW00t", - "verified-bot-commit", - "126a6a11889ab05bcff72ec2403c326cd249b84c", // v2.3.0 - ) - .id("commit") - .add_with(( - "message", - format!("Bump to {version} for @${{{{ github.actor }}}}"), - )) - .add_with(("ref", format!("refs/heads/{branch}"))) - .add_with(("files", "**")) - .add_with(("token", token.to_string())) + fn verify_prior_release_exists() -> Step { + named::bash(indoc::indoc! {r#" + status=$(curl -s -o /dev/null -w '%{http_code}' "https://cloud.zed.dev/releases/$CHANNEL/$VERSION/asset?asset=zed&os=macos&arch=aarch64") + if [[ "$status" != "200" ]]; then + echo "::error::version $VERSION has not been released on $CHANNEL yet (HTTP $status) — bump the patch version only after the current version is released" + exit 1 + fi + "#}) + .add_env(("CHANNEL", "${{ steps.channel.outputs.channel }}")) + .add_env(("VERSION", "${{ steps.channel.outputs.version }}")) } - fn create_version_tag( - version: &StepOutput, - tag_suffix: &StepOutput, - commit_sha: &StepOutput, - token: &StepOutput, - ) -> Step { - named::uses( - "actions", - "github-script", - "f28e40c7f34bde8b3046d885e986cb6290c5673b", // v7 - ) - .with( - Input::default() - .add( - "script", - indoc::formatdoc! {r#" - github.rest.git.createRef({{ - owner: context.repo.owner, - repo: context.repo.repo, - ref: 'refs/tags/v{version}{tag_suffix}', - sha: '{commit_sha}' - }}) - "#}, - ) - .add("github-token", token.to_string()), - ) + fn bump_version() -> Step { + named::bash(indoc::indoc! {r#" + version="$(cargo set-version -p zed --bump patch 2>&1 | sed 's/.* //')" + echo "version=$version" >> "$GITHUB_OUTPUT" + "#}) + .id("bump-version") } let (authenticate, token) = steps::authenticate_as_zippy().into(); + let channel_step = read_channel(); + let tag_suffix = StepOutput::new(&channel_step, "tag_suffix"); let bump_version_step = bump_version(); let version = StepOutput::new(&bump_version_step, "version"); - let tag_suffix = StepOutput::new(&bump_version_step, "tag_suffix"); - let commit_step = commit_changes(&version, &token, branch); + let commit_step: Step = steps::BotCommitStep::new( + format!("Bump to {version} for @${{{{ github.actor }}}}"), + branch, + &token, + ) + .into(); let commit_sha = StepOutput::new_unchecked(&commit_step, "commit"); named::job( Job::default() - .cond(Expression::new( - "github.repository_owner == 'zed-industries'", - )) - .runs_on(runners::LINUX_XL) + .with_repository_owner_guard() + .runs_on(runners::LINUX_DEFAULT) .add_step(authenticate) .add_step(checkout_branch(branch, &token)) + .add_step(channel_step) + .add_step(verify_prior_release_exists()) + .add_step(steps::install_cargo_edit()) .add_step(bump_version_step) .add_step(commit_step) - .add_step(create_version_tag( - &version, - &tag_suffix, + .add_step(steps::create_ref( + steps::GitRef::tag(format!("v{version}{tag_suffix}")), &commit_sha, &token, )), diff --git a/tooling/xtask/src/tasks/workflows/bump_zed_version.rs b/tooling/xtask/src/tasks/workflows/bump_zed_version.rs new file mode 100644 index 0000000000000000000000000000000000000000..af1ba95d1b3d089803d93f0b798f9c7a9a1cd8dc --- /dev/null +++ b/tooling/xtask/src/tasks/workflows/bump_zed_version.rs @@ -0,0 +1,264 @@ +use gh_workflow::*; + +use crate::tasks::workflows::{ + runners, + steps::{self, 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() + .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().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().into(); + + named::job( + Job::default() + .cond(Expression::new(format!( + "{} == 'all' || {} == 'main'", + target.expr(), + target.expr(), + ))) + .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().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", 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(), + ))) + .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().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", 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(), + ))) + .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, + )), + ) +} diff --git a/tooling/xtask/src/tasks/workflows/extension_bump.rs b/tooling/xtask/src/tasks/workflows/extension_bump.rs index 77d2acf7c830302407207950b1919b9002049460..6bb0e0a421f1f1fd00b62a66aa2fcb0a33e67719 100644 --- a/tooling/xtask/src/tasks/workflows/extension_bump.rs +++ b/tooling/xtask/src/tasks/workflows/extension_bump.rs @@ -1,4 +1,4 @@ -use gh_workflow::{ctx::Context, *}; +use gh_workflow::*; use indoc::{formatdoc, indoc}; use crate::tasks::workflows::{ @@ -327,27 +327,9 @@ fn create_pull_request( generated_token: StepOutput, branch_name: StepOutput, ) -> Step { - named::uses( - "peter-evans", - "create-pull-request", - "98357b18bf14b5342f975ff684046ec3b2a07725", - ) - .with( - Input::default() - .add("title", title.to_string()) - .add("body", body.to_string()) - .add("commit-message", title.to_string()) - .add("branch", branch_name.to_string()) - .add( - "committer", - "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", - ) - .add("base", "main") - .add("delete-branch", true) - .add("token", generated_token.to_string()) - .add("sign-commits", true) - .add("assignees", Context::github().actor().to_string()), - ) + steps::CreatePrStep::new(title.to_string(), branch_name, &generated_token) + .with_body(body) + .into() } fn trigger_release( diff --git a/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs b/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs index 1145cf2b5a70c30ac7212f6002e653d1396d55c4..2e44a9a698f607342aef494f6ba5742be85ff920 100644 --- a/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs +++ b/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs @@ -34,6 +34,7 @@ pub(crate) fn extension_workflow_rollout() -> Workflow { removed_ci, removed_shared, &extra_context_input, + &filter_repos_input, ); let create_tag = create_rollout_tag(&rollout_workflows, &filter_repos_input); @@ -192,6 +193,7 @@ fn rollout_workflows_to_extension( removed_ci: JobOutput, removed_shared: JobOutput, extra_context_input: &WorkflowInput, + filter_repos_input: &WorkflowInput, ) -> NamedJob { fn checkout_extension_repo(token: &StepOutput) -> CheckoutStep { steps::checkout_repo() @@ -259,6 +261,7 @@ fn rollout_workflows_to_extension( token: &StepOutput, short_sha: &StepOutput, context_input: &WorkflowInput, + filter_repos_input: &WorkflowInput, ) -> Step { let title = format!("Update CI workflows to `{short_sha}`"); @@ -270,29 +273,16 @@ fn rollout_workflows_to_extension( "#, }; - named::uses( - "peter-evans", - "create-pull-request", - "98357b18bf14b5342f975ff684046ec3b2a07725", - ) - .add_with(("path", "extension")) - .add_with(("title", title.clone())) - .add_with(("body", body)) - .add_with(("commit-message", title)) - .add_with(("branch", "update-workflows")) - .add_with(( - "committer", - "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", - )) - .add_with(( - "author", - "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", - )) - .add_with(("base", "main")) - .add_with(("delete-branch", true)) - .add_with(("token", token.to_string())) - .add_with(("sign-commits", true)) - .id("create-pr") + let pr_step: Step = steps::CreatePrStep::new(title, "update-workflows", token) + .with_body(body) + .with_path("extension") + // Save my inbox from exploding on rollout + .with_assignee(format!( + "${{{{ {repos_expr} != '' && github.actor || '' }}}}", + repos_expr = filter_repos_input.expr() + )) + .into(); + pr_step.id("create-pr") } fn enable_auto_merge(token: &StepOutput) -> Step { @@ -345,7 +335,7 @@ fn rollout_workflows_to_extension( .add_step(download_workflow_files()) .add_step(sync_workflow_files(removed_ci, removed_shared)) .add_step(calculate_short_sha) - .add_step(create_pull_request(&token, &short_sha, extra_context_input)) + .add_step(create_pull_request(&token, &short_sha, extra_context_input, filter_repos_input)) .add_step(enable_auto_merge(&token)); named::job(job) diff --git a/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs b/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs index 9f8d054241507af8597e2ff328263c440377686f..ea1266d03a9170b8c4d282f5ea7238e900726975 100644 --- a/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs +++ b/tooling/xtask/src/tasks/workflows/publish_extension_cli.rs @@ -1,4 +1,4 @@ -use gh_workflow::{ctx::Context, *}; +use gh_workflow::*; use indoc::indoc; use crate::tasks::workflows::{ @@ -88,31 +88,15 @@ fn create_pull_request_zed(generated_token: &StepOutput, short_sha: &StepOutput) short_sha ); - named::uses("peter-evans", "create-pull-request", "98357b18bf14b5342f975ff684046ec3b2a07725").with( - Input::default() - .add("title", title.clone()) - .add( - "body", - indoc! {r#" - This PR bumps the extension CLI version used in the extension workflows to `${{ github.sha }}`. - - Release Notes: - - - N/A - "#}, - ) - .add("commit-message", title) - .add("branch", "update-extension-cli-sha") - .add( - "committer", - "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", - ) - .add("base", "main") - .add("delete-branch", true) - .add("token", generated_token.to_string()) - .add("sign-commits", true) - .add("assignees", Context::github().actor().to_string()), - ) + 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 + "#}) + .into() } fn update_sha_in_extensions(publish_job: &NamedJob) -> NamedJob { @@ -160,28 +144,12 @@ fn create_pull_request_extensions( ) -> Step { let title = format!("Bump extension CLI version to `{}`", short_sha); - named::uses("peter-evans", "create-pull-request", "98357b18bf14b5342f975ff684046ec3b2a07725").with( - Input::default() - .add("title", title.clone()) - .add( - "body", - indoc! {r#" - This PR bumps the extension CLI version to https://github.com/zed-industries/zed/commit/${{ github.sha }}. - "#}, - ) - .add("commit-message", title) - .add("branch", "update-extension-cli-sha") - .add( - "committer", - "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", - ) - .add("base", "main") - .add("delete-branch", true) - .add("token", generated_token.to_string()) - .add("sign-commits", true) - .add("labels", "allow-no-extension") - .add("assignees", Context::github().actor().to_string()), - ) + 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_labels("allow-no-extension") + .into() } fn get_short_sha() -> (Step, StepOutput) { diff --git a/tooling/xtask/src/tasks/workflows/retag_release.rs b/tooling/xtask/src/tasks/workflows/retag_release.rs new file mode 100644 index 0000000000000000000000000000000000000000..98296a8f0432b1cae76be7eee9df82d542c85fc5 --- /dev/null +++ b/tooling/xtask/src/tasks/workflows/retag_release.rs @@ -0,0 +1,100 @@ +use gh_workflow::*; + +use crate::tasks::workflows::{ + runners, + steps::{self, CheckoutStep, CommonJobConditions, named}, + vars::{StepOutput, WorkflowInput}, +}; + +pub fn retag_release() -> Workflow { + let branch = WorkflowInput::string("branch", None) + .description("Release branch to re-tag (e.g. v0.180.x)"); + let retag_job = run_retag_release(&branch); + named::workflow() + .on(Event::default() + .workflow_dispatch(WorkflowDispatch::default().add_input(branch.name, branch.input()))) + .concurrency( + Concurrency::new(Expression::new(format!( + "${{{{ github.workflow }}}}-{branch}" + ))) + .cancel_in_progress(true), + ) + .add_job(retag_job.name, retag_job.job) +} + +fn run_retag_release(branch: &WorkflowInput) -> steps::NamedJob { + fn checkout_branch(branch: &WorkflowInput, token: &StepOutput) -> CheckoutStep { + steps::checkout_repo() + .with_token(token) + .with_ref(branch.to_string()) + } + + fn resolve_tag(branch: &WorkflowInput) -> Step { + named::bash(indoc::indoc! {r#" + if [[ ! "$BRANCH" =~ ^v[0-9]+\.[0-9]{1,3}\.x$ ]]; then + echo "::error::branch '$BRANCH' does not match the release branch pattern v[N].[N].x" + exit 1 + fi + + channel="$(cat crates/zed/RELEASE_CHANNEL)" + + tag_suffix="" + case $channel in + stable) + ;; + preview) + tag_suffix="-pre" + ;; + *) + echo "::error::must be run on a stable or preview release branch" + exit 1 + ;; + esac + + version=$(script/get-crate-version zed) + + { + echo "channel=$channel" + echo "version=$version" + echo "tag_suffix=$tag_suffix" + echo "head_sha=$(git rev-parse HEAD)" + } >> "$GITHUB_OUTPUT" + "#}) + .id("info") + .add_env(("BRANCH", branch.to_string())) + } + + fn verify_no_existing_release() -> Step { + named::bash(indoc::indoc! {r#" + status=$(curl -s -o /dev/null -w '%{http_code}' "https://cloud.zed.dev/releases/$CHANNEL/$VERSION/asset?asset=zed&os=macos&arch=aarch64") + if [[ "$status" == "200" ]]; then + echo "::error::version $VERSION is already released on $CHANNEL — cannot re-tag a released version" + exit 1 + fi + "#}) + .add_env(("CHANNEL", "${{ steps.info.outputs.channel }}")) + .add_env(("VERSION", "${{ steps.info.outputs.version }}")) + } + + let (authenticate, token) = steps::authenticate_as_zippy().into(); + let resolve_step = resolve_tag(branch); + let version = StepOutput::new(&resolve_step, "version"); + let tag_suffix = StepOutput::new(&resolve_step, "tag_suffix"); + let head_sha = StepOutput::new(&resolve_step, "head_sha"); + + named::job( + Job::default() + .with_repository_owner_guard() + .runs_on(runners::LINUX_XL) + .add_step(authenticate) + .add_step(checkout_branch(branch, &token)) + .add_step(resolve_step) + .add_step(verify_no_existing_release()) + .add_step(steps::update_ref( + steps::GitRef::tag(format!("v{version}{tag_suffix}")), + &head_sha, + &token, + true, + )), + ) +} diff --git a/tooling/xtask/src/tasks/workflows/run_tests.rs b/tooling/xtask/src/tasks/workflows/run_tests.rs index 8de3460f6dab7f77a3a55c277553f28a34f7763e..345f6b017e906f6244a24e829fb294f5b46556d4 100644 --- a/tooling/xtask/src/tasks/workflows/run_tests.rs +++ b/tooling/xtask/src/tasks/workflows/run_tests.rs @@ -430,12 +430,7 @@ fn check_style() -> NamedJob { fn check_dependencies() -> NamedJob { fn install_cargo_machete() -> Step { - named::uses( - "taiki-e", - "install-action", - "02cc5f8ca9f2301050c0c099055816a41ee05507", - ) - .add_with(("tool", "cargo-machete@0.7.0")) + steps::taiki_install_action("cargo-machete@0.7.0") } fn run_cargo_machete() -> Step { diff --git a/tooling/xtask/src/tasks/workflows/steps.rs b/tooling/xtask/src/tasks/workflows/steps.rs index 7f015c791bbd996ff885b64030bb19e0e902c487..df0f6aababdeddb881f5e665672d501e10fbc0cc 100644 --- a/tooling/xtask/src/tasks/workflows/steps.rs +++ b/tooling/xtask/src/tasks/workflows/steps.rs @@ -1,4 +1,4 @@ -use gh_workflow::*; +use gh_workflow::{ctx::Context, *}; use serde_json::Value; use crate::tasks::workflows::{ @@ -176,6 +176,20 @@ pub fn cargo_fmt() -> Step { named::bash("cargo fmt --all -- --check") } +pub fn install_cargo_edit() -> Step { + taiki_install_action("cargo-edit") +} + +pub fn taiki_install_action(tool: &str) -> Step { + Step::new(named::function_name(1)) + .uses( + "taiki-e", + "install-action", + "02cc5f8ca9f2301050c0c099055816a41ee05507", // v2 + ) + .add_with(("tool", tool)) +} + pub fn cargo_install_nextest() -> Step { named::uses( "taiki-e", @@ -648,3 +662,239 @@ fn generate_token_with_job_name<'a>( permissions: None, } } + +pub(crate) struct BotCommitStep { + message: String, + branch: String, + files: String, + token: String, +} + +impl BotCommitStep { + pub fn new(message: impl ToString, branch: impl ToString, token: &StepOutput) -> Self { + Self { + message: message.to_string(), + branch: branch.to_string(), + files: "**".to_string(), + token: token.to_string(), + } + } + + pub fn with_files(self, files: impl ToString) -> Self { + Self { + files: files.to_string(), + ..self + } + } +} + +impl From for Step { + fn from(step: BotCommitStep) -> Self { + Step::new("steps::bot_commit") + .uses( + "IAreKyleW00t", + "verified-bot-commit", + "126a6a11889ab05bcff72ec2403c326cd249b84c", // v2.3.0 + ) + .id("commit") + .add_with(("message", step.message)) + .add_with(("ref", format!("refs/heads/{}", step.branch))) + .add_with(("files", step.files)) + .add_with(("token", step.token)) + } +} + +pub(crate) enum GitRef { + Tag(String), + Branch(String), +} + +impl GitRef { + pub fn tag(name: impl ToString) -> Self { + Self::Tag(name.to_string()) + } + + pub fn branch(name: impl ToString) -> Self { + Self::Branch(name.to_string()) + } + + fn create_ref_path(&self) -> String { + match self { + Self::Tag(name) => format!("refs/tags/{name}"), + Self::Branch(name) => format!("refs/heads/{name}"), + } + } + + fn update_ref_path(&self) -> String { + match self { + Self::Tag(name) => format!("tags/{name}"), + Self::Branch(name) => format!("heads/{name}"), + } + } + + fn kind(&self) -> &'static str { + match self { + Self::Tag(_) => "tag", + Self::Branch(_) => "branch", + } + } +} + +#[allow(unused)] +enum RefOperation { + Create, + Update { force: bool }, +} + +struct RefOp { + git_ref: GitRef, + operation: RefOperation, + sha: String, + token: String, +} + +impl From for Step { + fn from(op: RefOp) -> Self { + let (api_method, ref_path, force_line) = match &op.operation { + RefOperation::Create => ("createRef", op.git_ref.create_ref_path(), String::new()), + RefOperation::Update { force } => ( + "updateRef", + op.git_ref.update_ref_path(), + format!(",\n force: {force}"), + ), + }; + let step_name = match &op.operation { + RefOperation::Create => format!("steps::create_{}", op.git_ref.kind()), + RefOperation::Update { .. } => format!("steps::update_{}", op.git_ref.kind()), + }; + let sha = &op.sha; + let script = indoc::formatdoc! {r#" + github.rest.git.{api_method}({{ + owner: context.repo.owner, + repo: context.repo.repo, + ref: '{ref_path}', + sha: '{sha}'{force_line} + }}) + "#}; + Step::new(step_name) + .uses( + "actions", + "github-script", + "f28e40c7f34bde8b3046d885e986cb6290c5673b", // v7 + ) + .with( + Input::default() + .add("script", script) + .add("github-token", op.token), + ) + } +} + +pub(crate) fn create_ref( + git_ref: GitRef, + sha: impl ToString, + token: &StepOutput, +) -> impl Into> { + RefOp { + git_ref, + operation: RefOperation::Create, + sha: sha.to_string(), + token: token.to_string(), + } +} + +#[allow(unused)] +pub(crate) fn update_ref( + git_ref: GitRef, + sha: impl ToString, + token: &StepOutput, + force: bool, +) -> impl Into> { + RefOp { + git_ref, + operation: RefOperation::Update { force }, + sha: sha.to_string(), + token: token.to_string(), + } +} + +const ZED_ZIPPY_COMMITTER: &str = + "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>"; + +pub(crate) struct CreatePrStep { + title: String, + body: String, + branch: String, + base: String, + token: String, + assignees: Option, + labels: Option, + path: Option, +} + +impl CreatePrStep { + pub fn new(title: impl ToString, branch: impl ToString, token: &StepOutput) -> Self { + Self { + title: title.to_string(), + body: "Release Notes:\n\n- N/A".to_string(), + branch: branch.to_string(), + base: "main".to_string(), + token: token.to_string(), + assignees: Some(Context::github().actor().to_string()), + labels: None, + path: None, + } + } + + pub fn with_body(self, body: impl ToString) -> Self { + Self { + body: body.to_string(), + ..self + } + } + + pub fn with_assignee(self, assignee: impl ToString) -> Self { + Self { + assignees: Some(assignee.to_string()), + ..self + } + } + + pub fn with_labels(self, labels: impl ToString) -> Self { + Self { + labels: Some(labels.to_string()), + ..self + } + } + + pub fn with_path(self, path: impl ToString) -> Self { + Self { + path: Some(path.to_string()), + ..self + } + } +} + +impl From for Step { + fn from(step: CreatePrStep) -> Self { + Step::new("steps::create_pull_request") + .uses( + "peter-evans", + "create-pull-request", + "98357b18bf14b5342f975ff684046ec3b2a07725", // v7 + ) + .add_with(("title", step.title.clone())) + .add_with(("body", step.body)) + .add_with(("commit-message", step.title)) + .add_with(("branch", step.branch)) + .add_with(("committer", ZED_ZIPPY_COMMITTER)) + .add_with(("author", ZED_ZIPPY_COMMITTER)) + .add_with(("base", step.base)) + .add_with(("delete-branch", true)) + .add_with(("token", step.token)) + .add_with(("sign-commits", true)) + .when_some(step.assignees, |s, v| s.add_with(("assignees", v))) + .when_some(step.labels, |s, v| s.add_with(("labels", v))) + .when_some(step.path, |s, v| s.add_with(("path", v))) + } +}