name: release
on:
  schedule:
    - cron: "0 0 * * *" # midnight UTC

  workflow_dispatch:

  push:
    branches:
      - release
      - trigger-nightly

env:
  CARGO_INCREMENTAL: 0
  CARGO_NET_RETRY: 10
  RUSTFLAGS: "-D warnings -W unreachable-pub"
  RUSTUP_MAX_RETRIES: 10
  FETCH_DEPTH: 0 # pull in the tags for the version string
  MACOSX_DEPLOYMENT_TARGET: 14.0
  ZIG_VERSION: 0.13.0
  ZIGBUILD_VERSION: 0.19.8

jobs:
  dist:
    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }}
    strategy:
      matrix:
        include:
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            code-target: win32-x64
            pgo: clap-rs/clap@v4.5.36
          - os: windows-latest
            target: i686-pc-windows-msvc
            pgo: clap-rs/clap@v4.5.36
          - os: windows-latest
            target: aarch64-pc-windows-msvc
            code-target: win32-arm64
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            # Use a container with glibc 2.28
            # Zig is not used because it doesn't work with PGO
            container: quay.io/pypa/manylinux_2_28_x86_64
            code-target: linux-x64
            pgo: clap-rs/clap@v4.5.36
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
            container: quay.io/pypa/manylinux_2_28_aarch64
            code-target: linux-arm64
            pgo: clap-rs/clap@v4.5.36
          - os: ubuntu-latest
            target: arm-unknown-linux-gnueabihf
            zig_target: arm-unknown-linux-gnueabihf.2.28
            code-target: linux-armhf
          - os: macos-14
            target: x86_64-apple-darwin
            code-target: darwin-x64
            pgo: clap-rs/clap@v4.5.36
          - os: macos-14
            target: aarch64-apple-darwin
            code-target: darwin-arm64
            pgo: clap-rs/clap@v4.5.36

    name: dist (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    container: ${{ matrix.container }}

    env:
      RA_TARGET: ${{ matrix.target }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: ${{ env.FETCH_DEPTH }}

      - name: Install Node.js toolchain
        uses: actions/setup-node@v6
        with:
          node-version: 22

      - name: Install rustup
        if: ${{ matrix.container }}
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH

      - name: Install Rust toolchain
        run: |
          rustup update --no-self-update stable
          # llvm-tools contain the llvm-profdata tool which is needed for PGO
          rustup component add rust-src ${{ matrix.pgo && 'llvm-tools' || '' }}
          rustup target add ${{ matrix.target }}

      - name: Install Zig toolchain
        if: ${{ matrix.zig_target }}
        run: |
          which cargo
          curl -L "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-$(uname -m)-${ZIG_VERSION}.tar.xz" | sudo tar JxC /usr/local
          sudo ln -s "/usr/local/zig-linux-$(uname -m)-${ZIG_VERSION}/zig" /usr/local/bin/zig
          curl -L "https://github.com/rust-cross/cargo-zigbuild/releases/download/v${ZIGBUILD_VERSION}/cargo-zigbuild-v${ZIGBUILD_VERSION}.x86_64-unknown-linux-musl.tar.gz" | tar zxC ~/.cargo/bin

      - name: Dist (plain)
        if: ${{ !matrix.zig_target }}
        run: cargo xtask dist --client-patch-version ${{ github.run_number }} ${{ matrix.pgo && format('--pgo {0}', matrix.pgo) || ''}}

      - name: Dist (using zigbuild)
        if: ${{ matrix.zig_target }}
        run: RA_TARGET=${{ matrix.zig_target}} cargo xtask dist --client-patch-version ${{ github.run_number }} --zig ${{ matrix.pgo && format('--pgo {0}', matrix.pgo) || ''}}

      - run: npm ci
        working-directory: editors/code

      - name: Package Extension (release)
        if: github.ref == 'refs/heads/release' && matrix.code-target
        run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
        working-directory: editors/code

      - name: Package Extension (nightly)
        if: github.ref != 'refs/heads/release' && matrix.code-target
        run: npx vsce package -o "../../dist/rust-analyzer-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
        working-directory: editors/code

      - if: matrix.target == 'x86_64-unknown-linux-gnu'
        run: rm -rf editors/code/server

      - if: matrix.target == 'x86_64-unknown-linux-gnu' && github.ref == 'refs/heads/release'
        run: npx vsce package -o ../../dist/rust-analyzer-no-server.vsix
        working-directory: editors/code

      - if: matrix.target == 'x86_64-unknown-linux-gnu' && github.ref != 'refs/heads/release'
        run: npx vsce package -o ../../dist/rust-analyzer-no-server.vsix --pre-release
        working-directory: editors/code

      - name: Run analysis-stats on rust-analyzer
        if: matrix.target == 'x86_64-unknown-linux-gnu'
        run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats . -q

      - name: Run analysis-stats on rust std library
        if: matrix.target == 'x86_64-unknown-linux-gnu'
        env:
          RUSTC_BOOTSTRAP: 1
        run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps --no-sysroot --no-test $(rustc --print sysroot)/lib/rustlib/src/rust/library/std -q

      - name: Upload artifacts
        uses: actions/upload-artifact@v7
        with:
          name: dist-${{ matrix.target }}
          path: ./dist

  dist-x86_64-unknown-linux-musl:
    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }}
    name: dist (x86_64-unknown-linux-musl)
    runs-on: ubuntu-latest
    env:
      RA_TARGET: x86_64-unknown-linux-musl
      # For some reason `-crt-static` is not working for clang without lld
      RUSTFLAGS: "-C link-arg=-fuse-ld=lld -C target-feature=-crt-static"
    container:
      image: rust:alpine
      volumes:
        - /usr/local/cargo/registry:/usr/local/cargo/registry

    steps:
      - name: Install dependencies
        run: apk add --no-cache git clang lld musl-dev nodejs npm

      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: ${{ env.FETCH_DEPTH }}

      - name: Dist
        run: cargo xtask dist --client-patch-version ${{ github.run_number }}

      - run: npm ci
        working-directory: editors/code

      - name: Package Extension (release)
        if: github.ref == 'refs/heads/release'
        run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64
        working-directory: editors/code

      - name: Package Extension (nightly)
        if: github.ref != 'refs/heads/release'
        run: npx vsce package -o "../../dist/rust-analyzer-alpine-x64.vsix" --target alpine-x64 --pre-release
        working-directory: editors/code

      - run: rm -rf editors/code/server

      - name: Upload artifacts
        uses: actions/upload-artifact@v7
        with:
          name: dist-x86_64-unknown-linux-musl
          path: ./dist

  publish:
    if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event_name == 'workflow_dispatch' }}
    name: publish
    runs-on: ubuntu-latest
    needs: ["dist", "dist-x86_64-unknown-linux-musl"]
    steps:
      - name: Install Nodejs
        uses: actions/setup-node@v6
        with:
          node-version: 22

      - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
        if: github.ref == 'refs/heads/release'
      - run: echo "TAG=nightly" >> $GITHUB_ENV
        if: github.ref != 'refs/heads/release'
      - run: 'echo "TAG: $TAG"'

      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: ${{ env.FETCH_DEPTH }}

      - run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
      - run: 'echo "HEAD_SHA: $HEAD_SHA"'

      - uses: actions/download-artifact@v8
        with:
          name: dist-aarch64-apple-darwin
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-x86_64-apple-darwin
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-x86_64-unknown-linux-gnu
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-x86_64-unknown-linux-musl
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-aarch64-unknown-linux-gnu
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-arm-unknown-linux-gnueabihf
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-x86_64-pc-windows-msvc
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-i686-pc-windows-msvc
          path: dist
      - uses: actions/download-artifact@v8
        with:
          name: dist-aarch64-pc-windows-msvc
          path: dist
      - run: ls -al ./dist

      - name: Publish Release
        uses: ./.github/actions/github-release
        with:
          files: "dist/*"
          name: ${{ env.TAG }}
          token: ${{ secrets.GITHUB_TOKEN }}

      - run: npm ci
        working-directory: ./editors/code

      - name: Publish Extension (Code Marketplace, release)
        if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
        working-directory: ./editors/code
        # token from https://dev.azure.com/rust-analyzer/
        run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix

      - name: Publish Extension (OpenVSX, release)
        if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
        working-directory: ./editors/code
        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
        timeout-minutes: 2

      - name: Publish Extension (Code Marketplace, nightly)
        if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
        working-directory: ./editors/code
        run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release

      - name: Publish Extension (OpenVSX, nightly)
        if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer'
        working-directory: ./editors/code
        run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix
        timeout-minutes: 2
