name: Setup bazel and Nix
description: Setup Bazel and Nix for CI builds and tests

inputs:
  useCache:
    description: "Cache Bazel artifacts. Use 'true' to enable with rw, 'readonly' to download, 'rbe' to enable with remote execution, 'log' to disable cache but upload logs, and 'false' to disable."
    default: "false"
    required: true
  buildBuddyApiKey:
    description: "BuildBuddy API key for caching Bazel artifacts"
    required: false
  rbePlatform:
    description: "RBE platform to use. If empty, RBE will not be used."
    required: false
  nixTools:
    description: "Nix tools to install as list of strings separated by newlines. If empty, no tools will be installed."
    default: ""
    required: false

runs:
  using: "composite"
  steps:
    - name: Check inputs
      id: check_inputs
      shell: bash
      run: |
        echo "::group::Check inputs"
        if [[ "${{ inputs.useCache }}" != "true" && "${{ inputs.useCache }}" != "readonly" && "${{ inputs.useCache }}" != "rbe" && "${{ inputs.useCache }}" != "logs" && "${{ inputs.useCache }}" != "false" ]]; then
          echo "Invalid value for 'useCache' input: '${{ inputs.useCache }}'. Must be 'true', 'readonly', or 'false'."
          exit 1
        fi
        if [[ "${{ inputs.useCache }}" == "true" || "${{ inputs.useCache }}" == "readonly" || "${{ inputs.useCache }}" == "logs" ]] && [[ -z "${{ inputs.buildBuddyApiKey }}" ]]; then
          echo "BuildBuddy API key is required when cache is enabled."
          exit 1
        fi
        if [[ "${{ inputs.useCache }}" == "rbe" && -z "${{ inputs.rbePlatform }}" ]]; then
          echo "RBE platform is required when cache is enabled."
          exit 1
        fi
        if [[ -n "${{inputs.rbePlatform}}" ]]; then
        case "${{ inputs.rbePlatform }}" in
          ubuntu-22.04)
            echo "rbeConfig=build_barn_rbe_ubuntu_22_04" | tee -a "$GITHUB_OUTPUT"
            ;;

          *)
            echo "Invalid value for 'rbePlatform' input: '${{ inputs.rbePlatform }}'. Must be 'ubuntu-22.04'."
            exit 1
            ;;
        esac
        fi
        if command -v nix; then
          echo "nixPreinstalled=true" | tee -a "$GITHUB_OUTPUT"
        else
          echo "nixPreinstalled=false" | tee -a "$GITHUB_OUTPUT"
        fi
        if command -v bazel; then
          echo "bazelPreinstalled=true" | tee -a "$GITHUB_OUTPUT"
        else
          echo "bazelPreinstalled=false" | tee -a "$GITHUB_OUTPUT"
        fi
        if [[ -f /etc/NIXOS ]]; then
          echo "nixOS=true" | tee -a "$GITHUB_OUTPUT"
        else
          echo "nixOS=false" | tee -a "$GITHUB_OUTPUT"
        fi
        if [[ "$RUNNER_OS" == "Linux" ]]; then
          echo "os=linux" | tee -a "$GITHUB_OUTPUT"
        elif [[ "$RUNNER_OS" == "Windows" ]]; then
          echo "os=windows" | tee -a "$GITHUB_OUTPUT"
         elif [[ "$RUNNER_OS" == "macOS" ]]; then
          echo "os=darwin" | tee -a "$GITHUB_OUTPUT"
        else
            echo "$RUNNER_OS not supported"
            exit 1
        fi
        if [[ "$RUNNER_ARCH" == "X64" ]]; then
          echo "arch=amd64" | tee -a "$GITHUB_OUTPUT"
        elif [[ "$RUNNER_ARCH" == "ARM64" ]]; then
          echo "arch=arm64" | tee -a "$GITHUB_OUTPUT"
        else
            echo "$RUNNER_ARCH not supported"
            exit 1
        fi
        echo "::endgroup::"

    - name: Prepare to install tools
      shell: bash
      run: |
        echo "::group::Prepare to install nix and bazel"
        requiredTools=( "curl" "xz" "unzip" "git" )
        declare -A packageNamesUbuntu=( ["curl"]="curl" ["xz"]="xz-utils" ["unzip"]="unzip" ["git"]="git" )
        missingTools=()
        for tool in "${requiredTools[@]}"; do
          if ! command -v "$tool"; then
            echo "$tool not found, installing..."
            missingTools+=("$tool")
          else
            echo "$tool found $(command -v "$tool")"
          fi
        done
        missingPackagesUbuntu=()
        for tool in "${missingTools[@]}"; do
          echo "Ubuntu name for $tool is ${packageNamesUbuntu[$tool]}"
          missingPackagesUbuntu+=("${packageNamesUbuntu[$tool]}")
        done
        if [[ "${#missingTools[@]}" -gt 0 ]]; then
          echo "Installing missing tools ${missingTools[*]}..."
          if [[ "$RUNNER_OS" == "Linux" ]]; then
            sudo apt-get update || true
            sudo apt-get install -y ${missingPackagesUbuntu[*]} || true
          fi
        fi
        echo "::endgroup::"

    - name: Install nix
      if: steps.check_inputs.outputs.nixPreinstalled == 'false'
      uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23

    - name: Set $USER if not set
      shell: bash
      run: |
        echo "::group::Set \$USER if not set"
        if [[ -z "$USER" ]]; then
          echo "USER=$(id -un)" | tee -a "$GITHUB_ENV"
        fi
        echo "::endgroup::"

    - name: Install Bazelisk
      if: steps.check_inputs.outputs.bazelPreinstalled == 'false' && steps.check_inputs.outputs.nixOS == 'false'
      shell: bash
      env:
        OS: ${{ steps.check_inputs.outputs.os }}
        ARCH: ${{ steps.check_inputs.outputs.arch }}
      run: |
        echo "::group::Install Bazelisk"
        sudo mkdir -p /usr/local/bin
        sudo chown -R "$USER" /usr/local/bin
        curl -fsSLo /usr/local/bin/bazel "https://github.com/bazelbuild/bazelisk/releases/download/v1.18.0/bazelisk-${OS}-${ARCH}"
        chmod +x /usr/local/bin/bazel
        echo "::endgroup::"

    - name: Free up space (Ubuntu)
      shell: bash
      if: startsWith(runner.name, 'GitHub Actions') && runner.os == 'Linux'
      run: |
        echo "::group::Free up space (Ubuntu)"
        echo "Available storage (before):"
        df -h

        sudo apt-get update || true
        sudo apt-get remove -y '^dotnet-.*' || true
        sudo apt-get remove -y '^llvm-.*' || true
        sudo apt-get remove -y 'php.*' || true
        sudo apt-get remove -y '^mongodb-.*' || true
        sudo apt-get remove -y '^mysql-.*' || true
        sudo rm -rf /usr/share/dotnet
        sudo rm -rf /usr/local/lib/android
        sudo rm -rf /opt/ghc
        sudo rm -rf /opt/hostedtoolcache/CodeQL
        sudo docker image prune --all --force
        sudo apt-get autoremove -y || true
        sudo apt-get clean || true

        echo "Available storage (after):"
        df -h
        echo "::endgroup::"

    - name: Configure Bazel (general)
      shell: bash
      env:
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel"
        cat <<EOF >> "${WORKSPACE}/.bazeloverwriterc"
        import %workspace%/bazel/bazelrc/ci.bazelrc
        EOF
        echo "::endgroup::"

    - name: Configure Bazel (rw)
      if: inputs.useCache == 'true' || inputs.useCache == 'readonly'
      shell: bash
      env:
        BUILDBUDDY_ORG_API_KEY: ${{ inputs.buildBuddyApiKey }}
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel"
        cat <<EOF >> "${WORKSPACE}/.bazeloverwriterc"
        common --bes_results_url=https://app.buildbuddy.io/invocation/
        common --bes_backend=grpcs://remote.buildbuddy.io
        common --remote_cache=grpcs://remote.buildbuddy.io
        common --remote_header=x-buildbuddy-api-key=${BUILDBUDDY_ORG_API_KEY}
        cquery --bes_results_url=
        cquery --bes_backend=
        cquery --remote_cache=
        query --bes_results_url=
        query --bes_backend=
        query --remote_cache=
        EOF
        echo "::endgroup::"

    - name: Configure Bazel (readonly)
      if: inputs.useCache == 'readonly'
      shell: bash
      env:
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel (readonly)"
        echo "common --remote_upload_local_results=false" >> "${WORKSPACE}/.bazeloverwriterc"
        echo "::endgroup::"

    - name: Configure Bazel (logs)
      if: inputs.useCache == 'logs'
      shell: bash
      env:
        BUILDBUDDY_ORG_API_KEY: ${{ inputs.buildBuddyApiKey }}
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel"
        cat <<EOF >> "${WORKSPACE}/.bazeloverwriterc"
        common --bes_results_url=https://app.buildbuddy.io/invocation/
        common --bes_backend=grpcs://remote.buildbuddy.io
        common --remote_header=x-buildbuddy-api-key=${BUILDBUDDY_ORG_API_KEY}
        cquery --bes_results_url=
        cquery --bes_backend=
        query --bes_results_url=
        query --bes_backend=
        EOF
        echo "::endgroup::"

    - name: Configure Bazel (rbe)
      if: inputs.useCache == 'rbe'
      shell: bash
      env:
        RBE_CONFIG: ${{ steps.check_inputs.outputs.rbeConfig }}
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel"
        cat <<EOF >> "${WORKSPACE}/.bazeloverwriterc"
        common --config=${RBE_CONFIG}
        common --repository_cache=/repository_cache
        common --repo_env=GOPROXY=http://goproxy:3000
        EOF
        echo "::endgroup::"
    - name: Configure Bazel (rbe logs)
      if: inputs.useCache == 'rbe' && inputs.buildBuddyApiKey != ''
      shell: bash
      env:
        BUILDBUDDY_ORG_API_KEY: ${{ inputs.buildBuddyApiKey }}
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel"
        cat <<EOF >> "${WORKSPACE}/.bazeloverwriterc"
        common --bes_results_url=https://app.buildbuddy.io/invocation/
        common --bes_backend=grpcs://remote.buildbuddy.io
        common --remote_header=x-buildbuddy-api-key=${BUILDBUDDY_ORG_API_KEY}
        cquery --bes_results_url=
        cquery --bes_backend=
        query --bes_results_url=
        query --bes_backend=
        EOF
        echo "::endgroup::"

    - name: Disable disk cache on GitHub Actions runners
      if: startsWith(runner.name , 'GitHub Actions')
      shell: bash
      env:
        WORKSPACE: ${{ github.workspace }}
      run: |
        echo "::group::Configure Bazel (disk cache)"
        echo "common --disk_cache=" >> "${WORKSPACE}/.bazeloverwriterc"
        echo "common --repository_cache=" >> "${WORKSPACE}/.bazeloverwriterc"
        echo "::endgroup::"

    - name: Install nix tools
      if: inputs.nixTools != ''
      shell: bash
      env:
        tools: ${{ inputs.nixTools }}
        repository: ${{ github.repository }}
        gitSha: ${{ github.sha }}
      run: |
        echo "::group::Install nix tools"
        toolsNixList=$(printf ' "%s"' ${tools[@]})
        toolsNixList="[ ${toolsNixList} ]"
        expressionFile=$(mktemp)
        cat << "EOF" > "${expressionFile}"
        { tools, repository, rev }:
        let
          repoFlake = builtins.getFlake ("github:" + repository + "/" + rev);
          nixpkgs = repoFlake.inputs.nixpkgsUnstable;
          pkgs = import nixpkgs { system = builtins.currentSystem; };
          toolPkgs = map (p: pkgs.${p}) tools;
        in
        {
          tools = pkgs.symlinkJoin { name = "tools"; paths = [ toolPkgs ]; };
          pathVar = pkgs.lib.makeBinPath toolPkgs;
        }
        EOF
        # ensure the store paths are created
        nix-build \
          --no-out-link \
          --arg tools "${toolsNixList}" \
          --argstr repository "${repository}" \
          --argstr rev "${gitSha}" \
          --attr tools \
          "${expressionFile}"
        # evaluate the path expression
        # EXTRA_PATH=/nix/store/...:/nix/store/...:/nix/store/...
        EXTRA_PATH=$(nix eval --raw --file "${expressionFile}" \
          --arg tools "${toolsNixList}" \
          --argstr repository "${repository}" \
          --argstr rev "${gitSha}" \
          pathVar)
        echo "EXTRA_PATH=${EXTRA_PATH}"
        echo "${EXTRA_PATH}" >> "${GITHUB_PATH}"
        echo "::endgroup::"
