Use gh-workflow for the `run-bundling` aspects of CI.yml (#41304)

Conrad Irwin and Ben Kunkle created

To help make our GitHub Actions easier to understand, we're planning to
split the existing `ci.yml` into three separate workflows:

* run_bundling.yml (this PR)
* run_tests.yml 
* make_release.yml

To avoid the duplication that this might otherwise cause, we're planning
to write the workflows with gh-workflow, and use rust instead of
encoding logic in YAML conditions.

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>

Change summary

.github/workflows/ci.yml                          |  86 -------
.github/workflows/danger.yml                      |  13 
.github/workflows/nix_build.yml                   |  23 +
.github/workflows/release_nightly.yml             |   2 
.github/workflows/run_bundling.yml                | 194 +++++++++++++++++
Cargo.lock                                        |   4 
Cargo.toml                                        |   2 
tooling/xtask/src/tasks/workflows.rs              |  23 +
tooling/xtask/src/tasks/workflows/danger.rs       |  53 ++++
tooling/xtask/src/tasks/workflows/nix_build.rs    | 101 +++++---
tooling/xtask/src/tasks/workflows/run_bundling.rs | 128 +++++++++++
tooling/xtask/src/tasks/workflows/runners.rs      |  37 +++
tooling/xtask/src/tasks/workflows/steps.rs        | 162 ++++++-------
tooling/xtask/src/tasks/workflows/vars.rs         |  30 ++
14 files changed, 621 insertions(+), 237 deletions(-)

Detailed changes

.github/workflows/ci.yml 🔗

@@ -516,9 +516,7 @@ jobs:
     name: Create a macOS bundle
     runs-on:
       - self-mini-macos
-    if: |
-      ( startsWith(github.ref, 'refs/tags/v')
-      || contains(github.event.pull_request.labels.*.name, 'run-bundling') )
+    if: startsWith(github.ref, 'refs/tags/v')
     needs: [macos_tests]
     env:
       MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
@@ -552,13 +550,11 @@ jobs:
         run: script/clear-target-dir-if-larger-than 300
 
       - name: Determine version and release channel
-        if: ${{ startsWith(github.ref, 'refs/tags/v') }}
         run: |
           # This exports RELEASE_CHANNEL into env (GITHUB_ENV)
           script/determine-release-channel
 
       - name: Draft release notes
-        if: ${{ startsWith(github.ref, 'refs/tags/v') }}
         run: |
           mkdir -p target/
           # Ignore any errors that occur while drafting release notes to not fail the build.
@@ -571,25 +567,10 @@ jobs:
         run: script/bundle-mac
 
       - name: Rename binaries
-        if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
         run: |
           mv target/aarch64-apple-darwin/release/Zed.dmg target/aarch64-apple-darwin/release/Zed-aarch64.dmg
           mv target/x86_64-apple-darwin/release/Zed.dmg target/x86_64-apple-darwin/release/Zed-x86_64.dmg
 
-      - name: Upload app bundle (aarch64) to workflow run if main branch or specific label
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
-        with:
-          name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg
-          path: target/aarch64-apple-darwin/release/Zed-aarch64.dmg
-
-      - name: Upload app bundle (x86_64) to workflow run if main branch or specific label
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }}
-        with:
-          name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg
-          path: target/x86_64-apple-darwin/release/Zed-x86_64.dmg
-
       - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
         name: Upload app bundle to release
         if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }}
@@ -610,8 +591,7 @@ jobs:
     runs-on:
       - namespace-profile-16x32-ubuntu-2004 # ubuntu 20.04 for minimal glibc
     if: |
-      ( startsWith(github.ref, 'refs/tags/v')
-      || contains(github.event.pull_request.labels.*.name, 'run-bundling') )
+      ( startsWith(github.ref, 'refs/tags/v') )
     needs: [linux_tests]
     steps:
       - name: Checkout repo
@@ -628,7 +608,6 @@ jobs:
           token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
 
       - name: Determine version and release channel
-        if: startsWith(github.ref, 'refs/tags/v')
         run: |
           # This exports RELEASE_CHANNEL into env (GITHUB_ENV)
           script/determine-release-channel
@@ -636,23 +615,8 @@ jobs:
       - name: Create Linux .tar.gz bundle
         run: script/bundle-linux
 
-      - name: Upload Artifact to Workflow - zed (run-bundling)
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
-        with:
-          name: zed-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.tar.gz
-          path: target/release/zed-*.tar.gz
-
-      - name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
-        with:
-          name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-x86_64-unknown-linux-gnu.gz
-          path: target/zed-remote-server-linux-x86_64.gz
-
       - name: Upload Artifacts to release
         uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
-        if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
         with:
           draft: true
           prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
@@ -669,7 +633,6 @@ jobs:
       - namespace-profile-8x32-ubuntu-2004-arm-m4 # ubuntu 20.04 for minimal glibc
     if: |
       startsWith(github.ref, 'refs/tags/v')
-      || contains(github.event.pull_request.labels.*.name, 'run-bundling')
     needs: [linux_tests]
     steps:
       - name: Checkout repo
@@ -686,7 +649,6 @@ jobs:
           token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
 
       - name: Determine version and release channel
-        if: startsWith(github.ref, 'refs/tags/v')
         run: |
           # This exports RELEASE_CHANNEL into env (GITHUB_ENV)
           script/determine-release-channel
@@ -694,23 +656,8 @@ jobs:
       - name: Create and upload Linux .tar.gz bundles
         run: script/bundle-linux
 
-      - name: Upload Artifact to Workflow - zed (run-bundling)
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
-        with:
-          name: zed-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.tar.gz
-          path: target/release/zed-*.tar.gz
-
-      - name: Upload Artifact to Workflow - zed-remote-server (run-bundling)
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
-        with:
-          name: zed-remote-server-${{ github.event.pull_request.head.sha || github.sha }}-aarch64-unknown-linux-gnu.gz
-          path: target/zed-remote-server-linux-aarch64.gz
-
       - name: Upload Artifacts to release
         uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
-        if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
         with:
           draft: true
           prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
@@ -724,8 +671,7 @@ jobs:
     timeout-minutes: 60
     runs-on: github-8vcpu-ubuntu-2404
     if: |
-      false && ( startsWith(github.ref, 'refs/tags/v')
-      || contains(github.event.pull_request.labels.*.name, 'run-bundling') )
+      false && ( startsWith(github.ref, 'refs/tags/v') )
     needs: [linux_tests]
     name: Build Zed on FreeBSD
     steps:
@@ -776,7 +722,7 @@ jobs:
 
   nix-build:
     name: Build with Nix
-    uses: ./.github/workflows/nix.yml
+    uses: ./.github/workflows/nix_build.yml
     needs: [job_spec]
     if: github.repository_owner == 'zed-industries' &&
       (contains(github.event.pull_request.labels.*.name, 'run-nix') ||
@@ -792,8 +738,7 @@ jobs:
     name: Create a Windows installer for x86_64
     runs-on: [self-32vcpu-windows-2022]
     if: |
-      ( startsWith(github.ref, 'refs/tags/v')
-      || contains(github.event.pull_request.labels.*.name, 'run-bundling') )
+      ( startsWith(github.ref, 'refs/tags/v') )
     needs: [windows_tests]
     env:
       AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
@@ -818,7 +763,6 @@ jobs:
 
       - name: Determine version and release channel
         working-directory: ${{ env.ZED_WORKSPACE }}
-        if: ${{ startsWith(github.ref, 'refs/tags/v') }}
         run: |
           # This exports RELEASE_CHANNEL into env (GITHUB_ENV)
           script/determine-release-channel.ps1
@@ -827,16 +771,8 @@ jobs:
         working-directory: ${{ env.ZED_WORKSPACE }}
         run: script/bundle-windows.ps1
 
-      - name: Upload installer (x86_64) to Workflow - zed (run-bundling)
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
-        with:
-          name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.exe
-          path: ${{ env.SETUP_PATH }}
-
       - name: Upload Artifacts to release
         uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
-        if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
         with:
           draft: true
           prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
@@ -849,8 +785,7 @@ jobs:
     name: Create a Windows installer for aarch64
     runs-on: [self-32vcpu-windows-2022]
     if: |
-      ( startsWith(github.ref, 'refs/tags/v')
-      || contains(github.event.pull_request.labels.*.name, 'run-bundling') )
+      ( startsWith(github.ref, 'refs/tags/v') )
     needs: [windows_tests]
     env:
       AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
@@ -875,7 +810,6 @@ jobs:
 
       - name: Determine version and release channel
         working-directory: ${{ env.ZED_WORKSPACE }}
-        if: ${{ startsWith(github.ref, 'refs/tags/v') }}
         run: |
           # This exports RELEASE_CHANNEL into env (GITHUB_ENV)
           script/determine-release-channel.ps1
@@ -884,16 +818,8 @@ jobs:
         working-directory: ${{ env.ZED_WORKSPACE }}
         run: script/bundle-windows.ps1 -Architecture aarch64
 
-      - name: Upload installer (aarch64) to Workflow - zed (run-bundling)
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
-        if: contains(github.event.pull_request.labels.*.name, 'run-bundling')
-        with:
-          name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.exe
-          path: ${{ env.SETUP_PATH }}
-
       - name: Upload Artifacts to release
         uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
-        if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) }}
         with:
           draft: true
           prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}

.github/workflows/danger.yml 🔗

@@ -1,5 +1,6 @@
-# generated `cargo xtask workflows`. Do not edit.
-name: Danger
+# Generated from xtask::workflows::danger
+# Rebuild with `cargo xtask workflows`.
+name: danger
 on:
   pull_request:
     types:
@@ -16,20 +17,22 @@ jobs:
     steps:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: 'false'
     - name: steps::setup_pnpm
       uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
       with:
         version: '9'
-    - name: steps::danger::setup_node
+    - name: steps::setup_node
       uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
       with:
         node-version: '20'
         cache: pnpm
         cache-dependency-path: script/danger/pnpm-lock.yaml
-    - name: steps::danger::install_deps
+    - name: danger::install_deps
       run: pnpm install --dir script/danger
       shell: bash -euxo pipefail {0}
-    - name: steps::danger::run
+    - name: danger::run
       run: pnpm run --dir script/danger danger ci
       shell: bash -euxo pipefail {0}
       env:

.github/workflows/nix.yml → .github/workflows/nix_build.yml 🔗

@@ -1,5 +1,6 @@
-# generated `cargo xtask workflows`. Do not edit.
-name: Nix build
+# Generated from xtask::workflows::nix_build
+# Rebuild with `cargo xtask workflows`.
+name: nix_build
 on:
   workflow_call:
     inputs:
@@ -14,7 +15,7 @@ jobs:
     runs-on: namespace-profile-16x32-ubuntu-2204
     env:
       ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
-      ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_MINIDUMP_ENDPOINT }}
+      ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
       ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
       GIT_LFS_SKIP_SMUDGE: '1'
     steps:
@@ -22,18 +23,18 @@ jobs:
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
         clean: 'false'
-    - name: steps::nix::install_nix
+    - name: nix_build::install_nix
       uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f
       with:
         github_access_token: ${{ secrets.GITHUB_TOKEN }}
-    - name: steps::nix::cachix_action
+    - name: nix_build::cachix_action
       uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad
       with:
         name: zed
         authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
         pushFilter: ${{ inputs.cachix-filter }}
         cachixArgs: -v
-    - name: steps::nix::build
+    - name: nix_build::build
       run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
       shell: bash -euxo pipefail {0}
     timeout-minutes: 60
@@ -43,7 +44,7 @@ jobs:
     runs-on: self-mini-macos
     env:
       ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
-      ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_MINIDUMP_ENDPOINT }}
+      ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
       ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON: ${{ secrets.ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON }}
       GIT_LFS_SKIP_SMUDGE: '1'
     steps:
@@ -51,22 +52,22 @@ jobs:
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
         clean: 'false'
-    - name: steps::nix::set_path
+    - name: nix_build::set_path
       run: |
         echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
         echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
       shell: bash -euxo pipefail {0}
-    - name: steps::nix::cachix_action
+    - name: nix_build::cachix_action
       uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad
       with:
         name: zed
         authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
         pushFilter: ${{ inputs.cachix-filter }}
         cachixArgs: -v
-    - name: steps::nix::build
+    - name: nix_build::build
       run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
       shell: bash -euxo pipefail {0}
-    - name: steps::nix::limit_store
+    - name: nix_build::limit_store
       run: |-
         if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
             nix-collect-garbage -d || true

.github/workflows/release_nightly.yml 🔗

@@ -242,7 +242,7 @@ jobs:
     name: Build and cache Nix package
     needs: tests
     secrets: inherit
-    uses: ./.github/workflows/nix.yml
+    uses: ./.github/workflows/nix_build.yml
 
   bundle-windows-x64:
     timeout-minutes: 60

.github/workflows/run_bundling.yml 🔗

@@ -0,0 +1,194 @@
+# Generated from xtask::workflows::run_bundling
+# Rebuild with `cargo xtask workflows`.
+name: run_bundling
+env:
+  CARGO_TERM_COLOR: always
+  CARGO_INCREMENTAL: '0'
+  RUST_BACKTRACE: '1'
+  ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
+  ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+on:
+  pull_request:
+    types:
+    - labeled
+    - synchronize
+jobs:
+  bundle_mac:
+    if: |-
+      (github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
+                       (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
+    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 ${{ env.MAX_SIZE }}
+      shell: bash -euxo pipefail {0}
+      env:
+        MAX_SIZE: ${{ runner.os == 'macOS' && 300 || 100 }}
+    - name: ./script/bundle-mac
+      run: ./script/bundle-mac
+      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
+    - 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
+    timeout-minutes: 60
+  bundle_linux_x86_64:
+    if: |-
+      (github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
+                       (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
+    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: ./script/linux
+      run: ./script/linux
+      shell: bash -euxo pipefail {0}
+    - name: ./script/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
+    - 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
+    timeout-minutes: 60
+  bundle_linux_arm64:
+    if: |-
+      (github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
+                       (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
+    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: ./script/linux
+      run: ./script/linux
+      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
+    - 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
+    timeout-minutes: 60
+  bundle_windows_x86_64:
+    if: |-
+      (github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
+                       (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
+    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 }}
+    timeout-minutes: 60
+  bundle_windows_arm64:
+    if: |-
+      (github.event.action == 'labeled' && github.event.label.name == 'run-bundling') ||
+                       (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-bundling'))
+    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 }}
+    timeout-minutes: 60
+concurrency:
+  group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
+  cancel-in-progress: true

Cargo.lock 🔗

@@ -6957,7 +6957,7 @@ dependencies = [
 [[package]]
 name = "gh-workflow"
 version = "0.8.0"
-source = "git+https://github.com/zed-industries/gh-workflow?rev=a0b197dd77c0ed2390c150e601f9d4f9a0ca7105#a0b197dd77c0ed2390c150e601f9d4f9a0ca7105"
+source = "git+https://github.com/zed-industries/gh-workflow?rev=fe5fd50664bfe164277cab5c86537a2361eddbbd#fe5fd50664bfe164277cab5c86537a2361eddbbd"
 dependencies = [
  "async-trait",
  "derive_more 2.0.1",
@@ -6974,7 +6974,7 @@ dependencies = [
 [[package]]
 name = "gh-workflow-macros"
 version = "0.8.0"
-source = "git+https://github.com/zed-industries/gh-workflow?rev=a0b197dd77c0ed2390c150e601f9d4f9a0ca7105#a0b197dd77c0ed2390c150e601f9d4f9a0ca7105"
+source = "git+https://github.com/zed-industries/gh-workflow?rev=fe5fd50664bfe164277cab5c86537a2361eddbbd#fe5fd50664bfe164277cab5c86537a2361eddbbd"
 dependencies = [
  "heck 0.5.0",
  "quote",

Cargo.toml 🔗

@@ -508,7 +508,7 @@ fork = "0.2.0"
 futures = "0.3"
 futures-batch = "0.6.1"
 futures-lite = "1.13"
-gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "a0b197dd77c0ed2390c150e601f9d4f9a0ca7105" }
+gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "fe5fd50664bfe164277cab5c86537a2361eddbbd" }
 git2 = { version = "0.20.1", default-features = false }
 globset = "0.4"
 handlebars = "4.3"

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

@@ -3,12 +3,15 @@ use clap::Parser;
 use std::fs;
 use std::path::Path;
 
+mod danger;
+mod nix_build;
+// mod release;
+mod run_bundling;
+// mod run_tests;
+
 mod runners;
 mod steps;
 mod vars;
-mod workflows;
-
-use workflows::*;
 
 #[derive(Parser)]
 pub struct GenerateWorkflowArgs {}
@@ -16,7 +19,13 @@ pub struct GenerateWorkflowArgs {}
 pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
     let dir = Path::new(".github/workflows");
 
-    let workflows = vec![("danger.yml", danger()), ("nix.yml", nix())];
+    let workflows = vec![
+        ("danger.yml", danger::danger()),
+        ("nix_build.yml", nix_build::nix_build()),
+        ("run_bundling.yml", run_bundling::run_bundling()),
+        // ("run_tests.yml", run_tests::run_tests()),
+        // ("release.yml", release::release()),
+    ];
     fs::create_dir_all(dir)
         .with_context(|| format!("Failed to create directory: {}", dir.display()))?;
 
@@ -24,7 +33,11 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
         let content = workflow
             .to_string()
             .map_err(|e| anyhow::anyhow!("{}: {:?}", filename, e))?;
-        let content = format!("# generated `cargo xtask workflows`. Do not edit.\n{content}");
+        let content = format!(
+            "# Generated from xtask::workflows::{}\n# Rebuild with `cargo xtask workflows`.\n{}",
+            workflow.name.unwrap(),
+            content
+        );
         let file_path = dir.join(filename);
         fs::write(&file_path, content)?;
     }

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

@@ -0,0 +1,53 @@
+use gh_workflow::*;
+
+use crate::tasks::workflows::steps::named;
+
+use super::{runners, steps};
+
+/// Generates the danger.yml workflow
+pub fn danger() -> Workflow {
+    named::workflow()
+        .on(
+            Event::default().pull_request(PullRequest::default().add_branch("main").types([
+                PullRequestType::Opened,
+                PullRequestType::Synchronize,
+                PullRequestType::Reopened,
+                PullRequestType::Edited,
+            ])),
+        )
+        .add_job(
+            "danger",
+            Job::default()
+                .cond(Expression::new(
+                    "github.repository_owner == 'zed-industries'",
+                ))
+                .runs_on(runners::LINUX_CHEAP)
+                .add_step(steps::checkout_repo())
+                .add_step(steps::setup_pnpm())
+                .add_step(
+                    steps::setup_node()
+                        .add_with(("cache", "pnpm"))
+                        .add_with(("cache-dependency-path", "script/danger/pnpm-lock.yaml")),
+                )
+                .add_step(install_deps())
+                .add_step(run()),
+        )
+}
+
+pub fn install_deps() -> Step<Run> {
+    named::bash("pnpm install --dir script/danger")
+}
+
+pub fn run() -> Step<Run> {
+    named::bash("pnpm run --dir script/danger danger ci")
+        // This GitHub token is not used, but the value needs to be here to prevent
+        // Danger from throwing an error.
+        .add_env(("GITHUB_TOKEN", "not_a_real_token"))
+        // All requests are instead proxied through an instance of
+        // https://github.com/maxdeviant/danger-proxy that allows Danger to securely
+        // authenticate with GitHub while still being able to run on PRs from forks.
+        .add_env((
+            "DANGER_GITHUB_API_BASE_URL",
+            "https://danger-proxy.fly.dev/github",
+        ))
+}

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

@@ -1,40 +1,13 @@
+use super::{runners, steps, steps::named, vars};
 use gh_workflow::*;
 use indexmap::IndexMap;
-
-use super::{runners, steps, vars};
-
-/// Generates the danger.yml workflow
-pub fn danger() -> Workflow {
-    Workflow::default()
-        .name("Danger")
-        .on(
-            Event::default().pull_request(PullRequest::default().add_branch("main").types([
-                PullRequestType::Opened,
-                PullRequestType::Synchronize,
-                PullRequestType::Reopened,
-                PullRequestType::Edited,
-            ])),
-        )
-        .add_job(
-            "danger",
-            Job::default()
-                .cond(Expression::new(
-                    "github.repository_owner == 'zed-industries'",
-                ))
-                .runs_on(runners::LINUX_CHEAP)
-                .add_step(steps::checkout_repo())
-                .add_step(steps::setup_pnpm())
-                .add_step(steps::danger::setup_node())
-                .add_step(steps::danger::install_deps())
-                .add_step(steps::danger::run()),
-        )
-}
+use indoc::indoc;
 
 /// Generates the nix.yml workflow
-pub fn nix() -> Workflow {
+pub fn nix_build() -> Workflow {
     let env: IndexMap<_, _> = [
         ("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED),
-        ("ZED_MINIDUMP_ENDPOINT", vars::ZED_MINIDUMP_ENDPOINT),
+        ("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT),
         (
             "ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON",
             vars::ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON,
@@ -64,8 +37,7 @@ pub fn nix() -> Workflow {
         },
     );
 
-    Workflow::default()
-        .name("Nix build")
+    named::workflow()
         .on(Event::default().workflow_call(
             WorkflowCall::default()
                 .add_input(flake_output.0, flake_output.1)
@@ -80,11 +52,11 @@ pub fn nix() -> Workflow {
                     "github.repository_owner == 'zed-industries'",
                 ))
                 .runs_on(runners::LINUX_DEFAULT)
-                .env(env.clone())
+                .envs(env.clone())
                 .add_step(steps::checkout_repo().add_with(("clean", "false")))
-                .add_step(steps::nix::install_nix())
-                .add_step(steps::nix::cachix_action(&input_cachix_filter))
-                .add_step(steps::nix::build(&input_flake_output)),
+                .add_step(install_nix())
+                .add_step(cachix_action(&input_cachix_filter))
+                .add_step(build(&input_flake_output)),
         )
         .add_job(
             "nix-build-mac-arm",
@@ -95,11 +67,56 @@ pub fn nix() -> Workflow {
                     "github.repository_owner == 'zed-industries'",
                 ))
                 .runs_on(runners::MAC_DEFAULT)
-                .env(env)
+                .envs(env)
                 .add_step(steps::checkout_repo().add_with(("clean", "false")))
-                .add_step(steps::nix::set_path())
-                .add_step(steps::nix::cachix_action(&input_cachix_filter))
-                .add_step(steps::nix::build(&input_flake_output))
-                .add_step(steps::nix::limit_store()),
+                .add_step(set_path())
+                .add_step(cachix_action(&input_cachix_filter))
+                .add_step(build(&input_flake_output))
+                .add_step(limit_store()),
         )
 }
+// on our macs we manually install nix. for some reason the cachix action is running
+// under a non-login /bin/bash shell which doesn't source the proper script to add the
+// nix profile to PATH, so we manually add them here
+pub fn set_path() -> Step<Run> {
+    named::bash(indoc! {r#"
+            echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
+            echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
+        "#})
+}
+
+pub fn install_nix() -> Step<Use> {
+    named::uses(
+        "cachix",
+        "install-nix-action",
+        "02a151ada4993995686f9ed4f1be7cfbb229e56f", // v31
+    )
+    .add_with(("github_access_token", vars::GITHUB_TOKEN))
+}
+
+pub fn cachix_action(cachix_filter: &str) -> Step<Use> {
+    named::uses(
+        "cachix",
+        "cachix-action",
+        "0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad", // v16
+    )
+    .add_with(("name", "zed"))
+    .add_with(("authToken", vars::CACHIX_AUTH_TOKEN))
+    .add_with(("pushFilter", cachix_filter))
+    .add_with(("cachixArgs", "-v"))
+}
+
+pub fn build(flake_output: &str) -> Step<Run> {
+    named::bash(&format!(
+        "nix build .#{} -L --accept-flake-config",
+        flake_output
+    ))
+}
+
+pub fn limit_store() -> Step<Run> {
+    named::bash(indoc! {r#"
+            if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
+                nix-collect-garbage -d || true
+            fi"#
+    })
+}

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

@@ -0,0 +1,128 @@
+use crate::tasks::workflows::steps::named;
+
+use super::{runners, steps, vars};
+use gh_workflow::*;
+
+pub fn run_bundling() -> Workflow {
+    named::workflow()
+        .on(Event::default().pull_request(
+            PullRequest::default().types([PullRequestType::Labeled, PullRequestType::Synchronize]),
+        ))
+        .concurrency(
+            Concurrency::new(Expression::new(
+                "${{ github.workflow }}-${{ github.head_ref || github.ref }}",
+            ))
+            .cancel_in_progress(true),
+        )
+        .add_env(("CARGO_TERM_COLOR", "always"))
+        .add_env(("CARGO_INCREMENTAL", "0"))
+        .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", bundle_mac())
+        .add_job("bundle_linux_x86_64", bundle_linux(runners::Arch::X86_64))
+        .add_job("bundle_linux_arm64", bundle_linux(runners::Arch::AARCH64))
+        .add_job(
+            "bundle_windows_x86_64",
+            bundle_windows_job(runners::Arch::X86_64),
+        )
+        .add_job(
+            "bundle_windows_arm64",
+            bundle_windows_job(runners::Arch::AARCH64),
+        )
+}
+
+fn bundle_job() -> Job {
+    Job::default()
+        .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 {
+    bundle_job()
+        .runs_on(runners::MAC_DEFAULT)
+        .add_env(("MACOS_CERTIFICATE", vars::MACOS_CERTIFICATE))
+        .add_env((
+            "MACOS_CERTIFICATE_PASSWORD",
+            vars::MACOS_CERTIFICATE_PASSWORD,
+        ))
+        .add_env(("APPLE_NOTARIZATION_KEY", vars::APPLE_NOTARIZATION_KEY))
+        .add_env(("APPLE_NOTARIZATION_KEY_ID", vars::APPLE_NOTARIZATION_KEY_ID))
+        .add_env((
+            "APPLE_NOTARIZATION_ISSUER_ID",
+            vars::APPLE_NOTARIZATION_ISSUER_ID,
+        ))
+        .add_step(steps::checkout_repo())
+        .add_step(steps::setup_node())
+        .add_step(steps::setup_sentry())
+        .add_step(steps::clear_target_dir_if_large())
+        .add_step(steps::script("./script/bundle-mac"))
+        .add_step(steps::upload_artifact(
+            "Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg",
+            "target/aarch64-apple-darwin/release/Zed.dmg",
+        ))
+        .add_step(steps::upload_artifact(
+            "Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg",
+            "target/x86_64-apple-darwin/release/Zed.dmg",
+        ))
+}
+
+fn bundle_linux(arch: runners::Arch) -> 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()
+    );
+    let mut job = bundle_job()
+        .runs_on(arch.linux_bundler())
+        .add_step(steps::checkout_repo())
+        .add_step(steps::setup_sentry())
+        .add_step(steps::script("./script/linux"));
+    // todo(ci) can we do this on arm too?
+    if arch == runners::Arch::X86_64 {
+        job = job.add_step(steps::script("./script/install-mold"));
+    }
+    job.add_step(steps::script("./script/bundle-linux"))
+        .add_step(steps::upload_artifact(
+            &artifact_name,
+            "target/release/zed-*.tar.gz",
+        ))
+        .add_step(steps::upload_artifact(
+            &remote_server_artifact_name,
+            "target/release/zed-remote-server-*.tar.gz",
+        ))
+}
+
+fn bundle_windows_job(arch: runners::Arch) -> Job {
+    use vars::GITHUB_SHA;
+    bundle_job()
+        .runs_on(runners::WINDOWS_DEFAULT)
+        .add_env(("AZURE_TENANT_ID", vars::AZURE_SIGNING_TENANT_ID))
+        .add_env(("AZURE_CLIENT_ID", vars::AZURE_SIGNING_CLIENT_ID))
+        .add_env(("AZURE_CLIENT_SECRET", vars::AZURE_SIGNING_CLIENT_SECRET))
+        .add_env(("ACCOUNT_NAME", vars::AZURE_SIGNING_ACCOUNT_NAME))
+        .add_env(("CERT_PROFILE_NAME", vars::AZURE_SIGNING_CERT_PROFILE_NAME))
+        .add_env(("ENDPOINT", vars::AZURE_SIGNING_ENDPOINT))
+        .add_env(("FILE_DIGEST", "SHA256"))
+        .add_env(("TIMESTAMP_DIGEST", "SHA256"))
+        .add_env(("TIMESTAMP_SERVER", "http://timestamp.acs.microsoft.com"))
+        .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"),
+            "${{ env.SETUP_PATH }}",
+        ))
+}
+
+fn bundle_windows(arch: runners::Arch) -> Step<Run> {
+    let step = match arch {
+        runners::Arch::X86_64 => named::pwsh("script/bundle-windows.ps1 -Architecture x86_64"),
+        runners::Arch::AARCH64 => named::pwsh("script/bundle-windows.ps1 -Architecture aarch64"),
+    };
+    step.working_directory("${{ env.ZED_WORKSPACE }}")
+}

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

@@ -1,6 +1,12 @@
 pub const LINUX_CHEAP: Runner = Runner("namespace-profile-2x4-ubuntu-2404");
 pub const LINUX_DEFAULT: Runner = Runner("namespace-profile-16x32-ubuntu-2204");
+
+// Using Ubuntu 20.04 for minimal glibc version
+pub const LINUX_X86_BUNDLER: Runner = Runner("namespace-profile-32x64-ubuntu-2004");
+pub const LINUX_ARM_BUNDLER: Runner = Runner("namespace-profile-8x32-ubuntu-2004-arm-m4");
+
 pub const MAC_DEFAULT: Runner = Runner("self-mini-macos");
+pub const WINDOWS_DEFAULT: Runner = Runner("self-32vcpu-windows-2022");
 
 pub struct Runner(&'static str);
 
@@ -9,3 +15,34 @@ impl Into<gh_workflow::RunsOn> for Runner {
         self.0.into()
     }
 }
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Arch {
+    X86_64,
+    AARCH64,
+}
+
+impl std::fmt::Display for Arch {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Arch::X86_64 => write!(f, "x86_64"),
+            Arch::AARCH64 => write!(f, "aarch64"),
+        }
+    }
+}
+
+impl Arch {
+    pub fn triple(&self) -> &'static str {
+        match self {
+            Arch::X86_64 => "x86_64-unknown-linux-gnu",
+            Arch::AARCH64 => "aarch64-unknown-linux-gnu",
+        }
+    }
+
+    pub fn linux_bundler(&self) -> Runner {
+        match self {
+            Arch::X86_64 => LINUX_X86_BUNDLER,
+            Arch::AARCH64 => LINUX_ARM_BUNDLER,
+        }
+    }
+}

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

@@ -1,11 +1,20 @@
 use gh_workflow::*;
 
+use crate::tasks::workflows::vars;
+
+const BASH_SHELL: &str = "bash -euxo pipefail {0}";
+// https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsshell
+const PWSH_SHELL: &str = "pwsh";
+
 pub fn checkout_repo() -> Step<Use> {
     named::uses(
         "actions",
         "checkout",
         "11bd71901bbe5b1630ceea73d27597364c9af683", // v4
     )
+    // prevent checkout action from running `git clean -ffdx` which
+    // would delete the target directory
+    .add_with(("clean", false))
 }
 
 pub fn setup_pnpm() -> Step<Use> {
@@ -17,109 +26,89 @@ pub fn setup_pnpm() -> Step<Use> {
     .add_with(("version", "9"))
 }
 
-pub mod danger {
-    use super::*;
+pub fn setup_node() -> Step<Use> {
+    named::uses(
+        "actions",
+        "setup-node",
+        "49933ea5288caeca8642d1e84afbd3f7d6820020", // v4
+    )
+    .add_with(("node-version", "20"))
+}
+
+pub fn setup_sentry() -> Step<Use> {
+    named::uses(
+        "matbour",
+        "setup-sentry-cli",
+        "3e938c54b3018bdd019973689ef984e033b0454b",
+    )
+    .add_with(("token", vars::SENTRY_AUTH_TOKEN))
+}
 
-    pub fn setup_node() -> Step<Use> {
-        named::uses(
+pub fn upload_artifact(name: &str, path: &str) -> Step<Use> {
+    Step::new(format!("@actions/upload-artifact {}", name))
+        .uses(
             "actions",
-            "setup-node",
-            "49933ea5288caeca8642d1e84afbd3f7d6820020", // v4
+            "upload-artifact",
+            "330a01c490aca151604b8cf639adc76d48f6c5d4", // v5
         )
-        .add_with(("node-version", "20"))
-        .add_with(("cache", "pnpm"))
-        .add_with(("cache-dependency-path", "script/danger/pnpm-lock.yaml"))
-    }
+        .add_with(("name", name))
+        .add_with(("path", path))
+}
 
-    pub fn install_deps() -> Step<Run> {
-        named::run("pnpm install --dir script/danger")
-    }
+pub fn clear_target_dir_if_large() -> Step<Run> {
+    named::bash("script/clear-target-dir-if-larger-than ${{ env.MAX_SIZE }}")
+        .add_env(("MAX_SIZE", "${{ runner.os == 'macOS' && 300 || 100 }}"))
+}
 
-    pub fn run() -> Step<Run> {
-        named::run("pnpm run --dir script/danger danger ci")
-            // This GitHub token is not used, but the value needs to be here to prevent
-            // Danger from throwing an error.
-            .add_env(("GITHUB_TOKEN", "not_a_real_token"))
-            // All requests are instead proxied through an instance of
-            // https://github.com/maxdeviant/danger-proxy that allows Danger to securely
-            // authenticate with GitHub while still being able to run on PRs from forks.
-            .add_env((
-                "DANGER_GITHUB_API_BASE_URL",
-                "https://danger-proxy.fly.dev/github",
-            ))
+pub fn script(name: &str) -> Step<Run> {
+    if name.ends_with(".ps1") {
+        Step::new(name).run(name).shell(PWSH_SHELL)
+    } else {
+        Step::new(name).run(name).shell(BASH_SHELL)
     }
 }
 
-pub mod nix {
-    use indoc::indoc;
-
-    use crate::tasks::workflows::vars;
-
+// (janky) helper to generate steps with a name that corresponds
+// to the name of the calling function.
+pub(crate) mod named {
     use super::*;
 
-    // on our macs we manually install nix. for some reason the cachix action is running
-    // under a non-login /bin/bash shell which doesn't source the proper script to add the
-    // nix profile to PATH, so we manually add them here
-    pub fn set_path() -> Step<Run> {
-        named::run(indoc! {r#"
-            echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
-            echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
-        "#})
-    }
-
-    pub fn install_nix() -> Step<Use> {
-        named::uses(
-            "cachix",
-            "install-nix-action",
-            "02a151ada4993995686f9ed4f1be7cfbb229e56f", // v31
-        )
-        .add_with(("github_access_token", vars::GITHUB_TOKEN))
-    }
-
-    pub fn cachix_action(cachix_filter: &str) -> Step<Use> {
-        named::uses(
-            "cachix",
-            "cachix-action",
-            "0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad", // v16
-        )
-        .add_with(("name", "zed"))
-        .add_with(("authToken", vars::CACHIX_AUTH_TOKEN))
-        .add_with(("pushFilter", cachix_filter))
-        .add_with(("cachixArgs", "-v"))
-    }
-
-    pub fn build(flake_output: &str) -> Step<Run> {
-        named::run(&format!(
-            "nix build .#{} -L --accept-flake-config",
-            flake_output
-        ))
+    /// Returns a uses step with the same name as the enclosing function.
+    /// (You shouldn't inline this function into the workflow definition, you must
+    /// wrap it in a new function.)
+    pub(crate) fn uses(owner: &str, repo: &str, ref_: &str) -> Step<Use> {
+        Step::new(function_name(1)).uses(owner, repo, ref_)
     }
 
-    pub fn limit_store() -> Step<Run> {
-        named::run(indoc! {r#"
-            if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
-                nix-collect-garbage -d || true
-            fi"#
-        })
+    /// Returns a bash-script step with the same name as the enclosing function.
+    /// (You shouldn't inline this function into the workflow definition, you must
+    /// wrap it in a new function.)
+    pub(crate) fn bash(script: &str) -> Step<Run> {
+        Step::new(function_name(1)).run(script).shell(BASH_SHELL)
     }
-}
 
-// (janky) helpers to generate steps with a name that corresponds
-// to the name of the calling function.
-mod named {
-    use gh_workflow::*;
-
-    pub(super) fn uses(owner: &str, repo: &str, ref_: &str) -> Step<Use> {
-        Step::new(function_name(1)).uses(owner, repo, ref_)
+    /// Returns a pwsh-script step with the same name as the enclosing function.
+    /// (You shouldn't inline this function into the workflow definition, you must
+    /// wrap it in a new function.)
+    pub(crate) fn pwsh(script: &str) -> Step<Run> {
+        Step::new(function_name(1)).run(script).shell(PWSH_SHELL)
     }
 
-    pub(super) fn run(script: &str) -> Step<Run> {
-        Step::new(function_name(1))
-            .run(script)
-            .shell("bash -euxo pipefail {0}")
+    /// Returns a Workflow with the same name as the enclosing module.
+    pub(crate) fn workflow() -> Workflow {
+        Workflow::default().name(
+            named::function_name(1)
+                .split("::")
+                .next()
+                .unwrap()
+                .to_owned(),
+        )
     }
 
-    fn function_name(i: usize) -> String {
+    /// Returns the function name N callers above in the stack
+    /// (typically 1).
+    /// This only works because xtask always runs debug builds.
+    pub(crate) fn function_name(i: usize) -> String {
         let mut name = "<unknown>".to_string();
         let mut count = 0;
         backtrace::trace(|frame| {
@@ -135,7 +124,8 @@ mod named {
             false
         });
         name.split("::")
-            .skip_while(|s| s != &"steps")
+            .skip_while(|s| s != &"workflows")
+            .skip(1)
             .collect::<Vec<_>>()
             .join("::")
     }

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

@@ -6,12 +6,34 @@ macro_rules! secret {
     };
 }
 
+macro_rules! var {
+    ($secret_name:ident) => {
+        pub const $secret_name: &str = concat!("${{ vars.", stringify!($secret_name), " }}");
+    };
+}
+
+pub fn input(name: &str, input: WorkflowCallInput) -> (String, (&str, WorkflowCallInput)) {
+    return (format!("${{{{ inputs.{name} }}}}"), (name, input));
+}
+
 secret!(GITHUB_TOKEN);
 secret!(CACHIX_AUTH_TOKEN);
 secret!(ZED_CLIENT_CHECKSUM_SEED);
-secret!(ZED_MINIDUMP_ENDPOINT);
+secret!(ZED_SENTRY_MINIDUMP_ENDPOINT);
 secret!(ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON);
+secret!(MACOS_CERTIFICATE);
+secret!(MACOS_CERTIFICATE_PASSWORD);
+secret!(APPLE_NOTARIZATION_KEY);
+secret!(APPLE_NOTARIZATION_KEY_ID);
+secret!(APPLE_NOTARIZATION_ISSUER_ID);
+secret!(SENTRY_AUTH_TOKEN);
+secret!(AZURE_SIGNING_TENANT_ID);
+secret!(AZURE_SIGNING_CLIENT_ID);
+secret!(AZURE_SIGNING_CLIENT_SECRET);
 
-pub fn input(name: &str, input: WorkflowCallInput) -> (String, (&str, WorkflowCallInput)) {
-    return (format!("${{{{ inputs.{name} }}}}"), (name, input));
-}
+// todo(ci) make these secrets too...
+var!(AZURE_SIGNING_ACCOUNT_NAME);
+var!(AZURE_SIGNING_CERT_PROFILE_NAME);
+var!(AZURE_SIGNING_ENDPOINT);
+
+pub const GITHUB_SHA: &str = "${{ github.event.pull_request.head.sha || github.sha }}";