#!/usr/bin/env bash set -euo pipefail summarize_failed_bazel_test_logs=0 use_node_test_env=0 remote_download_toplevel=0 windows_msvc_host_platform=0 while [[ $# -gt 0 ]]; do case "$1" in --summarize-failed-test-logs) summarize_failed_bazel_test_logs=1 shift ;; --use-node-test-env) use_node_test_env=1 shift ;; --remote-download-toplevel) remote_download_toplevel=1 shift ;; --windows-msvc-host-platform) windows_msvc_host_platform=1 shift ;; --) shift break ;; *) echo "Unknown option: $1" >&2 exit 1 ;; esac done if [[ $# -eq 0 ]]; then echo "Usage: $0 [--summarize-failed-test-logs] [--use-node-test-env] [--remote-download-toplevel] [--windows-msvc-host-platform] -- -- " >&2 exit 1 fi bazel_startup_args=() if [[ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]]; then bazel_startup_args+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}") fi run_bazel() { if [[ "${RUNNER_OS:-}" == "Windows" ]]; then MSYS2_ARG_CONV_EXCL='*' bazel "$@" return fi bazel "$@" } ci_config=ci-linux case "${RUNNER_OS:-}" in macOS) ci_config=ci-macos ;; Windows) ci_config=ci-windows ;; esac summarize_failed_bazel_test_logs() { local console_log="$1" local testlogs_dir= local -a bazel_info_cmd=(bazel) local rust_test_summary rust_test_summary="$(mktemp)" local failed_target_logs=() while IFS= read -r target_log; do failed_target_logs+=("$target_log") done < <( grep -E '^FAIL: //' "$console_log" \ | awk '{ target = $2 test_log = "" if (match($0, /\(see [^)]*test\.log\)/)) { test_log = substr($0, RSTART + 5, RLENGTH - 6) } if (!(target in test_logs) || test_logs[target] == "") { test_logs[target] = test_log } } END { for (target in test_logs) { print target "\t" test_logs[target] } }' \ | sort -u ) if [[ ${#failed_target_logs[@]} -eq 0 ]]; then echo "No failed Bazel test targets were found in console output." rm -f "$rust_test_summary" return fi for target_log in "${failed_target_logs[@]}"; do local target="${target_log%%$'\t'*}" local test_log="${target_log#*$'\t'}" if [[ -z "$test_log" ]]; then if [[ -z "$testlogs_dir" ]]; then if (( ${#bazel_startup_args[@]} > 0 )); then bazel_info_cmd+=("${bazel_startup_args[@]}") fi testlogs_dir="$(run_bazel "${bazel_info_cmd[@]:1}" info bazel-testlogs 2>/dev/null || echo bazel-testlogs)" fi local rel_path="${target#//}" rel_path="${rel_path/:/\/}" test_log="${testlogs_dir}/${rel_path}/test.log" fi if [[ -f "$test_log" ]]; then awk -v target="$target" ' /^failures:$/ { in_failures = 1 next } in_failures && /^ / { print "F\t" target "\t" substr($0, 5) next } in_failures && $0 !~ /^$/ { in_failures = 0 } /^test result: FAILED\./ { line = $0 sub(/^test result: FAILED\. /, "", line) sub(/; finished.*$/, "", line) field_count = split(line, fields, "; ") printf "T\t%s", target for (i = 1; i <= field_count; i++) { split(fields[i], parts, " ") printf "\t%d", parts[1] } print "" } ' "$test_log" >> "$rust_test_summary" fi done echo awk -F '\t' ' BEGIN { print "Rust test failures:" } $1 == "F" { saw_failure = 1 print " FAIL " $2 " " $3 } $1 == "T" { passed += $3 failed += $4 ignored += $5 measured += $6 filtered += $7 result_count += 1 } END { if (!saw_failure) { print " No Rust test failure names found in test logs." } print "" print "Rust test result totals across failed Bazel targets:" if (result_count > 0) { printf " %d passed; %d failed; %d ignored; %d measured; %d filtered out\n", passed, failed, ignored, measured, filtered } else { print " No Rust test result totals found in test logs." } } ' "$rust_test_summary" rm -f "$rust_test_summary" } bazel_args=() bazel_targets=() found_target_separator=0 for arg in "$@"; do if [[ "$arg" == "--" && $found_target_separator -eq 0 ]]; then found_target_separator=1 continue fi if [[ $found_target_separator -eq 0 ]]; then bazel_args+=("$arg") else bazel_targets+=("$arg") fi done if [[ ${#bazel_args[@]} -eq 0 || ${#bazel_targets[@]} -eq 0 ]]; then echo "Expected Bazel args and targets separated by --" >&2 exit 1 fi if [[ $use_node_test_env -eq 1 ]]; then # Bazel test sandboxes on macOS may resolve an older Homebrew `node` # before the `actions/setup-node` runtime on PATH. node_bin="$(which node)" if [[ "${RUNNER_OS:-}" == "Windows" ]]; then node_bin="$(cygpath -w "${node_bin}")" fi bazel_args+=("--test_env=CODEX_JS_REPL_NODE_PATH=${node_bin}") fi post_config_bazel_args=() if [[ "${RUNNER_OS:-}" == "Windows" && $windows_msvc_host_platform -eq 1 ]]; then has_host_platform_override=0 for arg in "${bazel_args[@]}"; do if [[ "$arg" == --host_platform=* ]]; then has_host_platform_override=1 break fi done if [[ $has_host_platform_override -eq 0 ]]; then # Keep Windows Bazel targets on `windows-gnullvm` for cfg coverage, but opt # specific jobs into an MSVC exec platform when they need helper binaries # like Rust test wrappers and V8 generators to resolve a compatible host # toolchain. post_config_bazel_args+=("--host_platform=//:local_windows_msvc") fi fi if [[ $remote_download_toplevel -eq 1 ]]; then # Override the CI config's remote_download_minimal setting when callers need # the built artifact to exist on disk after the command completes. post_config_bazel_args+=(--remote_download_toplevel) fi if [[ -n "${BAZEL_REPO_CONTENTS_CACHE:-}" ]]; then # Windows self-hosted runners can run multiple Bazel jobs concurrently. Give # each job its own repo contents cache so they do not fight over the shared # path configured in `ci-windows`. post_config_bazel_args+=("--repo_contents_cache=${BAZEL_REPO_CONTENTS_CACHE}") fi if [[ -n "${BAZEL_REPOSITORY_CACHE:-}" ]]; then post_config_bazel_args+=("--repository_cache=${BAZEL_REPOSITORY_CACHE}") fi if [[ -n "${CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR:-}" ]]; then post_config_bazel_args+=( "--execution_log_compact_file=${CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR}/execution-log-${bazel_args[0]}-${GITHUB_JOB:-local}-$$.zst" ) fi if [[ "${RUNNER_OS:-}" == "Windows" ]]; then windows_action_env_vars=( INCLUDE LIB LIBPATH PATH UCRTVersion UniversalCRTSdkDir VCINSTALLDIR VCToolsInstallDir WindowsLibPath WindowsSdkBinPath WindowsSdkDir WindowsSDKLibVersion WindowsSDKVersion ) for env_var in "${windows_action_env_vars[@]}"; do if [[ -n "${!env_var:-}" ]]; then post_config_bazel_args+=("--action_env=${env_var}" "--host_action_env=${env_var}") fi done fi bazel_console_log="$(mktemp)" trap 'rm -f "$bazel_console_log"' EXIT bazel_cmd=(bazel) if (( ${#bazel_startup_args[@]} > 0 )); then bazel_cmd+=("${bazel_startup_args[@]}") fi if [[ -n "${BUILDBUDDY_API_KEY:-}" ]]; then echo "BuildBuddy API key is available; using remote Bazel configuration." # Work around Bazel 9 remote repo contents cache / overlay materialization failures # seen in CI (for example "is not a symlink" or permission errors while # materializing external repos such as rules_perl). We still use BuildBuddy for # remote execution/cache; this only disables the startup-level repo contents cache. bazel_run_args=( "${bazel_args[@]}" "--config=${ci_config}" "--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}" ) if (( ${#post_config_bazel_args[@]} > 0 )); then bazel_run_args+=("${post_config_bazel_args[@]}") fi set +e run_bazel "${bazel_cmd[@]:1}" \ --noexperimental_remote_repo_contents_cache \ "${bazel_run_args[@]}" \ -- \ "${bazel_targets[@]}" \ 2>&1 | tee "$bazel_console_log" bazel_status=${PIPESTATUS[0]} set -e else echo "BuildBuddy API key is not available; using local Bazel configuration." # Keep fork/community PRs on Bazel but disable remote services that are # configured in .bazelrc and require auth. # # Flag docs: # - Command-line reference: https://bazel.build/reference/command-line-reference # - Remote caching overview: https://bazel.build/remote/caching # - Remote execution overview: https://bazel.build/remote/rbe # - Build Event Protocol overview: https://bazel.build/remote/bep # # --noexperimental_remote_repo_contents_cache: # disable remote repo contents cache enabled in .bazelrc startup options. # https://bazel.build/reference/command-line-reference#startup_options-flag--experimental_remote_repo_contents_cache # --remote_cache= and --remote_executor=: # clear remote cache/execution endpoints configured in .bazelrc. # https://bazel.build/reference/command-line-reference#common_options-flag--remote_cache # https://bazel.build/reference/command-line-reference#common_options-flag--remote_executor bazel_run_args=( "${bazel_args[@]}" --remote_cache= --remote_executor= ) if (( ${#post_config_bazel_args[@]} > 0 )); then bazel_run_args+=("${post_config_bazel_args[@]}") fi set +e run_bazel "${bazel_cmd[@]:1}" \ --noexperimental_remote_repo_contents_cache \ "${bazel_run_args[@]}" \ -- \ "${bazel_targets[@]}" \ 2>&1 | tee "$bazel_console_log" bazel_status=${PIPESTATUS[0]} set -e fi if [[ ${bazel_status:-0} -ne 0 ]]; then if [[ $summarize_failed_bazel_test_logs -eq 1 ]]; then summarize_failed_bazel_test_logs "$bazel_console_log" fi exit "$bazel_status" fi