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 <> "${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 <> "${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 <> "${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 <> "${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 <> "${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::"