Move Nightly release to gh-workflow (#41349)

Conrad Irwin and Ben Kunkle created

Follow up to #41304 to move nightly release over

Release Notes:

- N/A

---------

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

Change summary

.github/workflows/danger.yml                         |   2 
.github/workflows/nix_build.yml                      |  14 
.github/workflows/release_nightly.yml                | 660 +++++++------
.github/workflows/run_bundling.yml                   |  14 
Cargo.lock                                           |   4 
Cargo.toml                                           |   2 
ci/Dockerfile.namespace                              |  21 
tooling/xtask/src/tasks/workflows.rs                 |   4 
tooling/xtask/src/tasks/workflows/nix_build.rs       | 128 +-
tooling/xtask/src/tasks/workflows/release_nightly.rs | 270 +++++
tooling/xtask/src/tasks/workflows/run_bundling.rs    |  35 
tooling/xtask/src/tasks/workflows/runners.rs         |  25 
tooling/xtask/src/tasks/workflows/steps.rs           |  79 +
tooling/xtask/src/tasks/workflows/vars.rs            |  46 
14 files changed, 880 insertions(+), 424 deletions(-)

Detailed changes

.github/workflows/danger.yml 🔗

@@ -18,7 +18,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: steps::setup_pnpm
       uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
       with:

.github/workflows/nix_build.yml 🔗

@@ -10,9 +10,9 @@ on:
       cachix-filter:
         type: string
 jobs:
-  nix-build-linux-x86:
+  build_nix_linux_x86_64:
     if: github.repository_owner == 'zed-industries'
-    runs-on: namespace-profile-16x32-ubuntu-2204
+    runs-on: namespace-profile-32x64-ubuntu-2004
     env:
       ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
       ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
@@ -22,7 +22,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: nix_build::install_nix
       uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f
       with:
@@ -32,14 +32,14 @@ jobs:
       with:
         name: zed
         authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
-        pushFilter: ${{ inputs.cachix-filter }}
         cachixArgs: -v
+        pushFilter: ${{ inputs.cachix-filter }}
     - name: nix_build::build
       run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
       shell: bash -euxo pipefail {0}
     timeout-minutes: 60
     continue-on-error: true
-  nix-build-mac-arm:
+  build_nix_mac_aarch64:
     if: github.repository_owner == 'zed-industries'
     runs-on: self-mini-macos
     env:
@@ -51,7 +51,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: nix_build::set_path
       run: |
         echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
@@ -62,8 +62,8 @@ jobs:
       with:
         name: zed
         authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
-        pushFilter: ${{ inputs.cachix-filter }}
         cachixArgs: -v
+        pushFilter: ${{ inputs.cachix-filter }}
     - name: nix_build::build
       run: nix build .#${{ inputs.flake-output }} -L --accept-flake-config
       shell: bash -euxo pipefail {0}

.github/workflows/release_nightly.yml 🔗

@@ -1,93 +1,107 @@
-name: Release Nightly
-
-on:
-  schedule:
-    # Fire every day at 7:00am UTC (Roughly before EU workday and after US workday)
-    - cron: "0 7 * * *"
-  push:
-    tags:
-      - "nightly"
-
+# Generated from xtask::workflows::release_nightly
+# Rebuild with `cargo xtask workflows`.
+name: release_nightly
 env:
   CARGO_TERM_COLOR: always
-  CARGO_INCREMENTAL: 0
-  RUST_BACKTRACE: 1
+  CARGO_INCREMENTAL: '0'
+  RUST_BACKTRACE: '1'
   ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
   ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
   DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
   DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
-
+on:
+  push:
+    tags:
+    - nightly
+  schedule:
+  - cron: 0 7 * * *
 jobs:
-  style:
-    timeout-minutes: 60
-    name: Check formatting and Clippy lints
+  check_style:
     if: github.repository_owner == 'zed-industries'
-    runs-on:
-      - self-hosted
-      - macOS
+    runs-on: self-mini-macos
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-          fetch-depth: 0
-
-      - name: Run style checks
-        uses: ./.github/actions/check_style
-
-      - name: Run clippy
-        run: ./script/clippy
-
-  tests:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+        fetch-depth: 0
+    - name: steps::cargo_fmt
+      run: cargo fmt --all -- --check
+      shell: bash -euxo pipefail {0}
+    - name: ./script/clippy
+      run: ./script/clippy
+      shell: bash -euxo pipefail {0}
     timeout-minutes: 60
-    name: Run tests
+  run_tests_mac:
     if: github.repository_owner == 'zed-industries'
-    runs-on:
-      - self-hosted
-      - macOS
-    needs: style
+    runs-on: self-mini-macos
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Run tests
-        uses: ./.github/actions/run_tests
-
-  windows-tests:
+    - 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::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
-    name: Run tests on Windows
+  run_tests_windows:
     if: github.repository_owner == 'zed-industries'
-    runs-on: [self-32vcpu-windows-2022]
+    runs-on: self-32vcpu-windows-2022
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Configure CI
-        run: |
-          New-Item -ItemType Directory -Path "./../.cargo" -Force
-          Copy-Item -Path "./.cargo/ci-config.toml" -Destination "./../.cargo/config.toml"
-
-      - name: Run tests
-        uses: ./.github/actions/run_tests_windows
-
-      - name: Limit target directory size
-        run: ./script/clear-target-dir-if-larger-than.ps1 1024
-
-      - name: Clean CI config file
-        if: always()
-        run: Remove-Item -Recurse -Path "./../.cargo" -Force -ErrorAction SilentlyContinue
-
-  bundle-mac:
+    - 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::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
-    name: Create a macOS bundle
+  bundle_mac_nightly:
+    needs:
+    - check_style
+    - run_tests_mac
     if: github.repository_owner == 'zed-industries'
-    runs-on:
-      - self-mini-macos
-    needs: tests
+    runs-on: self-mini-macos
     env:
       MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
       MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
@@ -95,161 +109,120 @@ jobs:
       APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
       APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
     steps:
-      - name: Install Node
-        uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
-        with:
-          node-version: "18"
-
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Set release channel to nightly
-        run: |
-          set -eu
-          version=$(git rev-parse --short HEAD)
-          echo "Publishing version: ${version} on release channel nightly"
-          echo "nightly" > crates/zed/RELEASE_CHANNEL
-
-      - name: Setup Sentry CLI
-        uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b #v2
-        with:
-          token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
-
-      - name: Create macOS app bundle
-        run: script/bundle-mac
-
-      - name: Upload Zed Nightly
-        run: script/upload-nightly macos
-
-  bundle-linux-x86:
+    - 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: release_nightly::set_release_channel_to_nightly
+      run: |
+        set -eu
+        version=$(git rev-parse --short HEAD)
+        echo "Publishing version: ${version} on release channel nightly"
+        echo "nightly" > crates/zed/RELEASE_CHANNEL
+      shell: bash -euxo pipefail {0}
+    - name: ./script/bundle-mac
+      run: ./script/bundle-mac
+      shell: bash -euxo pipefail {0}
+    - name: release_nightly::upload_zed_nightly
+      run: script/upload-nightly macos
+      shell: bash -euxo pipefail {0}
     timeout-minutes: 60
-    name: Create a Linux *.tar.gz bundle for x86
+  bundle_linux_nightly_x86_64:
+    needs:
+    - check_style
+    - run_tests_mac
     if: github.repository_owner == 'zed-industries'
-    runs-on:
-      - namespace-profile-16x32-ubuntu-2004 # ubuntu 20.04 for minimal glibc
-    needs: tests
+    runs-on: namespace-profile-32x64-ubuntu-2004
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Add Rust to the PATH
-        run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
-
-      - name: Install Linux dependencies
-        run: ./script/linux && ./script/install-mold 2.34.0
-
-      - name: Setup Sentry CLI
-        uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b #v2
-        with:
-          token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
-
-      - name: Limit target directory size
-        run: script/clear-target-dir-if-larger-than 100
-
-      - name: Set release channel to nightly
-        run: |
-          set -euo pipefail
-          version=$(git rev-parse --short HEAD)
-          echo "Publishing version: ${version} on release channel nightly"
-          echo "nightly" > crates/zed/RELEASE_CHANNEL
-
-      - name: Create Linux .tar.gz bundle
-        run: script/bundle-linux
-
-      - name: Upload Zed Nightly
-        run: script/upload-nightly linux-targz
-
-  bundle-linux-arm:
+    - 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: release_nightly::add_rust_to_path
+      run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
+      shell: bash -euxo pipefail {0}
+    - 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: steps::clear_target_dir_if_large
+      run: ./script/clear-target-dir-if-larger-than 100
+      shell: bash -euxo pipefail {0}
+    - name: release_nightly::set_release_channel_to_nightly
+      run: |
+        set -eu
+        version=$(git rev-parse --short HEAD)
+        echo "Publishing version: ${version} on release channel nightly"
+        echo "nightly" > crates/zed/RELEASE_CHANNEL
+      shell: bash -euxo pipefail {0}
+    - name: ./script/bundle-linux
+      run: ./script/bundle-linux
+      shell: bash -euxo pipefail {0}
+    - name: release_nightly::upload_zed_nightly
+      run: script/upload-nightly linux-targz
+      shell: bash -euxo pipefail {0}
     timeout-minutes: 60
-    name: Create a Linux *.tar.gz bundle for ARM
+  bundle_linux_nightly_aarch64:
+    needs:
+    - check_style
+    - run_tests_mac
     if: github.repository_owner == 'zed-industries'
-    runs-on:
-      - namespace-profile-8x32-ubuntu-2004-arm-m4 # ubuntu 20.04 for minimal glibc
-    needs: tests
+    runs-on: namespace-profile-8x32-ubuntu-2004-arm-m4
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Install Linux dependencies
-        run: ./script/linux
-
-      - name: Setup Sentry CLI
-        uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b #v2
-        with:
-          token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
-
-      - name: Limit target directory size
-        run: script/clear-target-dir-if-larger-than 100
-
-      - name: Set release channel to nightly
-        run: |
-          set -euo pipefail
-          version=$(git rev-parse --short HEAD)
-          echo "Publishing version: ${version} on release channel nightly"
-          echo "nightly" > crates/zed/RELEASE_CHANNEL
-
-      - name: Create Linux .tar.gz bundle
-        run: script/bundle-linux
-
-      - name: Upload Zed Nightly
-        run: script/upload-nightly linux-targz
-
-  freebsd:
+    - 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: release_nightly::add_rust_to_path
+      run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
+      shell: bash -euxo pipefail {0}
+    - name: ./script/linux
+      run: ./script/linux
+      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: release_nightly::set_release_channel_to_nightly
+      run: |
+        set -eu
+        version=$(git rev-parse --short HEAD)
+        echo "Publishing version: ${version} on release channel nightly"
+        echo "nightly" > crates/zed/RELEASE_CHANNEL
+      shell: bash -euxo pipefail {0}
+    - name: ./script/bundle-linux
+      run: ./script/bundle-linux
+      shell: bash -euxo pipefail {0}
+    - name: release_nightly::upload_zed_nightly
+      run: script/upload-nightly linux-targz
+      shell: bash -euxo pipefail {0}
     timeout-minutes: 60
-    if: false && github.repository_owner == 'zed-industries'
-    runs-on: github-8vcpu-ubuntu-2404
-    needs: tests
-    name: Build Zed on FreeBSD
-    steps:
-      - uses: actions/checkout@v4
-      - name: Build FreeBSD remote-server
-        id: freebsd-build
-        uses: vmactions/freebsd-vm@c3ae29a132c8ef1924775414107a97cac042aad5 # v1.2.0
-        with:
-          # envs: "MYTOKEN MYTOKEN2"
-          usesh: true
-          release: 13.5
-          copyback: true
-          prepare: |
-            pkg install -y \
-              bash curl jq git \
-              rustup-init cmake-core llvm-devel-lite pkgconf protobuf # ibx11 alsa-lib rust-bindgen-cli
-          run: |
-            freebsd-version
-            sysctl hw.model
-            sysctl hw.ncpu
-            sysctl hw.physmem
-            sysctl hw.usermem
-            git config --global --add safe.directory /home/runner/work/zed/zed
-            rustup-init --profile minimal --default-toolchain none -y
-            . "$HOME/.cargo/env"
-            ./script/bundle-freebsd
-            mkdir -p out/
-            mv "target/zed-remote-server-freebsd-x86_64.gz" out/
-            rm -rf target/
-            cargo clean
-
-      - name: Upload Zed Nightly
-        run: script/upload-nightly freebsd
-
-  bundle-nix:
-    name: Build and cache Nix package
-    needs: tests
-    secrets: inherit
-    uses: ./.github/workflows/nix_build.yml
-
-  bundle-windows-x64:
-    timeout-minutes: 60
-    name: Create a Windows installer for x86_64
+  bundle_windows_nightly_x86_64:
+    needs:
+    - check_style
+    - run_tests_windows
     if: github.repository_owner == 'zed-industries'
-    runs-on: [self-32vcpu-windows-2022]
-    needs: windows-tests
+    runs-on: self-32vcpu-windows-2022
     env:
       AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
       AZURE_CLIENT_ID: ${{ secrets.AZURE_SIGNING_CLIENT_ID }}
@@ -259,40 +232,39 @@ jobs:
       ENDPOINT: ${{ vars.AZURE_SIGNING_ENDPOINT }}
       FILE_DIGEST: SHA256
       TIMESTAMP_DIGEST: SHA256
-      TIMESTAMP_SERVER: "http://timestamp.acs.microsoft.com"
+      TIMESTAMP_SERVER: http://timestamp.acs.microsoft.com
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Set release channel to nightly
-        working-directory: ${{ env.ZED_WORKSPACE }}
-        run: |
-          $ErrorActionPreference = "Stop"
-          $version = git rev-parse --short HEAD
-          Write-Host "Publishing version: $version on release channel nightly"
-          "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
-
-      - name: Setup Sentry CLI
-        uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b #v2
-        with:
-          token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
-
-      - name: Build Zed installer
-        working-directory: ${{ env.ZED_WORKSPACE }}
-        run: script/bundle-windows.ps1 -Architecture x86_64
-
-      - name: Upload Zed Nightly
-        working-directory: ${{ env.ZED_WORKSPACE }}
-        run: script/upload-nightly.ps1 -Architecture x86_64
-
-  bundle-windows-arm64:
+    - 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: release_nightly::set_release_channel_to_nightly
+      run: |
+        $ErrorActionPreference = "Stop"
+        $version = git rev-parse --short HEAD
+        Write-Host "Publishing version: $version on release channel nightly"
+        "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    - name: release_nightly::build_zed_installer
+      run: script/bundle-windows.ps1 -Architecture x86_64
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    - name: release_nightly::upload_zed_nightly_windows
+      run: script/upload-nightly.ps1 -Architecture x86_64
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
     timeout-minutes: 60
-    name: Create a Windows installer for aarch64
+  bundle_windows_nightly_aarch64:
+    needs:
+    - check_style
+    - run_tests_windows
     if: github.repository_owner == 'zed-industries'
-    runs-on: [self-32vcpu-windows-2022]
-    needs: windows-tests
+    runs-on: self-32vcpu-windows-2022
     env:
       AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
       AZURE_CLIENT_ID: ${{ secrets.AZURE_SIGNING_CLIENT_ID }}
@@ -302,66 +274,134 @@ jobs:
       ENDPOINT: ${{ vars.AZURE_SIGNING_ENDPOINT }}
       FILE_DIGEST: SHA256
       TIMESTAMP_DIGEST: SHA256
-      TIMESTAMP_SERVER: "http://timestamp.acs.microsoft.com"
+      TIMESTAMP_SERVER: http://timestamp.acs.microsoft.com
     steps:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          clean: false
-
-      - name: Set release channel to nightly
-        working-directory: ${{ env.ZED_WORKSPACE }}
-        run: |
-          $ErrorActionPreference = "Stop"
-          $version = git rev-parse --short HEAD
-          Write-Host "Publishing version: $version on release channel nightly"
-          "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
-
-      - name: Setup Sentry CLI
-        uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b #v2
-        with:
-          token: ${{ SECRETS.SENTRY_AUTH_TOKEN }}
-
-      - name: Build Zed installer
-        working-directory: ${{ env.ZED_WORKSPACE }}
-        run: script/bundle-windows.ps1 -Architecture aarch64
-
-      - name: Upload Zed Nightly
-        working-directory: ${{ env.ZED_WORKSPACE }}
-        run: script/upload-nightly.ps1 -Architecture aarch64
-
-  update-nightly-tag:
-    name: Update nightly tag
+    - 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: release_nightly::set_release_channel_to_nightly
+      run: |
+        $ErrorActionPreference = "Stop"
+        $version = git rev-parse --short HEAD
+        Write-Host "Publishing version: $version on release channel nightly"
+        "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    - name: release_nightly::build_zed_installer
+      run: script/bundle-windows.ps1 -Architecture aarch64
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    - name: release_nightly::upload_zed_nightly_windows
+      run: script/upload-nightly.ps1 -Architecture aarch64
+      shell: pwsh
+      working-directory: ${{ env.ZED_WORKSPACE }}
+    timeout-minutes: 60
+  build_nix_linux_x86_64:
+    needs:
+    - check_style
+    - run_tests_mac
     if: github.repository_owner == 'zed-industries'
-    runs-on: namespace-profile-2x4-ubuntu-2404
+    runs-on: namespace-profile-32x64-ubuntu-2004
+    env:
+      ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
+      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:
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - name: nix_build::install_nix
+      uses: cachix/install-nix-action@02a151ada4993995686f9ed4f1be7cfbb229e56f
+      with:
+        github_access_token: ${{ secrets.GITHUB_TOKEN }}
+    - name: nix_build::cachix_action
+      uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad
+      with:
+        name: zed
+        authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
+        cachixArgs: -v
+    - name: nix_build::build
+      run: nix build .#default -L --accept-flake-config
+      shell: bash -euxo pipefail {0}
+    timeout-minutes: 60
+    continue-on-error: true
+  build_nix_mac_aarch64:
     needs:
-      - bundle-mac
-      - bundle-linux-x86
-      - bundle-linux-arm
-      - bundle-windows-x64
-      - bundle-windows-arm64
+    - check_style
+    - run_tests_mac
+    if: github.repository_owner == 'zed-industries'
+    runs-on: self-mini-macos
+    env:
+      ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
+      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:
-      - name: Checkout repo
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-        with:
-          fetch-depth: 0
-
-      - name: Update nightly tag
-        run: |
-          if [ "$(git rev-parse nightly)" = "$(git rev-parse HEAD)" ]; then
-            echo "Nightly tag already points to current commit. Skipping tagging."
-            exit 0
-          fi
-          git config user.name github-actions
-          git config user.email github-actions@github.com
-          git tag -f nightly
-          git push origin nightly --force
-
-      - name: Create Sentry release
-        uses: getsentry/action-release@526942b68292201ac6bbb99b9a0747d4abee354c # v3
-        env:
-          SENTRY_ORG: zed-dev
-          SENTRY_PROJECT: zed
-          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
-        with:
-          environment: production
+    - name: steps::checkout_repo
+      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+      with:
+        clean: false
+    - 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: nix_build::cachix_action
+      uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad
+      with:
+        name: zed
+        authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
+        cachixArgs: -v
+    - name: nix_build::build
+      run: nix build .#default -L --accept-flake-config
+      shell: bash -euxo pipefail {0}
+    - name: nix_build::limit_store
+      run: |-
+        if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
+            nix-collect-garbage -d || true
+        fi
+      shell: bash -euxo pipefail {0}
+    timeout-minutes: 60
+    continue-on-error: true
+  update_nightly_tag:
+    needs:
+    - bundle_mac_nightly
+    - bundle_linux_nightly_x86_64
+    - bundle_linux_nightly_aarch64
+    - bundle_windows_nightly_x86_64
+    - bundle_windows_nightly_aarch64
+    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: 0
+    - name: release_nightly::update_nightly_tag
+      run: |
+        if [ "$(git rev-parse nightly)" = "$(git rev-parse HEAD)" ]; then
+          echo "Nightly tag already points to current commit. Skipping tagging."
+          exit 0
+        fi
+        git config user.name github-actions
+        git config user.email github-actions@github.com
+        git tag -f nightly
+        git push origin nightly --force
+      shell: bash -euxo pipefail {0}
+    - name: release_nightly::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 }}
+    timeout-minutes: 60

.github/workflows/run_bundling.yml 🔗

@@ -28,7 +28,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: steps::setup_node
       uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
       with:
@@ -38,10 +38,8 @@ jobs:
       with:
         token: ${{ secrets.SENTRY_AUTH_TOKEN }}
     - name: steps::clear_target_dir_if_large
-      run: script/clear-target-dir-if-larger-than ${{ env.MAX_SIZE }}
+      run: ./script/clear-target-dir-if-larger-than 300
       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}
@@ -65,7 +63,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: steps::setup_sentry
       uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
       with:
@@ -99,7 +97,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: steps::setup_sentry
       uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
       with:
@@ -140,7 +138,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: steps::setup_sentry
       uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
       with:
@@ -174,7 +172,7 @@ jobs:
     - name: steps::checkout_repo
       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
       with:
-        clean: 'false'
+        clean: false
     - name: steps::setup_sentry
       uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b
       with:

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=fe5fd50664bfe164277cab5c86537a2361eddbbd#fe5fd50664bfe164277cab5c86537a2361eddbbd"
+source = "git+https://github.com/zed-industries/gh-workflow?rev=0090c6b6ef82fff02bc8616645953e778d1acc08#0090c6b6ef82fff02bc8616645953e778d1acc08"
 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=fe5fd50664bfe164277cab5c86537a2361eddbbd#fe5fd50664bfe164277cab5c86537a2361eddbbd"
+source = "git+https://github.com/zed-industries/gh-workflow?rev=0090c6b6ef82fff02bc8616645953e778d1acc08#0090c6b6ef82fff02bc8616645953e778d1acc08"
 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 = "fe5fd50664bfe164277cab5c86537a2361eddbbd" }
+gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "0090c6b6ef82fff02bc8616645953e778d1acc08" }
 git2 = { version = "0.20.1", default-features = false }
 globset = "0.4"
 handlebars = "4.3"

ci/Dockerfile.namespace 🔗

@@ -0,0 +1,21 @@
+ARG NAMESPACE_BASE_IMAGE_REF=""
+
+# Your image must build FROM NAMESPACE_BASE_IMAGE_REF
+FROM ${NAMESPACE_BASE_IMAGE_REF} AS base
+
+# Remove problematic git-lfs packagecloud source
+RUN sudo rm -f /etc/apt/sources.list.d/*git-lfs*.list
+# Install git and SSH for cloning private repositories
+RUN sudo apt-get update && \
+    sudo apt-get install -y git openssh-client
+
+# Clone the Zed repository
+RUN git clone https://github.com/zed-industries/zed.git ~/zed
+
+# Run the Linux installation script
+WORKDIR /home/runner/zed
+RUN ./script/linux
+
+# Clean up unnecessary files to reduce image size
+RUN sudo apt-get clean && sudo rm -rf \
+    /home/runner/zed

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

@@ -5,9 +5,8 @@ use std::path::Path;
 
 mod danger;
 mod nix_build;
-// mod release;
+mod release_nightly;
 mod run_bundling;
-// mod run_tests;
 
 mod runners;
 mod steps;
@@ -23,6 +22,7 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
         ("danger.yml", danger::danger()),
         ("nix_build.yml", nix_build::nix_build()),
         ("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()),
     ];

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

@@ -1,23 +1,14 @@
+use crate::tasks::workflows::{
+    runners::{Arch, Platform},
+    steps::NamedJob,
+};
+
 use super::{runners, steps, steps::named, vars};
 use gh_workflow::*;
-use indexmap::IndexMap;
 use indoc::indoc;
 
 /// Generates the nix.yml workflow
 pub fn nix_build() -> Workflow {
-    let env: IndexMap<_, _> = [
-        ("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED),
-        ("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT),
-        (
-            "ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON",
-            vars::ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON,
-        ),
-        ("GIT_LFS_SKIP_SMUDGE", "1"), // breaks the livekit rust sdk examples which we don't actually depend on
-    ]
-    .into_iter()
-    .map(|(key, value)| (key.into(), value.into()))
-    .collect();
-
     // todo(ci) instead of having these as optional YAML inputs,
     // should we just generate two copies of the job (one for release-nightly
     // and one for CI?)
@@ -37,44 +28,80 @@ pub fn nix_build() -> Workflow {
         },
     );
 
+    let linux_x86 = build_nix(
+        Platform::Linux,
+        Arch::X86_64,
+        &input_flake_output,
+        Some(&input_cachix_filter),
+        &[],
+    );
+    let mac_arm = build_nix(
+        Platform::Mac,
+        Arch::ARM64,
+        &input_flake_output,
+        Some(&input_cachix_filter),
+        &[],
+    );
+
     named::workflow()
         .on(Event::default().workflow_call(
             WorkflowCall::default()
                 .add_input(flake_output.0, flake_output.1)
                 .add_input(cachix_filter.0, cachix_filter.1),
         ))
-        .add_job(
-            "nix-build-linux-x86",
-            Job::default()
-                .timeout_minutes(60u32)
-                .continue_on_error(true)
-                .cond(Expression::new(
-                    "github.repository_owner == 'zed-industries'",
-                ))
-                .runs_on(runners::LINUX_DEFAULT)
-                .envs(env.clone())
-                .add_step(steps::checkout_repo().add_with(("clean", "false")))
-                .add_step(install_nix())
-                .add_step(cachix_action(&input_cachix_filter))
-                .add_step(build(&input_flake_output)),
-        )
-        .add_job(
-            "nix-build-mac-arm",
-            Job::default()
-                .timeout_minutes(60u32)
-                .continue_on_error(true)
-                .cond(Expression::new(
-                    "github.repository_owner == 'zed-industries'",
-                ))
-                .runs_on(runners::MAC_DEFAULT)
-                .envs(env)
-                .add_step(steps::checkout_repo().add_with(("clean", "false")))
-                .add_step(set_path())
-                .add_step(cachix_action(&input_cachix_filter))
-                .add_step(build(&input_flake_output))
-                .add_step(limit_store()),
-        )
+        .add_job(linux_x86.name, linux_x86.job)
+        .add_job(mac_arm.name, mac_arm.job)
+}
+
+pub(crate) fn build_nix(
+    platform: Platform,
+    arch: Arch,
+    flake_output: &str,
+    cachix_filter: Option<&str>,
+    deps: &[&NamedJob],
+) -> NamedJob {
+    let runner = match platform {
+        Platform::Windows => unimplemented!(),
+        Platform::Linux => runners::LINUX_X86_BUNDLER,
+        Platform::Mac => runners::MAC_DEFAULT,
+    };
+    let mut job = Job::default()
+        .timeout_minutes(60u32)
+        .continue_on_error(true)
+        .cond(Expression::new(
+            "github.repository_owner == 'zed-industries'",
+        ))
+        .runs_on(runner)
+        .add_env(("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED))
+        .add_env(("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT))
+        .add_env((
+            "ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON",
+            vars::ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON,
+        ))
+        .add_env(("GIT_LFS_SKIP_SMUDGE", "1")) // breaks the livekit rust sdk examples which we don't actually depend on
+        .add_step(steps::checkout_repo());
+
+    if deps.len() > 0 {
+        job = job.needs(deps.iter().map(|d| d.name.clone()).collect::<Vec<String>>());
+    }
+
+    job = if platform == Platform::Linux {
+        job.add_step(install_nix())
+            .add_step(cachix_action(cachix_filter))
+            .add_step(build(&flake_output))
+    } else {
+        job.add_step(set_path())
+            .add_step(cachix_action(cachix_filter))
+            .add_step(build(&flake_output))
+            .add_step(limit_store())
+    };
+
+    NamedJob {
+        name: format!("build_nix_{platform}_{arch}"),
+        job,
+    }
 }
+
 // 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
@@ -94,16 +121,19 @@ pub fn install_nix() -> Step<Use> {
     .add_with(("github_access_token", vars::GITHUB_TOKEN))
 }
 
-pub fn cachix_action(cachix_filter: &str) -> Step<Use> {
-    named::uses(
+pub fn cachix_action(cachix_filter: Option<&str>) -> Step<Use> {
+    let mut step = 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"))
+    .add_with(("cachixArgs", "-v"));
+    if let Some(cachix_filter) = cachix_filter {
+        step = step.add_with(("pushFilter", cachix_filter));
+    }
+    step
 }
 
 pub fn build(flake_output: &str) -> Step<Run> {

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

@@ -0,0 +1,270 @@
+use crate::tasks::workflows::{
+    nix_build::build_nix,
+    runners::{Arch, Platform},
+    steps::NamedJob,
+    vars::{mac_bundle_envs, windows_bundle_envs},
+};
+
+use super::{runners, steps, steps::named, vars};
+use gh_workflow::*;
+use indexmap::IndexMap;
+
+/// Generates the release_nightly.yml workflow
+pub fn release_nightly() -> Workflow {
+    let env: IndexMap<_, _> = [
+        ("CARGO_TERM_COLOR", "always"),
+        ("CARGO_INCREMENTAL", "0"),
+        ("RUST_BACKTRACE", "1"),
+        ("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED),
+        ("ZED_MINIDUMP_ENDPOINT", vars::ZED_SENTRY_MINIDUMP_ENDPOINT),
+        (
+            "DIGITALOCEAN_SPACES_ACCESS_KEY",
+            vars::DIGITALOCEAN_SPACES_ACCESS_KEY,
+        ),
+        (
+            "DIGITALOCEAN_SPACES_SECRET_KEY",
+            vars::DIGITALOCEAN_SPACES_SECRET_KEY,
+        ),
+    ]
+    .into_iter()
+    .map(|(key, value)| (key.into(), value.into()))
+    .collect();
+
+    let style = check_style();
+    let tests = run_tests(Platform::Mac);
+    let windows_tests = run_tests(Platform::Windows);
+    let bundle_mac = bundle_mac_nightly(&[&style, &tests]);
+    let linux_x86 = bundle_linux_nightly(Arch::X86_64, &[&style, &tests]);
+    let linux_arm = bundle_linux_nightly(Arch::ARM64, &[&style, &tests]);
+    let windows_x86 = bundle_windows_nightly(Arch::X86_64, &[&style, &windows_tests]);
+    let windows_arm = bundle_windows_nightly(Arch::ARM64, &[&style, &windows_tests]);
+
+    let nix_linux_x86 = build_nix(
+        Platform::Linux,
+        Arch::X86_64,
+        "default",
+        None,
+        &[&style, &tests],
+    );
+    let nix_mac_arm = build_nix(
+        Platform::Mac,
+        Arch::ARM64,
+        "default",
+        None,
+        &[&style, &tests],
+    );
+    let update_nightly_tag = update_nightly_tag_job(&[
+        &bundle_mac,
+        &linux_x86,
+        &linux_arm,
+        &windows_x86,
+        &windows_arm,
+    ]);
+
+    named::workflow()
+        .on(Event::default()
+            // Fire every day at 7:00am UTC (Roughly before EU workday and after US workday)
+            .schedule([Schedule::new("0 7 * * *")])
+            .push(Push::default().add_tag("nightly")))
+        .envs(env)
+        .add_job(style.name, style.job)
+        .add_job(tests.name, tests.job)
+        .add_job(windows_tests.name, windows_tests.job)
+        .add_job(bundle_mac.name, bundle_mac.job)
+        .add_job(linux_x86.name, linux_x86.job)
+        .add_job(linux_arm.name, linux_arm.job)
+        .add_job(windows_x86.name, windows_x86.job)
+        .add_job(windows_arm.name, windows_arm.job)
+        .add_job(nix_linux_x86.name, nix_linux_x86.job)
+        .add_job(nix_mac_arm.name, nix_mac_arm.job)
+        .add_job(update_nightly_tag.name, update_nightly_tag.job)
+}
+
+fn check_style() -> NamedJob {
+    let job = release_job(&[])
+        .runs_on(runners::MAC_DEFAULT)
+        .add_step(
+            steps::checkout_repo()
+                .add_with(("clean", false))
+                .add_with(("fetch-depth", 0)),
+        )
+        .add_step(steps::cargo_fmt())
+        .add_step(steps::script("./script/clippy"));
+
+    named::job(job)
+}
+
+fn release_job(deps: &[&NamedJob]) -> Job {
+    let job = Job::default()
+        .cond(Expression::new(
+            "github.repository_owner == 'zed-industries'",
+        ))
+        .timeout_minutes(60u32);
+    if deps.len() > 0 {
+        job.needs(deps.iter().map(|j| j.name.clone()).collect::<Vec<_>>())
+    } else {
+        job
+    }
+}
+
+fn run_tests(platform: Platform) -> NamedJob {
+    let runner = match platform {
+        Platform::Windows => runners::WINDOWS_DEFAULT,
+        Platform::Linux => runners::LINUX_DEFAULT,
+        Platform::Mac => runners::MAC_DEFAULT,
+    };
+    NamedJob {
+        name: format!("run_tests_{platform}"),
+        job: release_job(&[])
+            .runs_on(runner)
+            .add_step(steps::checkout_repo())
+            .add_step(steps::setup_cargo_config(platform))
+            .add_step(steps::setup_node())
+            .add_step(steps::cargo_install_nextest(platform))
+            .add_step(steps::clear_target_dir_if_large(platform))
+            .add_step(steps::cargo_nextest(platform))
+            .add_step(steps::cleanup_cargo_config(platform)),
+    }
+}
+
+fn bundle_mac_nightly(deps: &[&NamedJob]) -> NamedJob {
+    let platform = Platform::Mac;
+    let job = release_job(deps)
+        .runs_on(runners::MAC_DEFAULT)
+        .envs(mac_bundle_envs())
+        .add_step(steps::checkout_repo())
+        .add_step(steps::setup_node())
+        .add_step(steps::setup_sentry())
+        .add_step(steps::clear_target_dir_if_large(platform))
+        .add_step(set_release_channel_to_nightly(platform))
+        .add_step(steps::script("./script/bundle-mac"))
+        .add_step(upload_zed_nightly(platform, Arch::ARM64));
+    named::job(job)
+}
+
+fn bundle_linux_nightly(arch: Arch, deps: &[&NamedJob]) -> NamedJob {
+    let platform = Platform::Linux;
+    let mut job = release_job(deps)
+        .runs_on(arch.linux_bundler())
+        .add_step(steps::checkout_repo())
+        .add_step(steps::setup_sentry())
+        .add_step(add_rust_to_path())
+        .add_step(steps::script("./script/linux"));
+
+    // todo(ci) can we do this on arm too?
+    if arch == Arch::X86_64 {
+        job = job.add_step(steps::script("./script/install-mold"));
+    }
+    job = job
+        .add_step(steps::clear_target_dir_if_large(platform))
+        .add_step(set_release_channel_to_nightly(platform))
+        .add_step(steps::script("./script/bundle-linux"))
+        .add_step(upload_zed_nightly(platform, arch));
+    NamedJob {
+        name: format!("bundle_linux_nightly_{arch}"),
+        job,
+    }
+}
+
+fn bundle_windows_nightly(arch: Arch, deps: &[&NamedJob]) -> NamedJob {
+    let platform = Platform::Windows;
+    NamedJob {
+        name: format!("bundle_windows_nightly_{arch}"),
+        job: release_job(deps)
+            .runs_on(runners::WINDOWS_DEFAULT)
+            .envs(windows_bundle_envs())
+            .add_step(steps::checkout_repo())
+            .add_step(steps::setup_sentry())
+            .add_step(set_release_channel_to_nightly(platform))
+            .add_step(build_zed_installer(arch))
+            .add_step(upload_zed_nightly_windows(arch)),
+    }
+}
+
+fn update_nightly_tag_job(deps: &[&NamedJob]) -> NamedJob {
+    NamedJob {
+        name: "update_nightly_tag".to_owned(),
+        job: release_job(deps)
+            .runs_on(runners::LINUX_CHEAP)
+            .add_step(steps::checkout_repo().add_with(("fetch-depth", 0)))
+            .add_step(update_nightly_tag())
+            .add_step(create_sentry_release()),
+    }
+}
+
+fn set_release_channel_to_nightly(platform: Platform) -> Step<Run> {
+    match platform {
+        Platform::Linux | Platform::Mac => named::bash(indoc::indoc! {r#"
+            set -eu
+            version=$(git rev-parse --short HEAD)
+            echo "Publishing version: ${version} on release channel nightly"
+            echo "nightly" > crates/zed/RELEASE_CHANNEL
+        "#}),
+        Platform::Windows => named::pwsh(indoc::indoc! {r#"
+            $ErrorActionPreference = "Stop"
+            $version = git rev-parse --short HEAD
+            Write-Host "Publishing version: $version on release channel nightly"
+            "nightly" | Set-Content -Path "crates/zed/RELEASE_CHANNEL"
+        "#})
+        .working_directory("${{ env.ZED_WORKSPACE }}"),
+    }
+}
+
+fn add_rust_to_path() -> Step<Run> {
+    named::bash(r#"echo "$HOME/.cargo/bin" >> "$GITHUB_PATH""#)
+}
+
+fn upload_zed_nightly(platform: Platform, arch: Arch) -> Step<Run> {
+    match platform {
+        Platform::Linux => named::bash("script/upload-nightly linux-targz"),
+        Platform::Mac => named::bash("script/upload-nightly macos"),
+        Platform::Windows => {
+            let cmd = match arch {
+                Arch::X86_64 => "script/upload-nightly.ps1 -Architecture x86_64",
+                Arch::ARM64 => "script/upload-nightly.ps1 -Architecture aarch64",
+            };
+            named::pwsh(cmd).working_directory("${{ env.ZED_WORKSPACE }}")
+        }
+    }
+}
+
+fn build_zed_installer(arch: Arch) -> Step<Run> {
+    let cmd = match arch {
+        Arch::X86_64 => "script/bundle-windows.ps1 -Architecture x86_64",
+        Arch::ARM64 => "script/bundle-windows.ps1 -Architecture aarch64",
+    };
+    named::pwsh(cmd).working_directory("${{ env.ZED_WORKSPACE }}")
+}
+
+fn upload_zed_nightly_windows(arch: Arch) -> Step<Run> {
+    let cmd = match arch {
+        Arch::X86_64 => "script/upload-nightly.ps1 -Architecture x86_64",
+        Arch::ARM64 => "script/upload-nightly.ps1 -Architecture aarch64",
+    };
+    named::pwsh(cmd).working_directory("${{ env.ZED_WORKSPACE }}")
+}
+
+fn update_nightly_tag() -> Step<Run> {
+    named::bash(indoc::indoc! {r#"
+        if [ "$(git rev-parse nightly)" = "$(git rev-parse HEAD)" ]; then
+          echo "Nightly tag already points to current commit. Skipping tagging."
+          exit 0
+        fi
+        git config user.name github-actions
+        git config user.email github-actions@github.com
+        git tag -f nightly
+        git push origin nightly --force
+    "#})
+}
+
+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", vars::SENTRY_AUTH_TOKEN))
+    .add_with(("environment", "production"))
+}

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

@@ -1,4 +1,7 @@
-use crate::tasks::workflows::steps::named;
+use crate::tasks::workflows::{
+    steps::named,
+    vars::{mac_bundle_envs, windows_bundle_envs},
+};
 
 use super::{runners, steps, vars};
 use gh_workflow::*;
@@ -21,14 +24,14 @@ pub fn run_bundling() -> Workflow {
         .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_linux_arm64", bundle_linux(runners::Arch::ARM64))
         .add_job(
             "bundle_windows_x86_64",
             bundle_windows_job(runners::Arch::X86_64),
         )
         .add_job(
             "bundle_windows_arm64",
-            bundle_windows_job(runners::Arch::AARCH64),
+            bundle_windows_job(runners::Arch::ARM64),
         )
 }
 
@@ -44,21 +47,11 @@ fn bundle_job() -> Job {
 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,
-        ))
+        .envs(mac_bundle_envs())
         .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::clear_target_dir_if_large(runners::Platform::Mac))
         .add_step(steps::script("./script/bundle-mac"))
         .add_step(steps::upload_artifact(
             "Zed_${{ github.event.pull_request.head.sha || github.sha }}-aarch64.dmg",
@@ -101,15 +94,7 @@ 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"))
+        .envs(windows_bundle_envs())
         .add_step(steps::checkout_repo())
         .add_step(steps::setup_sentry())
         .add_step(bundle_windows(arch))
@@ -122,7 +107,7 @@ fn bundle_windows_job(arch: runners::Arch) -> Job {
 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"),
+        runners::Arch::ARM64 => named::pwsh("script/bundle-windows.ps1 -Architecture aarch64"),
     };
     step.working_directory("${{ env.ZED_WORKSPACE }}")
 }

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

@@ -19,14 +19,14 @@ impl Into<gh_workflow::RunsOn> for Runner {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum Arch {
     X86_64,
-    AARCH64,
+    ARM64,
 }
 
 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"),
+            Arch::ARM64 => write!(f, "aarch64"),
         }
     }
 }
@@ -35,14 +35,31 @@ impl Arch {
     pub fn triple(&self) -> &'static str {
         match self {
             Arch::X86_64 => "x86_64-unknown-linux-gnu",
-            Arch::AARCH64 => "aarch64-unknown-linux-gnu",
+            Arch::ARM64 => "aarch64-unknown-linux-gnu",
         }
     }
 
     pub fn linux_bundler(&self) -> Runner {
         match self {
             Arch::X86_64 => LINUX_X86_BUNDLER,
-            Arch::AARCH64 => LINUX_ARM_BUNDLER,
+            Arch::ARM64 => LINUX_ARM_BUNDLER,
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Platform {
+    Windows,
+    Linux,
+    Mac,
+}
+
+impl std::fmt::Display for Platform {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Platform::Windows => write!(f, "windows"),
+            Platform::Linux => write!(f, "linux"),
+            Platform::Mac => write!(f, "mac"),
         }
     }
 }

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

@@ -1,6 +1,6 @@
 use gh_workflow::*;
 
-use crate::tasks::workflows::vars;
+use crate::tasks::workflows::{runners::Platform, vars};
 
 const BASH_SHELL: &str = "bash -euxo pipefail {0}";
 // https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsshell
@@ -44,6 +44,48 @@ pub fn setup_sentry() -> Step<Use> {
     .add_with(("token", vars::SENTRY_AUTH_TOKEN))
 }
 
+pub fn cargo_fmt() -> Step<Run> {
+    named::bash("cargo fmt --all -- --check")
+}
+
+pub fn cargo_install_nextest(platform: Platform) -> Step<Run> {
+    named::run(platform, "cargo install cargo-nextest --locked")
+}
+
+pub fn cargo_nextest(platform: Platform) -> Step<Run> {
+    named::run(
+        platform,
+        "cargo nextest run --workspace --no-fail-fast --failure-output immediate-final",
+    )
+}
+
+pub fn setup_cargo_config(platform: Platform) -> Step<Run> {
+    match platform {
+        Platform::Windows => named::pwsh(indoc::indoc! {r#"
+            New-Item -ItemType Directory -Path "./../.cargo" -Force
+            Copy-Item -Path "./.cargo/ci-config.toml" -Destination "./../.cargo/config.toml"
+        "#}),
+
+        Platform::Linux | Platform::Mac => named::bash(indoc::indoc! {r#"
+            mkdir -p ./../.cargo
+            cp ./.cargo/ci-config.toml ./../.cargo/config.toml
+        "#}),
+    }
+}
+
+pub fn cleanup_cargo_config(platform: Platform) -> Step<Run> {
+    let step = match platform {
+        Platform::Windows => named::pwsh(indoc::indoc! {r#"
+            Remove-Item -Recurse -Path "./../.cargo" -Force -ErrorAction SilentlyContinue
+        "#}),
+        Platform::Linux | Platform::Mac => named::bash(indoc::indoc! {r#"
+            rm -rf ./../.cargo
+        "#}),
+    };
+
+    step.if_condition(Expression::new("always()"))
+}
+
 pub fn upload_artifact(name: &str, path: &str) -> Step<Use> {
     Step::new(format!("@actions/upload-artifact {}", name))
         .uses(
@@ -55,9 +97,12 @@ pub fn upload_artifact(name: &str, path: &str) -> Step<Use> {
         .add_with(("path", path))
 }
 
-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 clear_target_dir_if_large(platform: Platform) -> Step<Run> {
+    match platform {
+        Platform::Windows => named::pwsh("./script/clear-target-dir-if-larger-than.ps1 250"),
+        Platform::Linux => named::bash("./script/clear-target-dir-if-larger-than 100"),
+        Platform::Mac => named::bash("./script/clear-target-dir-if-larger-than 300"),
+    }
 }
 
 pub fn script(name: &str) -> Step<Run> {
@@ -68,6 +113,11 @@ pub fn script(name: &str) -> Step<Run> {
     }
 }
 
+pub(crate) struct NamedJob {
+    pub name: String,
+    pub job: Job,
+}
+
 // (janky) helper to generate steps with a name that corresponds
 // to the name of the calling function.
 pub(crate) mod named {
@@ -94,6 +144,18 @@ pub(crate) mod named {
         Step::new(function_name(1)).run(script).shell(PWSH_SHELL)
     }
 
+    /// Runs the command in either powershell or bash, depending on platform.
+    /// (You shouldn't inline this function into the workflow definition, you must
+    /// wrap it in a new function.)
+    pub(crate) fn run(platform: Platform, script: &str) -> Step<Run> {
+        match platform {
+            Platform::Windows => Step::new(function_name(1)).run(script).shell(PWSH_SHELL),
+            Platform::Linux | Platform::Mac => {
+                Step::new(function_name(1)).run(script).shell(BASH_SHELL)
+            }
+        }
+    }
+
     /// Returns a Workflow with the same name as the enclosing module.
     pub(crate) fn workflow() -> Workflow {
         Workflow::default().name(
@@ -105,6 +167,15 @@ pub(crate) mod named {
         )
     }
 
+    /// Returns a Job with the same name as the enclosing function.
+    /// (note job names may not contain `::`)
+    pub(crate) fn job(job: Job) -> NamedJob {
+        NamedJob {
+            name: function_name(1).split("::").last().unwrap().to_owned(),
+            job,
+        }
+    }
+
     /// Returns the function name N callers above in the stack
     /// (typically 1).
     /// This only works because xtask always runs debug builds.

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

@@ -1,4 +1,4 @@
-use gh_workflow::WorkflowCallInput;
+use gh_workflow::{Env, WorkflowCallInput};
 
 macro_rules! secret {
     ($secret_name:ident) => {
@@ -16,20 +16,22 @@ pub fn input(name: &str, input: WorkflowCallInput) -> (String, (&str, WorkflowCa
     return (format!("${{{{ inputs.{name} }}}}"), (name, input));
 }
 
-secret!(GITHUB_TOKEN);
-secret!(CACHIX_AUTH_TOKEN);
-secret!(ZED_CLIENT_CHECKSUM_SEED);
-secret!(ZED_SENTRY_MINIDUMP_ENDPOINT);
-secret!(ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON);
-secret!(MACOS_CERTIFICATE);
-secret!(MACOS_CERTIFICATE_PASSWORD);
+secret!(APPLE_NOTARIZATION_ISSUER_ID);
 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);
+secret!(AZURE_SIGNING_TENANT_ID);
+secret!(CACHIX_AUTH_TOKEN);
+secret!(DIGITALOCEAN_SPACES_ACCESS_KEY);
+secret!(DIGITALOCEAN_SPACES_SECRET_KEY);
+secret!(GITHUB_TOKEN);
+secret!(MACOS_CERTIFICATE);
+secret!(MACOS_CERTIFICATE_PASSWORD);
+secret!(SENTRY_AUTH_TOKEN);
+secret!(ZED_CLIENT_CHECKSUM_SEED);
+secret!(ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON);
+secret!(ZED_SENTRY_MINIDUMP_ENDPOINT);
 
 // todo(ci) make these secrets too...
 var!(AZURE_SIGNING_ACCOUNT_NAME);
@@ -37,3 +39,25 @@ var!(AZURE_SIGNING_CERT_PROFILE_NAME);
 var!(AZURE_SIGNING_ENDPOINT);
 
 pub const GITHUB_SHA: &str = "${{ github.event.pull_request.head.sha || github.sha }}";
+
+pub fn mac_bundle_envs() -> Env {
+    Env::default()
+        .add("MACOS_CERTIFICATE", MACOS_CERTIFICATE)
+        .add("MACOS_CERTIFICATE_PASSWORD", MACOS_CERTIFICATE_PASSWORD)
+        .add("APPLE_NOTARIZATION_KEY", APPLE_NOTARIZATION_KEY)
+        .add("APPLE_NOTARIZATION_KEY_ID", APPLE_NOTARIZATION_KEY_ID)
+        .add("APPLE_NOTARIZATION_ISSUER_ID", APPLE_NOTARIZATION_ISSUER_ID)
+}
+
+pub fn windows_bundle_envs() -> Env {
+    Env::default()
+        .add("AZURE_TENANT_ID", AZURE_SIGNING_TENANT_ID)
+        .add("AZURE_CLIENT_ID", AZURE_SIGNING_CLIENT_ID)
+        .add("AZURE_CLIENT_SECRET", AZURE_SIGNING_CLIENT_SECRET)
+        .add("ACCOUNT_NAME", AZURE_SIGNING_ACCOUNT_NAME)
+        .add("CERT_PROFILE_NAME", AZURE_SIGNING_CERT_PROFILE_NAME)
+        .add("ENDPOINT", AZURE_SIGNING_ENDPOINT)
+        .add("FILE_DIGEST", "SHA256")
+        .add("TIMESTAMP_DIGEST", "SHA256")
+        .add("TIMESTAMP_SERVER", "http://timestamp.acs.microsoft.com")
+}