gh-workflow release (#41502)

Ben Kunkle created

Closes #ISSUE

Rewrite our release pipeline to be generated by `gh-workflow`

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

.github/workflows/ci.yml                          |   1 
.github/workflows/release.yml                     | 478 +++++++++++++++++
.github/workflows/run_bundling.yml                |  30 
tooling/xtask/src/tasks/workflows.rs              |   2 
tooling/xtask/src/tasks/workflows/release.rs      | 207 +++++++
tooling/xtask/src/tasks/workflows/run_bundling.rs |  80 ++
tooling/xtask/src/tasks/workflows/run_tests.rs    |   5 
tooling/xtask/src/tasks/workflows/steps.rs        |   9 
tooling/xtask/src/tasks/workflows/vars.rs         |   8 
9 files changed, 790 insertions(+), 30 deletions(-)

Detailed changes

.github/workflows/ci.yml 🔗

@@ -4,6 +4,7 @@ on:
   push:
     tags:
       - "v*"
+      - "!v00.00.00-test" # todo! remove
 
 concurrency:
   # Allow only one workflow per any non-`main` branch.

.github/workflows/release.yml 🔗

@@ -0,0 +1,478 @@
+# Generated from xtask::workflows::release
+# Rebuild with `cargo xtask workflows`.
+name: release
+on:
+  push:
+    tags:
+    - v*
+jobs:
+  run_tests_mac:
+    if: github.repository_owner == 'zed-industries'
+    runs-on: self-mini-macos
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_cargo_config
+      run: |
+        mkdir -p ./../.cargo
+        cp ./.cargo/ci-config.toml ./../.cargo/config.toml
+      shell: bash -euxo pipefail {0}
+    - name: steps::setup_node
+      uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
+      with:
+        node-version: '20'
+    - name: steps::clippy
+      run: ./script/clippy
+      shell: bash -euxo pipefail {0}
+    - name: steps::cargo_install_nextest
+      run: cargo install cargo-nextest --locked
+      shell: bash -euxo pipefail {0}
+    - name: steps::clear_target_dir_if_large
+      run: ./script/clear-target-dir-if-larger-than 300
+      shell: bash -euxo pipefail {0}
+    - name: steps::cargo_nextest
+      run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
+      shell: bash -euxo pipefail {0}
+    - name: steps::cleanup_cargo_config
+      if: always()
+      run: |
+        rm -rf ./../.cargo
+      shell: bash -euxo pipefail {0}
+    timeout-minutes: 60
+  run_tests_linux:
+    if: github.repository_owner == 'zed-industries'
+    runs-on: namespace-profile-16x32-ubuntu-2204
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_cargo_config
+      run: |
+        mkdir -p ./../.cargo
+        cp ./.cargo/ci-config.toml ./../.cargo/config.toml
+      shell: bash -euxo pipefail {0}
+    - name: steps::setup_linux
+      run: ./script/linux
+      shell: bash -euxo pipefail {0}
+    - name: steps::install_mold
+      run: ./script/install-mold
+      shell: bash -euxo pipefail {0}
+    - name: steps::setup_node
+      uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
+      with:
+        node-version: '20'
+    - name: steps::clippy
+      run: ./script/clippy
+      shell: bash -euxo pipefail {0}
+    - name: steps::cargo_install_nextest
+      run: cargo install cargo-nextest --locked
+      shell: bash -euxo pipefail {0}
+    - name: steps::clear_target_dir_if_large
+      run: ./script/clear-target-dir-if-larger-than 100
+      shell: bash -euxo pipefail {0}
+    - name: steps::cargo_nextest
+      run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
+      shell: bash -euxo pipefail {0}
+    - name: steps::cleanup_cargo_config
+      if: always()
+      run: |
+        rm -rf ./../.cargo
+      shell: bash -euxo pipefail {0}
+    timeout-minutes: 60
+  run_tests_windows:
+    if: github.repository_owner == 'zed-industries'
+    runs-on: self-32vcpu-windows-2022
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_cargo_config
+      run: |
+        New-Item -ItemType Directory -Path "./../.cargo" -Force
+        Copy-Item -Path "./.cargo/ci-config.toml" -Destination "./../.cargo/config.toml"
+      shell: pwsh
+    - name: steps::setup_node
+      uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
+      with:
+        node-version: '20'
+    - name: steps::clippy
+      run: ./script/clippy.ps1
+      shell: pwsh
+    - name: steps::cargo_install_nextest
+      run: cargo install cargo-nextest --locked
+      shell: pwsh
+    - name: steps::clear_target_dir_if_large
+      run: ./script/clear-target-dir-if-larger-than.ps1 250
+      shell: pwsh
+    - name: steps::cargo_nextest
+      run: cargo nextest run --workspace --no-fail-fast --failure-output immediate-final
+      shell: pwsh
+    - name: steps::cleanup_cargo_config
+      if: always()
+      run: |
+        Remove-Item -Recurse -Path "./../.cargo" -Force -ErrorAction SilentlyContinue
+      shell: pwsh
+    timeout-minutes: 60
+  check_scripts:
+    if: github.repository_owner == 'zed-industries'
+    runs-on: namespace-profile-2x4-ubuntu-2404
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: run_tests::check_scripts::run_shellcheck
+      run: ./script/shellcheck-scripts error
+      shell: bash -euxo pipefail {0}
+    - id: get_actionlint
+      name: run_tests::check_scripts::download_actionlint
+      run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
+      shell: bash -euxo pipefail {0}
+    - name: run_tests::check_scripts::run_actionlint
+      run: |
+        ${{ steps.get_actionlint.outputs.executable }} -color
+      shell: bash -euxo pipefail {0}
+    - name: run_tests::check_scripts::check_xtask_workflows
+      run: |
+        cargo xtask workflows
+        if ! git diff --exit-code .github; then
+          echo "Error: .github directory has uncommitted changes after running 'cargo xtask workflows'"
+          echo "Please run 'cargo xtask workflows' locally and commit the changes"
+          exit 1
+        fi
+      shell: bash -euxo pipefail {0}
+    timeout-minutes: 60
+  create_draft_release:
+    if: github.repository_owner == 'zed-industries'
+    runs-on: namespace-profile-2x4-ubuntu-2404
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+        fetch-depth: 25
+        ref: ${{ github.ref }}
+    - name: script/determine-release-channel
+      run: script/determine-release-channel
+      shell: bash -euxo pipefail {0}
+    - name: mkdir -p target/
+      run: mkdir -p target/
+      shell: bash -euxo pipefail {0}
+    - name: script/draft-release-notes "$RELEASE_VERSION" "$RELEASE_CHANNEL" > target/release-notes.md || true
+      run: script/draft-release-notes "$RELEASE_VERSION" "$RELEASE_CHANNEL" > target/release-notes.md || true
+      shell: bash -euxo pipefail {0}
+    - name: script/create-draft-release target/release-notes.md
+      run: script/create-draft-release target/release-notes.md
+      shell: bash -euxo pipefail {0}
+    timeout-minutes: 60
+  bundle_linux_arm64:
+    needs:
+    - run_tests_linux
+    - check_scripts
+    runs-on: namespace-profile-8x32-ubuntu-2004-arm-m4
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_sentry
+      uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
+      with:
+        token: ${{ secrets.SENTRY_AUTH_TOKEN }}
+    - name: steps::setup_linux
+      run: ./script/linux
+      shell: bash -euxo pipefail {0}
+    - name: steps::install_mold
+      run: ./script/install-mold
+      shell: bash -euxo pipefail {0}
+    - name: ./script/bundle-linux
+      run: ./script/bundle-linux
+      shell: bash -euxo pipefail {0}
+    - name: '@actions/upload-artifact zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
+        path: target/release/zed-*.tar.gz
+        if-no-files-found: error
+    - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
+        path: target/zed-remote-server-*.gz
+        if-no-files-found: error
+    outputs:
+      zed: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
+    timeout-minutes: 60
+  bundle_linux_x86_64:
+    needs:
+    - run_tests_linux
+    - check_scripts
+    runs-on: namespace-profile-32x64-ubuntu-2004
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_sentry
+      uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
+      with:
+        token: ${{ secrets.SENTRY_AUTH_TOKEN }}
+    - name: steps::setup_linux
+      run: ./script/linux
+      shell: bash -euxo pipefail {0}
+    - name: steps::install_mold
+      run: ./script/install-mold
+      shell: bash -euxo pipefail {0}
+    - name: ./script/bundle-linux
+      run: ./script/bundle-linux
+      shell: bash -euxo pipefail {0}
+    - name: '@actions/upload-artifact zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
+        path: target/release/zed-*.tar.gz
+        if-no-files-found: error
+    - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
+        path: target/zed-remote-server-*.gz
+        if-no-files-found: error
+    outputs:
+      zed: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
+    timeout-minutes: 60
+  bundle_mac_arm64:
+    needs:
+    - run_tests_mac
+    - check_scripts
+    runs-on: self-mini-macos
+    env:
+      MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
+      MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
+      APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
+      APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
+      APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_node
+      uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
+      with:
+        node-version: '20'
+    - name: steps::setup_sentry
+      uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
+      with:
+        token: ${{ secrets.SENTRY_AUTH_TOKEN }}
+    - name: steps::clear_target_dir_if_large
+      run: ./script/clear-target-dir-if-larger-than 300
+      shell: bash -euxo pipefail {0}
+    - name: run_bundling::bundle_mac
+      run: ./script/bundle-mac aarch64-apple-darwin
+      shell: bash -euxo pipefail {0}
+    - name: '@actions/upload-artifact Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
+        path: target/aarch64-apple-darwin/release/Zed.dmg
+        if-no-files-found: error
+    - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-aarch64.gz'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-aarch64.gz
+        path: target/zed-remote-server-macos-aarch64.gz
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-aarch64.gz
+    timeout-minutes: 60
+  bundle_mac_x86_64:
+    needs:
+    - run_tests_mac
+    - check_scripts
+    runs-on: self-mini-macos
+    env:
+      MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
+      MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
+      APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
+      APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
+      APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_node
+      uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
+      with:
+        node-version: '20'
+    - name: steps::setup_sentry
+      uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
+      with:
+        token: ${{ secrets.SENTRY_AUTH_TOKEN }}
+    - name: steps::clear_target_dir_if_large
+      run: ./script/clear-target-dir-if-larger-than 300
+      shell: bash -euxo pipefail {0}
+    - name: run_bundling::bundle_mac
+      run: ./script/bundle-mac x86_64-apple-darwin
+      shell: bash -euxo pipefail {0}
+    - name: '@actions/upload-artifact Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
+        path: target/x86_64-apple-darwin/release/Zed.dmg
+        if-no-files-found: error
+    - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-x86_64.gz'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-x86_64.gz
+        path: target/zed-remote-server-macos-x86_64.gz
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-x86_64.gz
+    timeout-minutes: 60
+  bundle_windows_arm64:
+    needs:
+    - run_tests_windows
+    - check_scripts
+    runs-on: self-32vcpu-windows-2022
+    env:
+      AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
+      AZURE_CLIENT_ID: ${{ secrets.AZURE_SIGNING_CLIENT_ID }}
+      AZURE_CLIENT_SECRET: ${{ secrets.AZURE_SIGNING_CLIENT_SECRET }}
+      ACCOUNT_NAME: ${{ vars.AZURE_SIGNING_ACCOUNT_NAME }}
+      CERT_PROFILE_NAME: ${{ vars.AZURE_SIGNING_CERT_PROFILE_NAME }}
+      ENDPOINT: ${{ vars.AZURE_SIGNING_ENDPOINT }}
+      FILE_DIGEST: SHA256
+      TIMESTAMP_DIGEST: SHA256
+      TIMESTAMP_SERVER: http://timestamp.acs.microsoft.com
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_sentry
+      uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
+      with:
+        token: ${{ secrets.SENTRY_AUTH_TOKEN }}
+    - name: run_bundling::bundle_windows
+      run: script/bundle-windows.ps1 -Architecture aarch64
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    - name: '@actions/upload-artifact Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.exe'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.exe
+        path: ${{ env.SETUP_PATH }}
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.exe
+    timeout-minutes: 60
+  bundle_windows_x86_64:
+    needs:
+    - run_tests_windows
+    - check_scripts
+    runs-on: self-32vcpu-windows-2022
+    env:
+      AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
+      AZURE_CLIENT_ID: ${{ secrets.AZURE_SIGNING_CLIENT_ID }}
+      AZURE_CLIENT_SECRET: ${{ secrets.AZURE_SIGNING_CLIENT_SECRET }}
+      ACCOUNT_NAME: ${{ vars.AZURE_SIGNING_ACCOUNT_NAME }}
+      CERT_PROFILE_NAME: ${{ vars.AZURE_SIGNING_CERT_PROFILE_NAME }}
+      ENDPOINT: ${{ vars.AZURE_SIGNING_ENDPOINT }}
+      FILE_DIGEST: SHA256
+      TIMESTAMP_DIGEST: SHA256
+      TIMESTAMP_SERVER: http://timestamp.acs.microsoft.com
+    steps:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: steps::setup_sentry
+      uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
+      with:
+        token: ${{ secrets.SENTRY_AUTH_TOKEN }}
+    - name: run_bundling::bundle_windows
+      run: script/bundle-windows.ps1 -Architecture x86_64
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    - name: '@actions/upload-artifact Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.exe'
+      uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+      with:
+        name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.exe
+        path: ${{ env.SETUP_PATH }}
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.exe
+    timeout-minutes: 60
+  upload_release_assets:
+    needs:
+    - create_draft_release
+    - bundle_linux_arm64
+    - bundle_linux_x86_64
+    - bundle_mac_arm64
+    - bundle_mac_x86_64
+    - bundle_windows_arm64
+    - bundle_windows_x86_64
+    runs-on: namespace-profile-4x8-ubuntu-2204
+    steps:
+    - name: release::upload_release_assets::download_workflow_artifacts
+      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
+      with:
+        path: ./artifacts/
+    - name: ls -lR ./artifacts
+      run: ls -lR ./artifacts
+      shell: bash -euxo pipefail {0}
+    - name: release::upload_release_assets::prep_release_artifacts
+      run: |-
+        mkdir -p release-artifacts/
+
+        mv ./artifacts/${{ needs.bundle_mac_x86_64.outputs.zed }}/* release-artifacts/Zed-x86_64.dmg
+        mv ./artifacts/${{ needs.bundle_mac_arm64.outputs.zed }}/* release-artifacts/Zed-aarch64.dmg
+        mv ./artifacts/${{ needs.bundle_windows_x86_64.outputs.zed }}/* release-artifacts/Zed-x86_64.exe
+        mv ./artifacts/${{ needs.bundle_windows_arm64.outputs.zed }}/* release-artifacts/Zed-aarch64.exe
+        mv ./artifacts/${{ needs.bundle_linux_arm64.outputs.zed }}/* release-artifacts/zed-linux-aarch64.tar.gz
+        mv ./artifacts/${{ needs.bundle_linux_x86_64.outputs.zed }}/* release-artifacts/zed-linux-x86_64.tar.gz
+        mv ./artifacts/${{ needs.bundle_linux_x86_64.outputs.remote-server }}/* release-artifacts/zed-remote-server-linux-x86_64.gz
+        mv ./artifacts/${{ needs.bundle_linux_arm64.outputs.remote-server }}/* release-artifacts/zed-remote-server-linux-aarch64.gz
+        mv ./artifacts/${{ needs.bundle_mac_x86_64.outputs.remote-server }}/* release-artifacts/zed-remote-server-macos-x86_64.gz
+        mv ./artifacts/${{ needs.bundle_mac_arm64.outputs.remote-server }}/* release-artifacts/zed-remote-server-macos-aarch64.gz
+      shell: bash -euxo pipefail {0}
+    - name: gh release upload "$GITHUB_REF_NAME" --repo=zed-industries/zed release-artifacts/*
+      run: gh release upload "$GITHUB_REF_NAME" --repo=zed-industries/zed release-artifacts/*
+      shell: bash -euxo pipefail {0}
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+  auto_release_preview:
+    needs:
+    - upload_release_assets
+    if: |
+      false
+      && startsWith(github.ref, 'refs/tags/v')
+      && endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
+    runs-on: namespace-profile-2x4-ubuntu-2404
+    steps:
+    - name: gh release edit "$GITHUB_REF_NAME" --repo=zed-industries/zed --draft=false
+      run: gh release edit "$GITHUB_REF_NAME" --repo=zed-industries/zed --draft=false
+      shell: bash -euxo pipefail {0}
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+    - name: release::create_sentry_release
+      uses: getsentry/action-release@526942b68292201ac6bbb99b9a0747d4abee354c
+      with:
+        environment: production
+      env:
+        SENTRY_ORG: zed-dev
+        SENTRY_PROJECT: zed
+        SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
+  cancel-in-progress: true

.github/workflows/run_bundling.yml 🔗

@@ -48,11 +48,16 @@ jobs:
       with:
         name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
         path: target/x86_64-apple-darwin/release/Zed.dmg
+        if-no-files-found: error
     - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-x86_64.gz'
       uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
       with:
         name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-x86_64.gz
         path: target/zed-remote-server-macos-x86_64.gz
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-x86_64.gz
     timeout-minutes: 60
   bundle_mac_arm64:
     if: |-
@@ -89,11 +94,16 @@ jobs:
       with:
         name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
         path: target/aarch64-apple-darwin/release/Zed.dmg
+        if-no-files-found: error
     - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-aarch64.gz'
       uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
       with:
         name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-aarch64.gz
         path: target/zed-remote-server-macos-aarch64.gz
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-macos-aarch64.gz
     timeout-minutes: 60
   bundle_linux_x86_64:
     if: |-
@@ -123,11 +133,16 @@ jobs:
       with:
         name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
         path: target/release/zed-*.tar.gz
+        if-no-files-found: error
     - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz'
       uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
       with:
         name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
-        path: target/release/zed-remote-server-*.tar.gz
+        path: target/zed-remote-server-*.gz
+        if-no-files-found: error
+    outputs:
+      zed: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
     timeout-minutes: 60
   bundle_linux_arm64:
     if: |-
@@ -157,11 +172,16 @@ jobs:
       with:
         name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
         path: target/release/zed-*.tar.gz
+        if-no-files-found: error
     - name: '@actions/upload-artifact zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz'
       uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
       with:
         name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
-        path: target/release/zed-remote-server-*.tar.gz
+        path: target/zed-remote-server-*.gz
+        if-no-files-found: error
+    outputs:
+      zed: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
+      remote-server: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
     timeout-minutes: 60
   bundle_windows_x86_64:
     if: |-
@@ -196,6 +216,9 @@ jobs:
       with:
         name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.exe
         path: ${{ env.SETUP_PATH }}
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.exe
     timeout-minutes: 60
   bundle_windows_arm64:
     if: |-
@@ -230,6 +253,9 @@ jobs:
       with:
         name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.exe
         path: ${{ env.SETUP_PATH }}
+        if-no-files-found: error
+    outputs:
+      zed: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.exe
     timeout-minutes: 60
 concurrency:
   group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}

tooling/xtask/src/tasks/workflows.rs 🔗

@@ -9,6 +9,7 @@ mod nix_build;
 mod release_nightly;
 mod run_bundling;
 
+mod release;
 mod run_tests;
 mod runners;
 mod steps;
@@ -25,6 +26,7 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
         ("run_bundling.yml", run_bundling::run_bundling()),
         ("release_nightly.yml", release_nightly::release_nightly()),
         ("run_tests.yml", run_tests::run_tests()),
+        ("release.yml", release::release()),
         ("compare_perf.yml", compare_perf::compare_perf()),
     ];
     fs::create_dir_all(dir)

tooling/xtask/src/tasks/workflows/release.rs 🔗

@@ -0,0 +1,207 @@
+use gh_workflow::{Event, Expression, Push, Run, Step, Use, Workflow};
+
+use crate::tasks::workflows::{
+    run_bundling, run_tests, runners,
+    steps::{self, NamedJob, dependant_job, named, release_job},
+    vars,
+};
+
+pub(crate) fn release() -> Workflow {
+    let macos_tests = run_tests::run_platform_tests(runners::Platform::Mac);
+    let linux_tests = run_tests::run_platform_tests(runners::Platform::Linux);
+    let windows_tests = run_tests::run_platform_tests(runners::Platform::Windows);
+    let check_scripts = run_tests::check_scripts();
+
+    let create_draft_release = create_draft_release();
+
+    let bundle = ReleaseBundleJobs {
+        linux_arm64: bundle_linux_arm64(&[&linux_tests, &check_scripts]),
+        linux_x86_64: bundle_linux_x86_64(&[&linux_tests, &check_scripts]),
+        mac_arm64: bundle_mac_arm64(&[&macos_tests, &check_scripts]),
+        mac_x86_64: bundle_mac_x86_64(&[&macos_tests, &check_scripts]),
+        windows_arm64: bundle_windows_arm64(&[&windows_tests, &check_scripts]),
+        windows_x86_64: bundle_windows_x86_64(&[&windows_tests, &check_scripts]),
+    };
+
+    let upload_release_assets = upload_release_assets(&[&create_draft_release], &bundle);
+
+    let auto_release_preview = auto_release_preview(&[&upload_release_assets]);
+
+    named::workflow()
+        .on(Event::default().push(Push::default().tags(vec!["v*".to_string()])))
+        .concurrency(vars::one_workflow_per_non_main_branch())
+        .add_job(macos_tests.name, macos_tests.job)
+        .add_job(linux_tests.name, linux_tests.job)
+        .add_job(windows_tests.name, windows_tests.job)
+        .add_job(check_scripts.name, check_scripts.job)
+        .add_job(create_draft_release.name, create_draft_release.job)
+        .add_job(bundle.linux_arm64.name, bundle.linux_arm64.job)
+        .add_job(bundle.linux_x86_64.name, bundle.linux_x86_64.job)
+        .add_job(bundle.mac_arm64.name, bundle.mac_arm64.job)
+        .add_job(bundle.mac_x86_64.name, bundle.mac_x86_64.job)
+        .add_job(bundle.windows_arm64.name, bundle.windows_arm64.job)
+        .add_job(bundle.windows_x86_64.name, bundle.windows_x86_64.job)
+        .add_job(upload_release_assets.name, upload_release_assets.job)
+        .add_job(auto_release_preview.name, auto_release_preview.job)
+}
+
+fn auto_release_preview(deps: &[&NamedJob; 1]) -> NamedJob {
+    named::job(
+        dependant_job(deps)
+            .runs_on(runners::LINUX_SMALL)
+            .cond(Expression::new(indoc::indoc!(
+                r#"
+                false
+                && startsWith(github.ref, 'refs/tags/v')
+                && endsWith(github.ref, '-pre') && !endsWith(github.ref, '.0-pre')
+            "# // todo(ci-release) enable
+            )))
+            .add_step(
+                steps::script(
+                    r#"gh release edit "$GITHUB_REF_NAME" --repo=zed-industries/zed --draft=false"#,
+                )
+                .add_env(("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")),
+            )
+            .add_step(create_sentry_release()),
+    )
+}
+
+fn create_sentry_release() -> Step<Use> {
+    named::uses(
+        "getsentry",
+        "action-release",
+        "526942b68292201ac6bbb99b9a0747d4abee354c", // v3
+    )
+    .add_env(("SENTRY_ORG", "zed-dev"))
+    .add_env(("SENTRY_PROJECT", "zed"))
+    .add_env(("SENTRY_AUTH_TOKEN", "${{ secrets.SENTRY_AUTH_TOKEN }}"))
+    .add_with(("environment", "production"))
+}
+
+struct ReleaseBundleJobs {
+    linux_arm64: NamedJob,
+    linux_x86_64: NamedJob,
+    mac_arm64: NamedJob,
+    mac_x86_64: NamedJob,
+    windows_arm64: NamedJob,
+    windows_x86_64: NamedJob,
+}
+
+fn upload_release_assets(deps: &[&NamedJob], bundle_jobs: &ReleaseBundleJobs) -> NamedJob {
+    fn download_workflow_artifacts() -> Step<Use> {
+        named::uses(
+            "actions",
+            "download-artifact",
+            "018cc2cf5baa6db3ef3c5f8a56943fffe632ef53", // v6.0.0
+        )
+        .add_with(("path", "./artifacts/"))
+    }
+
+    fn prep_release_artifacts(bundle: &ReleaseBundleJobs) -> Step<Run> {
+        let assets = [
+            (&bundle.mac_x86_64.name, "zed", "Zed-x86_64.dmg"),
+            (&bundle.mac_arm64.name, "zed", "Zed-aarch64.dmg"),
+            (&bundle.windows_x86_64.name, "zed", "Zed-x86_64.exe"),
+            (&bundle.windows_arm64.name, "zed", "Zed-aarch64.exe"),
+            (&bundle.linux_arm64.name, "zed", "zed-linux-aarch64.tar.gz"),
+            (&bundle.linux_x86_64.name, "zed", "zed-linux-x86_64.tar.gz"),
+            (
+                &bundle.linux_x86_64.name,
+                "remote-server",
+                "zed-remote-server-linux-x86_64.gz",
+            ),
+            (
+                &bundle.linux_arm64.name,
+                "remote-server",
+                "zed-remote-server-linux-aarch64.gz",
+            ),
+            (
+                &bundle.mac_x86_64.name,
+                "remote-server",
+                "zed-remote-server-macos-x86_64.gz",
+            ),
+            (
+                &bundle.mac_arm64.name,
+                "remote-server",
+                "zed-remote-server-macos-aarch64.gz",
+            ),
+        ];
+
+        let mut script_lines = vec!["mkdir -p release-artifacts/\n".to_string()];
+        for (job_name, artifact_kind, release_artifact_name) in assets {
+            let artifact_path =
+                ["${{ needs.", job_name, ".outputs.", artifact_kind, " }}"].join("");
+            let mv_command = format!(
+                "mv ./artifacts/{artifact_path}/* release-artifacts/{release_artifact_name}"
+            );
+            script_lines.push(mv_command)
+        }
+
+        named::bash(&script_lines.join("\n"))
+    }
+
+    let mut deps = deps.to_vec();
+    deps.extend([
+        &bundle_jobs.linux_arm64,
+        &bundle_jobs.linux_x86_64,
+        &bundle_jobs.mac_arm64,
+        &bundle_jobs.mac_x86_64,
+        &bundle_jobs.windows_arm64,
+        &bundle_jobs.windows_x86_64,
+    ]);
+
+    named::job(
+        dependant_job(&deps)
+            .runs_on(runners::LINUX_MEDIUM)
+            .add_step(download_workflow_artifacts())
+            .add_step(steps::script("ls -lR ./artifacts"))
+            .add_step(prep_release_artifacts(bundle_jobs))
+            .add_step(
+                steps::script("gh release upload \"$GITHUB_REF_NAME\" --repo=zed-industries/zed release-artifacts/*")
+                    .add_env(("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")),
+            ),
+    )
+}
+
+fn create_draft_release() -> NamedJob {
+    named::job(
+        release_job(&[])
+            .runs_on(runners::LINUX_SMALL)
+            // We need to fetch more than one commit so that `script/draft-release-notes`
+            // is able to diff between the current and previous tag.
+            //
+            // 25 was chosen arbitrarily.
+            .add_step(
+                steps::checkout_repo()
+                    .add_with(("fetch-depth", 25))
+                    .add_with(("clean", false))
+                    .add_with(("ref", "${{ github.ref }}")),
+            )
+            .add_step(steps::script("script/determine-release-channel")) // export RELEASE_CHANNEL and RELEASE_VERSION
+            .add_step(steps::script("mkdir -p target/"))
+            .add_step(steps::script(r#"script/draft-release-notes "$RELEASE_VERSION" "$RELEASE_CHANNEL" > target/release-notes.md || true"#))
+            .add_step(steps::script("script/create-draft-release target/release-notes.md")),
+    )
+}
+
+fn bundle_mac_x86_64(deps: &[&NamedJob]) -> NamedJob {
+    named::job(run_bundling::bundle_mac_job(runners::Arch::X86_64, deps))
+}
+fn bundle_mac_arm64(deps: &[&NamedJob]) -> NamedJob {
+    named::job(run_bundling::bundle_mac_job(runners::Arch::ARM64, deps))
+}
+fn bundle_linux_x86_64(deps: &[&NamedJob]) -> NamedJob {
+    named::job(run_bundling::bundle_linux_job(runners::Arch::X86_64, deps))
+}
+fn bundle_linux_arm64(deps: &[&NamedJob]) -> NamedJob {
+    named::job(run_bundling::bundle_linux_job(runners::Arch::ARM64, deps))
+}
+fn bundle_windows_x86_64(deps: &[&NamedJob]) -> NamedJob {
+    named::job(run_bundling::bundle_windows_job(
+        runners::Arch::X86_64,
+        deps,
+    ))
+}
+fn bundle_windows_arm64(deps: &[&NamedJob]) -> NamedJob {
+    named::job(run_bundling::bundle_windows_job(runners::Arch::ARM64, deps))
+}

tooling/xtask/src/tasks/workflows/run_bundling.rs 🔗

@@ -1,10 +1,11 @@
 use crate::tasks::workflows::{
-    steps::{FluentBuilder, named},
+    steps::{FluentBuilder, NamedJob, dependant_job, named},
     vars::{mac_bundle_envs, windows_bundle_envs},
 };
 
 use super::{runners, steps, vars};
 use gh_workflow::*;
+use indexmap::IndexMap;
 
 pub fn run_bundling() -> Workflow {
     named::workflow()
@@ -22,32 +23,47 @@ pub fn run_bundling() -> Workflow {
         .add_env(("RUST_BACKTRACE", "1"))
         .add_env(("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED))
         .add_env(("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT))
-        .add_job("bundle_mac_x86_64", bundle_mac_job(runners::Arch::X86_64))
-        .add_job("bundle_mac_arm64", bundle_mac_job(runners::Arch::ARM64))
-        .add_job("bundle_linux_x86_64", bundle_linux(runners::Arch::X86_64))
-        .add_job("bundle_linux_arm64", bundle_linux(runners::Arch::ARM64))
+        .add_job(
+            "bundle_mac_x86_64",
+            bundle_mac_job(runners::Arch::X86_64, &[]),
+        )
+        .add_job(
+            "bundle_mac_arm64",
+            bundle_mac_job(runners::Arch::ARM64, &[]),
+        )
+        .add_job(
+            "bundle_linux_x86_64",
+            bundle_linux_job(runners::Arch::X86_64, &[]),
+        )
+        .add_job(
+            "bundle_linux_arm64",
+            bundle_linux_job(runners::Arch::ARM64, &[]),
+        )
         .add_job(
             "bundle_windows_x86_64",
-            bundle_windows_job(runners::Arch::X86_64),
+            bundle_windows_job(runners::Arch::X86_64, &[]),
         )
         .add_job(
             "bundle_windows_arm64",
-            bundle_windows_job(runners::Arch::ARM64),
+            bundle_windows_job(runners::Arch::ARM64, &[]),
         )
 }
 
-fn bundle_job() -> Job {
-    Job::default()
-        .cond(Expression::new(
+fn bundle_job(deps: &[&NamedJob]) -> Job {
+    dependant_job(deps)
+        .when(deps.len() == 0, |job|
+                job.cond(Expression::new(
                 "(github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
                  (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))",
-            ))
+            )))
         .timeout_minutes(60u32)
 }
 
-fn bundle_mac_job(arch: runners::Arch) -> Job {
+pub(crate) fn bundle_mac_job(arch: runners::Arch, deps: &[&NamedJob]) -> Job {
     use vars::GITHUB_SHA;
-    bundle_job()
+    let artifact_name = format!("Zed_{GITHUB_SHA}-{arch}.dmg");
+    let remote_server_artifact_name = format!("zed-remote-server-{GITHUB_SHA}-macos-{arch}.gz");
+    bundle_job(deps)
         .runs_on(runners::MAC_DEFAULT)
         .envs(mac_bundle_envs())
         .add_step(steps::checkout_repo())
@@ -56,27 +72,35 @@ fn bundle_mac_job(arch: runners::Arch) -> Job {
         .add_step(steps::clear_target_dir_if_large(runners::Platform::Mac))
         .add_step(bundle_mac(arch))
         .add_step(steps::upload_artifact(
-            &format!("Zed_{GITHUB_SHA}-{arch}.dmg"),
+            &artifact_name,
             &format!("target/{arch}-apple-darwin/release/Zed.dmg"),
         ))
         .add_step(steps::upload_artifact(
-            &format!("zed-remote-server-{GITHUB_SHA}-macos-{arch}.gz"),
+            &remote_server_artifact_name,
             &format!("target/zed-remote-server-macos-{arch}.gz"),
         ))
+        .outputs(
+            [
+                ("zed".to_string(), artifact_name),
+                ("remote-server".to_string(), remote_server_artifact_name),
+            ]
+            .into_iter()
+            .collect::<IndexMap<_, _>>(),
+        )
 }
 
 pub fn bundle_mac(arch: runners::Arch) -> Step<Run> {
     named::bash(&format!("./script/bundle-mac {arch}-apple-darwin"))
 }
 
-fn bundle_linux(arch: runners::Arch) -> Job {
+pub(crate) fn bundle_linux_job(arch: runners::Arch, deps: &[&NamedJob]) -> Job {
     let artifact_name = format!("zed-{}-{}.tar.gz", vars::GITHUB_SHA, arch.triple());
     let remote_server_artifact_name = format!(
         "zed-remote-server-{}-{}.tar.gz",
         vars::GITHUB_SHA,
         arch.triple()
     );
-    bundle_job()
+    bundle_job(deps)
         .runs_on(arch.linux_bundler())
         .add_step(steps::checkout_repo())
         .add_step(steps::setup_sentry())
@@ -88,22 +112,36 @@ fn bundle_linux(arch: runners::Arch) -> Job {
         ))
         .add_step(steps::upload_artifact(
             &remote_server_artifact_name,
-            "target/release/zed-remote-server-*.tar.gz",
+            "target/zed-remote-server-*.gz",
         ))
+        .outputs(
+            [
+                ("zed".to_string(), artifact_name),
+                ("remote-server".to_string(), remote_server_artifact_name),
+            ]
+            .into_iter()
+            .collect::<IndexMap<_, _>>(),
+        )
 }
 
-fn bundle_windows_job(arch: runners::Arch) -> Job {
+pub(crate) fn bundle_windows_job(arch: runners::Arch, deps: &[&NamedJob]) -> Job {
     use vars::GITHUB_SHA;
-    bundle_job()
+    let artifact_name = format!("Zed_{GITHUB_SHA}-{arch}.exe");
+    bundle_job(deps)
         .runs_on(runners::WINDOWS_DEFAULT)
         .envs(windows_bundle_envs())
         .add_step(steps::checkout_repo())
         .add_step(steps::setup_sentry())
         .add_step(bundle_windows(arch))
         .add_step(steps::upload_artifact(
-            &format!("Zed_{GITHUB_SHA}-{arch}.exe"),
+            &artifact_name,
             "${{ env.SETUP_PATH }}",
         ))
+        .outputs(
+            [("zed".to_string(), artifact_name)]
+                .into_iter()
+                .collect::<IndexMap<_, _>>(),
+        )
 }
 
 fn bundle_windows(arch: runners::Arch) -> Step<Run> {

tooling/xtask/src/tasks/workflows/run_tests.rs 🔗

@@ -302,9 +302,6 @@ pub(crate) fn run_platform_tests(platform: Platform) -> NamedJob {
     NamedJob {
         name: format!("run_tests_{platform}"),
         job: release_job(&[])
-            .cond(Expression::new(
-                "github.repository_owner == 'zed-industries'",
-            ))
             .runs_on(runner)
             .add_step(steps::checkout_repo())
             .add_step(steps::setup_cargo_config(platform))
@@ -436,7 +433,7 @@ fn check_docs() -> NamedJob {
     )
 }
 
-fn check_scripts() -> NamedJob {
+pub(crate) fn check_scripts() -> NamedJob {
     fn download_actionlint() -> Step<Run> {
         named::bash(
             "bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)",

tooling/xtask/src/tasks/workflows/steps.rs 🔗

@@ -95,6 +95,7 @@ pub fn upload_artifact(name: &str, path: &str) -> Step<Use> {
         )
         .add_with(("name", name))
         .add_with(("path", path))
+        .add_with(("if-no-files-found", "error"))
 }
 
 pub fn clear_target_dir_if_large(platform: Platform) -> Step<Run> {
@@ -156,11 +157,15 @@ pub(crate) struct NamedJob {
 // }
 
 pub(crate) fn release_job(deps: &[&NamedJob]) -> Job {
-    let job = Job::default()
+    dependant_job(deps)
         .cond(Expression::new(
             "github.repository_owner == 'zed-industries'",
         ))
-        .timeout_minutes(60u32);
+        .timeout_minutes(60u32)
+}
+
+pub(crate) fn dependant_job(deps: &[&NamedJob]) -> Job {
+    let job = Job::default();
     if deps.len() > 0 {
         job.needs(deps.iter().map(|j| j.name.clone()).collect::<Vec<_>>())
     } else {

tooling/xtask/src/tasks/workflows/vars.rs 🔗

@@ -1,6 +1,6 @@
 use std::cell::RefCell;
 
-use gh_workflow::{Env, Expression};
+use gh_workflow::{Concurrency, Env, Expression};
 
 use crate::tasks::workflows::steps::NamedJob;
 
@@ -62,6 +62,12 @@ pub fn windows_bundle_envs() -> Env {
         .add("TIMESTAMP_SERVER", "http://timestamp.acs.microsoft.com")
 }
 
+pub(crate) fn one_workflow_per_non_main_branch() -> Concurrency {
+    Concurrency::default()
+        .group("${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}")
+        .cancel_in_progress(true)
+}
+
 // Represents a pattern to check for changed files and corresponding output variable
 pub(crate) struct PathCondition {
     pub name: &'static str,