# Turborepo Release Pipeline
#
# This release consists of a few steps
#
# 1. Create a staging branch
# 2. Run some smoke tests on that branch
# 3. Build the Rust binary
# 4. Publish JS packages npm (including turbo itself)
# 5. Create a release branch and open a PR.

# You can opt into a dry run, which will skip publishing to npm and opening the release branch

name: Turborepo Release

env:
  CARGO_PROFILE_RELEASE_LTO: true
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
  RELEASE_TURBO_CLI: true # TODO: do we need this?

on:
  workflow_dispatch:
    inputs:
      increment:
        description: "SemVer Increment (prerelease = bump canary)"
        required: true
        default: "prerelease"
        type: choice
        options:
          # Bump the canary version of the existing semver release
          - prerelease
          # Bump to the next patch version, creating its first canary release
          - prepatch
          # Bump to the next minor version, creating its first canary release
          - preminor
          # Bump to the next major version, creating its first canary release
          - premajor
          # Bump to the next patch version
          - patch
          # Bump to the next minor version
          - minor
          # Bump to the next major version
          - major
      dry_run:
        description: "Do a dry run, skipping the final publish step."
        type: boolean
      tag-override:
        description: "Override default npm dist-tag for the release. Should only be used for backporting"
        required: false
        type: string
      ci-tag-override:
        description: "Override default npm dist-tag to use for running tests. Should only be used when the most recent release was faulty"
        required: false
        type: string
        default: ""

jobs:
  stage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup-node
        with:
          enable-corepack: false
      - name: Configure git
        run: |
          git config --global user.name 'Turbobot'
          git config --global user.email 'turbobot@vercel.com'
      - name: Version
        run: |
          ./scripts/version.js ${{ inputs.increment }} ${{ inputs.tag-override }}
          cat version.txt
      - name: Stage Commit
        id: stage
        run: cd cli && make stage-release && echo "STAGE_BRANCH=$(git branch --show-current)" >> $GITHUB_OUTPUT
      - name: Show Stage Commit
        run: echo "${{ steps.stage.outputs.STAGE_BRANCH }}"
    outputs:
      stage-branch: "${{ steps.stage.outputs.STAGE_BRANCH }}"

  rust-smoke-test:
    name: Rust Unit Tests
    runs-on: ubuntu-latest
    needs: [stage]
    steps:
      - name: Show Stage Commit
        run: echo "${{ needs.stage.outputs.stage-branch }}"
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.stage.outputs.stage-branch }}
      - name: Setup Turborepo Environment
        uses: ./.github/actions/setup-turborepo-environment
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Run Rust Unit Tests
        run: cargo groups test turborepo

  js-smoke-test:
    name: JS Package Tests
    runs-on: ubuntu-latest
    needs: [stage]
    steps:
      - name: Show Stage Commit
        run: echo "${{ needs.stage.outputs.stage-branch }}"
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.stage.outputs.stage-branch }}
      - name: Setup Turborepo Environment
        uses: ./.github/actions/setup-turborepo-environment
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"
      - name: Install Global Turbo
        uses: ./.github/actions/install-global-turbo
        with:
          turbo-version: "${{ github.event.inputs.ci-tag-override }}"
      - name: Run JS Package Tests
        run: turbo run check-types test --filter="./packages/*" --color

  build-rust:
    name: "Build Rust"
    needs: [stage]
    strategy:
      fail-fast: false
      matrix:
        settings:
          - host: macos-latest
            target: "x86_64-apple-darwin"
          - host: macos-latest
            target: "aarch64-apple-darwin"
          - host: ubuntu-latest
            target: "x86_64-unknown-linux-musl"
            setup: "sudo apt-get update && sudo apt-get install -y build-essential clang lldb llvm libclang-dev curl musl-tools sudo unzip"
          - host: ubuntu-latest
            target: "aarch64-unknown-linux-musl"
            rust-build-env: 'CC_aarch64_unknown_linux_musl=clang AR_aarch64_unknown_linux_musl=llvm-ar RUSTFLAGS="-Clink-self-contained=yes -Clinker=rust-lld"'
            setup: "sudo apt-get update && sudo apt-get install -y build-essential musl-tools clang llvm gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu"
          - host: windows-latest
            target: x86_64-pc-windows-msvc
    runs-on: ${{ matrix.settings.host }}
    steps:
      - name: Show Stage Commit
        run: echo "${{ needs.stage.outputs.stage-branch }}"
      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          ref: "${{ needs.stage.outputs.stage-branch }}"

      - name: Setup Protoc
        uses: arduino/setup-protoc@v3
        with:
          version: "26.x"
          repo-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup capnproto
        uses: ./.github/actions/setup-capnproto

      - name: Rust Setup
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          target: ${{ matrix.settings.target }}
          # needed to not make it override the defaults
          rustflags: ""
          # we want more specific settings
          cache: false

      - name: Build Setup
        shell: bash
        if: ${{ matrix.settings.setup }}
        run: ${{ matrix.settings.setup }}

      - name: Build
        run: ${{ matrix.settings.rust-build-env }} cargo build --profile release-turborepo -p turbo --target ${{ matrix.settings.target }}

      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: turbo-${{ matrix.settings.target }}
          path: target/${{ matrix.settings.target }}/release-turborepo/turbo*

  npm-publish:
    name: "Publish To NPM"
    runs-on: ubuntu-latest
    needs: [stage, build-rust, rust-smoke-test, js-smoke-test]
    steps:
      - name: Show Stage Commit
        run: echo "${{ needs.stage.outputs.stage-branch }}"
      - uses: actions/checkout@v4
        with:
          ref: "${{ needs.stage.outputs.stage-branch }}"
      - run: git fetch origin --tags
      - uses: ./.github/actions/setup-node
        with:
          enable-corepack: false

      - name: Install Global Turbo
        uses: ./.github/actions/install-global-turbo
        with:
          turbo-version: "${{ github.event.inputs.ci-tag-override }}"

      - name: Configure git
        run: |
          git config --global user.name 'Turbobot'
          git config --global user.email 'turbobot@vercel.com'

      - name: Download Rust artifacts
        uses: actions/download-artifact@v4
        with:
          path: rust-artifacts

      - name: Move Rust artifacts into place
        run: |
          mv rust-artifacts/turbo-aarch64-apple-darwin cli/dist-darwin-arm64
          mv rust-artifacts/turbo-aarch64-unknown-linux-musl cli/dist-linux-arm64
          cp -r rust-artifacts/turbo-x86_64-pc-windows-msvc cli/dist-windows-arm64
          mv rust-artifacts/turbo-x86_64-unknown-linux-musl cli/dist-linux-x64
          mv rust-artifacts/turbo-x86_64-apple-darwin cli/dist-darwin-x64
          mv rust-artifacts/turbo-x86_64-pc-windows-msvc cli/dist-windows-x64

      - name: Perform Release
        run: cd cli && make publish-turbo SKIP_PUBLISH=${{ inputs.dry_run && '--skip-publish' || '' }}
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

      # Upload published artifacts in case they are needed for debugging later
      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: turbo-combined
          path: cli/dist

  create-release-pr:
    name: "Open Release Branch PR"
    needs: [stage, npm-publish]
    runs-on: ubuntu-latest
    steps:
      - name: Show Stage Commit
        run: echo "${{ needs.stage.outputs.stage-branch }}"
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.stage.outputs.stage-branch }}
      - name: Get version
        id: getVersion
        run: echo "version=$(head -n 1 version.txt)" >> $GITHUB_OUTPUT
      - name: Create pull request
        uses: thomaseizinger/create-pull-request@master
        if: ${{ !inputs.dry_run }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          head: ${{ needs.stage.outputs.stage-branch }}
          base: main
          title: "release(turborepo): ${{ steps.getVersion.outputs.version }}"
