ci: Add workflow for bumping Zed versions (#54485)

Release Notes:

- N/A
This commit is contained in:
Finn Evers 2026-04-23 00:56:18 +02:00 committed by GitHub
parent a658e1d3b0
commit 8d53ebc70e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1141 additions and 323 deletions

View file

@ -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 }}

226
.github/workflows/bump_zed_version.yml vendored Normal file
View file

@ -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}

View file

@ -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 }}

View file

@ -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

View file

@ -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}

88
.github/workflows/retag_release.yml vendored Normal file
View file

@ -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}

View file

@ -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 <<MESSAGE
Prepared new Zed versions locally. You will need to push the branches and open a PR for the change to main.
# To push and open a PR to update main:
git push -u origin \\
${preview_tag_name} \\
${stable_tag_name} \\
${minor_branch_name} \\
${prev_minor_branch_name} \\
${bump_main_branch_name}
echo -e "Release Notes:\n\n- N/A" | gh pr create \\
--title "Bump Zed to v${major}.${next_minor}" \\
--body-file "-" \\
--base main \\
--head ${bump_main_branch_name} \\
--web
# To undo this push:
git push -f . \\
:${preview_tag_name} \\
:${stable_tag_name} \\
:${minor_branch_name} \\
:${bump_main_branch_name} \\
${old_prev_minor_sha}:${prev_minor_branch_name}
MESSAGE

View file

@ -1,18 +0,0 @@
#!/usr/bin/env bash
channel=$(cat crates/zed/RELEASE_CHANNEL)
tag_suffix=""
case $channel in
stable)
;;
preview)
tag_suffix="-pre"
;;
*)
echo "this must be run on either of stable|preview release branches" >&2
exit 1
;;
esac
exec script/lib/bump-version.sh zed v "$tag_suffix" patch

50
script/bump-zed-version Executable file
View file

@ -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"

38
script/retag-release Executable file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -eu
usage() {
echo "Usage: $0 <branch>"
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"

View file

@ -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),

View file

@ -61,12 +61,7 @@ fn run_autofix(pr_number: &WorkflowInput, run_clippy: &WorkflowInput) -> NamedJo
}
fn install_cargo_machete() -> Step<Use> {
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<Run> {

View file

@ -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<Run> {
fn read_channel() -> Step<Run> {
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=$(script/get-crate-version zed)
{
echo "channel=$channel"
echo "version=$version"
echo "tag_suffix=$tag_suffix"
} >> "$GITHUB_OUTPUT"
"#})
.id("channel")
}
fn verify_prior_release_exists() -> Step<Run> {
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 bump_version() -> Step<Run> {
named::bash(indoc::indoc! {r#"
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("bump-version")
}
fn commit_changes(
version: &StepOutput,
token: &StepOutput,
branch: &WorkflowInput,
) -> Step<Use> {
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 create_version_tag(
version: &StepOutput,
tag_suffix: &StepOutput,
commit_sha: &StepOutput,
token: &StepOutput,
) -> Step<Use> {
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()),
)
}
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<Use> = 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,
)),

View file

@ -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<Run> {
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<Run> {
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<Run> {
named::bash("echo -n preview > crates/zed/RELEASE_CHANNEL")
}
fn get_main_sha() -> Step<Run> {
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<Use> = 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<Use> = 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,
)),
)
}

View file

@ -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<Use> {
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(

View file

@ -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<Use> {
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<Use> = 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<gh_workflow::Run> {
@ -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)

View file

@ -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 }}`.
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:
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()),
)
- N/A
"#})
.into()
}
fn update_sha_in_extensions(publish_job: &NamedJob) -> NamedJob {
@ -160,28 +144,12 @@ fn create_pull_request_extensions(
) -> Step<Use> {
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<Run>, StepOutput) {

View file

@ -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<Run> {
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<Run> {
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,
)),
)
}

View file

@ -430,12 +430,7 @@ fn check_style() -> NamedJob {
fn check_dependencies() -> NamedJob {
fn install_cargo_machete() -> Step<Use> {
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<Run> {

View file

@ -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<Run> {
named::bash("cargo fmt --all -- --check")
}
pub fn install_cargo_edit() -> Step<Use> {
taiki_install_action("cargo-edit")
}
pub fn taiki_install_action(tool: &str) -> Step<Use> {
Step::new(named::function_name(1))
.uses(
"taiki-e",
"install-action",
"02cc5f8ca9f2301050c0c099055816a41ee05507", // v2
)
.add_with(("tool", tool))
}
pub fn cargo_install_nextest() -> Step<Use> {
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<BotCommitStep> for Step<Use> {
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<RefOp> for Step<Use> {
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<Step<Use>> {
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<Step<Use>> {
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<String>,
labels: Option<String>,
path: Option<String>,
}
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<CreatePrStep> for Step<Use> {
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)))
}
}