Group Linux Bazel CI jobs by toolchain config using a reusable workflow.

Extract the Linux Bazel job steps and the inner (std, compilation_mode) matrix into a reusable workflow (bazel_linux.yml) invoked per toolchain configuration from ci.yml. This groups each toolchain configuration into its own collapsible matrix in the GitHub Actions UI and eliminates the need for matrix exclude rules.

PiperOrigin-RevId: 999694727
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..074e87c
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,84 @@
+# Copyright 2026 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17
+
+common --show_timestamps
+
+# Retry longer to avoid flakes when fetching external dependencies.
+common --http_connector_attempts=10
+common --http_timeout_scaling=2.0
+
+# Link GoogleTest against Abseil.
+build --define=absl=1
+
+test --keep_going
+test --test_env=GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1
+test --test_env=TZDIR=absl/time/internal/cctz/testdata/zoneinfo
+test --test_output=errors
+test --test_tag_filters=-benchmark
+
+# Configure our Linux Docker toolchain setup.
+common --extra_toolchains=@docker_toolchain//:all
+build:linux --copt=-Werror
+build:linux --copt=-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1
+build:linux --features=external_include_paths
+build:linux --per_file_copt=external/.*@-w
+build:linux --host_per_file_copt=external/.*@-w
+
+build:gcc --config=linux --platforms=//ci:linux_gcc
+build:clang --config=linux --platforms=//ci:linux_clang --conlyopt=-Wno-unused-command-line-argument
+build:clang-arm --config=linux --platforms=//ci:linux_arm_clang --conlyopt=-Wno-unused-command-line-argument
+build:libstdc++ --config=clang --features=libstdc++ --copt=-march=haswell
+
+# Sanitizer configurations.
+build:ubsan --features=ubsan
+build:ubsan --copt=-fsanitize=float-divide-by-zero,nullability,undefined
+build:ubsan --copt=-fno-sanitize-recover=float-divide-by-zero,nullability,undefined
+build:ubsan --copt=-fno-sanitize-blacklist
+test:ubsan --test_env=UBSAN_OPTIONS=print_stacktrace=1
+
+build:asan --config=clang --config=ubsan --features=asan
+build:asan --copt=-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG
+build:asan --linkopt=-fsanitize-link-c++-runtime
+test:asan --test_tag_filters=-benchmark,-noasan
+
+build:tsan --config=clang --features=tsan
+build:tsan --copt=-fno-sanitize-blacklist
+build:tsan --build_tag_filters=-notsan
+test:tsan --test_tag_filters=-benchmark,-notsan
+
+# Remote build configuration for CI.
+build:remote-base --define=EXECUTOR=remote
+build:remote-base --remote_timeout=1200 --bes_timeout=600s
+build:remote-base --jobs=50
+build:remote-base --remote_download_minimal
+# Retry up for up to 15 minutes under heavy load (exponential backoff).
+build:remote-base --remote_retries=20 --remote_retry_max_delay=60s
+
+build:remote --config=remote-base
+build:remote --extra_execution_platforms=//ci:rbe
+build:remote --host_platform=//ci:rbe
+build:remote --platforms=//ci:rbe
+build:remote --remote_cache=grpcs://us-east1-remotebuildexecution.googleapis.com
+build:remote --remote_executor=grpcs://us-east1-remotebuildexecution.googleapis.com
+build:remote --remote_instance_name=projects/google.com:absl-177019/instances/absl-ci-east
+
+build:remote-arm --config=remote-base
+build:remote-arm --extra_execution_platforms=//ci:rbe-arm
+build:remote-arm --host_platform=//ci:rbe-arm
+build:remote-arm --platforms=//ci:rbe-arm
+build:remote-arm --remote_cache=grpcs://us-central1-remotebuildexecution.googleapis.com
+build:remote-arm --remote_executor=grpcs://us-central1-remotebuildexecution.googleapis.com
+build:remote-arm --remote_instance_name=projects/google.com:absl-177019/instances/gloop-ci-central
diff --git a/.github/workflows/bazel_linux.yml b/.github/workflows/bazel_linux.yml
new file mode 100644
index 0000000..cd39f2a
--- /dev/null
+++ b/.github/workflows/bazel_linux.yml
@@ -0,0 +1,105 @@
+# Copyright 2026 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Linux Bazel Tests
+
+on:
+  workflow_call:
+    inputs:
+      toolchain:
+        description: "Bazel toolchain config (--config=<toolchain>)"
+        required: true
+        type: string
+      exceptions:
+        description: "Exception handling flag (-fexceptions or -fno-exceptions)"
+        required: false
+        default: "-fno-exceptions"
+        type: string
+      alternate_options:
+        description: "Whether to use ci/absl_alternate_options.h"
+        required: false
+        default: false
+        type: boolean
+      arch_suffix:
+        description: "Architecture suffix for runner and remote config (e.g. -arm)"
+        required: false
+        default: ""
+        type: string
+      stds:
+        description: "JSON array of C++ standards to test"
+        required: false
+        default: '["c++17", "c++20", "c++23"]'
+        type: string
+      compilation_modes:
+        description: "JSON array of Bazel compilation modes to test"
+        required: false
+        default: '["opt", "fastbuild"]'
+        type: string
+
+permissions:
+  contents: read
+
+jobs:
+  test:
+    runs-on: ubuntu-24.04${{ inputs.arch_suffix }}
+    name: (${{ matrix.std }}, ${{ matrix.compilation_mode }})
+    container:
+      image: gcr.io/google.com/absl-177019/gloop-linux-toolchain-latest@sha256:a820c79d3f356a64db121a4ee16c4408df18f9d2c8bf6c6abfae21666ac003ba
+      options: --cap-add=SYS_PTRACE
+      credentials:
+        # TODO(b/573304451): Switch to Workload Identity Federation to avoid the need for secrets.
+        username: _json_key
+        password: ${{ secrets.GAR_SERVICE_ACCOUNT }}
+    strategy:
+      fail-fast: false  # Don't cancel all jobs if one fails.
+      matrix:
+        std: ${{ fromJSON(inputs.stds) }}
+        compilation_mode: ${{ fromJSON(inputs.compilation_modes) }}
+    env:
+      USE_BAZEL_VERSION: 9.x
+    steps:
+      - name: Checkout
+        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0
+
+      - name: Set up alternate options
+        if: ${{ inputs.alternate_options }}
+        run: cp ci/absl_alternate_options.h absl/base/options.h
+
+      - name: Setup continuous run
+        if: ${{ github.event_name == 'schedule' }}
+        # Force builds and tests to run on scheduled runs.
+        run: echo "EXTRA_BAZEL_ARGS=--cache_test_results=no --action_env=FORCE_REBUILD=1" >> $GITHUB_ENV
+
+      - name: Set up Bazel and caches
+        uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0
+        with:
+          cache-save: true
+          bazelisk-cache: true
+          external-cache: true
+          repository-cache: true
+          google-credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
+          bazelrc: common --config=remote${{ inputs.arch_suffix }}
+
+      - name: Fetch all external dependencies
+        run: bazel fetch //...
+
+      - name: Run tests
+        run: >
+          bazel test ...
+          --config=${{ inputs.toolchain }}
+          --cxxopt=-std=${{ matrix.std }}
+          --host_cxxopt=-std=${{ matrix.std }}
+          --compilation_mode=${{ matrix.compilation_mode }}
+          --copt=${{ inputs.exceptions }}
+          ${{ env.EXTRA_BAZEL_ARGS }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..2302b9f
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,121 @@
+# Copyright 2026 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: Tests
+
+on:
+  # continuous runs
+  schedule:
+    # Run every hour
+    - cron: "0 * * * *"
+
+  # postsubmit runs
+  push:
+    branches:
+      - master
+      - lts_*
+
+  # presubmit runs
+  pull_request:
+    branches:
+      - master
+      - lts_*
+
+  # manual runs
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+# Avoid running twice at the same time over the same changes.
+concurrency:
+  group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.head_ref || github.ref }}
+  cancel-in-progress: ${{ contains(fromJSON('["pull_request", "workflow_dispatch", "schedule"]'), github.event_name) }}
+
+jobs:
+  # Mirrors the 10 Linux "latest" Bazel Kokoro jobs (parallelized to 1 job per
+  # Bazel invocation). Not yet covered here:
+  #   - "Linux GCC/libstdc++ (floor) Bazel" (requires a floor GCC Docker image)
+  #   - CMake, macOS, and Windows Kokoro jobs
+  linux:
+    name: Linux ${{ matrix.config.name }} Bazel
+    strategy:
+      fail-fast: false  # Don't cancel all jobs if one fails.
+      matrix:
+        config:
+          # linux_clang-latest_libcxx_bazel.cfg
+          - name: Clang/libc++
+            toolchain: clang
+          # linux_clang-latest_libcxx_exceptions_bazel.cfg (fastbuild only)
+          - name: Clang/libc++ -fexceptions
+            toolchain: clang
+            exceptions: -fexceptions
+            compilation_modes: '["fastbuild"]'
+          # linux_clang-latest_libcxx_alternate_options_bazel.cfg
+          - name: Clang/libc++ alternate options
+            toolchain: clang
+            alternate_options: true
+          # linux_clang-latest_libcxx_asan_bazel.cfg (fastbuild only)
+          - name: Clang/libc++ ASAN
+            toolchain: asan
+            compilation_modes: '["fastbuild"]'
+          # linux_clang-latest_libcxx_tsan_bazel.cfg (fastbuild only)
+          - name: Clang/libc++ TSAN
+            toolchain: tsan
+            compilation_modes: '["fastbuild"]'
+          # linux_clang-latest_libstdcxx_bazel.cfg
+          - name: Clang/libstdc++
+            toolchain: libstdc++
+          # linux_gcc-latest_libstdcxx_bazel.cfg
+          - name: GCC/libstdc++
+            toolchain: gcc
+          # linux_gcc-latest_libstdcxx_exceptions_bazel.cfg (fastbuild only)
+          - name: GCC/libstdc++ -fexceptions
+            toolchain: gcc
+            exceptions: -fexceptions
+            compilation_modes: '["fastbuild"]'
+          # linux_gcc-latest_libstdcxx_alternate_options_bazel.cfg
+          - name: GCC/libstdc++ alternate options
+            toolchain: gcc
+            alternate_options: true
+          # linux_arm_clang-latest_libcxx_bazel.cfg (c++17 and c++20 only)
+          - name: ARM64 Clang/libc++
+            toolchain: clang-arm
+            arch_suffix: -arm
+            stds: '["c++17", "c++20"]'
+    uses: ./.github/workflows/bazel_linux.yml
+    secrets: inherit
+    with:
+      toolchain: ${{ matrix.config.toolchain }}
+      exceptions: ${{ matrix.config.exceptions || '-fno-exceptions' }}
+      alternate_options: ${{ matrix.config.alternate_options || false }}
+      arch_suffix: ${{ matrix.config.arch_suffix || '' }}
+      stds: ${{ matrix.config.stds || '["c++17", "c++20", "c++23"]' }}
+      compilation_modes: ${{ matrix.config.compilation_modes || '["opt", "fastbuild"]' }}
+
+  # Provide a single predictably-named job for copybara to look for
+  all-blocking-tests:
+    name: All Blocking Tests
+    needs: [linux]
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check for cancellations
+        if: ${{ contains(join(needs.*.result, ' '), 'cancelled') }}
+        run: echo "One or more tests were cancelled.  If this was a presubmit job cancelled by a submit job, you can ignore this." && exit 1
+      - name: Check for failures
+        if: ${{ contains(join(needs.*.result, ' '), 'failure') }}
+        run: echo "One or more tests failed.  Please check the logs above and fix the issues." && exit 1
+    # This workflow must run even if one or more of the dependent workflows
+    # failed.
+    if: always()
diff --git a/MODULE.bazel b/MODULE.bazel
index 00ab4c3..d4096d2 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -50,3 +50,9 @@
     version = "20260708.rc1",
     repo_name = "do_not_use_for_gloop_visibility_only",
 )
+
+bazel_dep(name = "docker_toolchain", version = "0.1", dev_dependency = True)
+local_path_override(
+    module_name = "docker_toolchain",
+    path = "ci/docker_toolchain",
+)
diff --git a/ci/BUILD.bazel b/ci/BUILD.bazel
new file mode 100644
index 0000000..607a356
--- /dev/null
+++ b/ci/BUILD.bazel
@@ -0,0 +1,70 @@
+# Copyright 2026 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package(default_visibility = ["//visibility:private"])
+
+platform(
+    name = "rbe",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+    ],
+    exec_properties = {
+        "OSArch": "amd64",
+        "cache-silo-key": "n2-highcpu-2-2026-10-14",
+        "container-image": "docker://gcr.io/google.com/absl-177019/gloop-linux-latest@sha256:e199623ad0b61b30d240e8bbddf473f65cb1a11da09462001d83b5066dbd9e98",
+        "dockerRunAsRoot": "true",
+    },
+)
+
+platform(
+    name = "rbe-arm",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:aarch64",
+    ],
+    exec_properties = {
+        "OSArch": "arm64",
+        "cache-silo-key": "t2a-standard-4-2026-10-14",
+        "container-image": "docker://gcr.io/google.com/absl-177019/gloop-linux-latest@sha256:e199623ad0b61b30d240e8bbddf473f65cb1a11da09462001d83b5066dbd9e98",
+        "dockerRunAsRoot": "true",
+    },
+)
+
+platform(
+    name = "linux_gcc",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+        "@bazel_tools//tools/cpp:gcc",
+    ],
+)
+
+platform(
+    name = "linux_clang",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+        "@bazel_tools//tools/cpp:clang",
+    ],
+)
+
+platform(
+    name = "linux_arm_clang",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:aarch64",
+        "@bazel_tools//tools/cpp:clang",
+    ],
+)
diff --git a/ci/docker_toolchain/BUILD.bazel b/ci/docker_toolchain/BUILD.bazel
new file mode 100644
index 0000000..ae482b0
--- /dev/null
+++ b/ci/docker_toolchain/BUILD.bazel
@@ -0,0 +1,21 @@
+# Copyright 2026 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Standalone fallback toolchain module when building outside the Docker container.
+# Inside the container, this module is overridden via /etc/bazel.bazelrc to point to an actual
+# toolchain.
+
+package(default_visibility = ["//visibility:public"])
+
+filegroup(name = "empty")
diff --git a/ci/docker_toolchain/MODULE.bazel b/ci/docker_toolchain/MODULE.bazel
new file mode 100644
index 0000000..10d8650
--- /dev/null
+++ b/ci/docker_toolchain/MODULE.bazel
@@ -0,0 +1,18 @@
+# Copyright 2026 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module(
+    name = "docker_toolchain",
+    version = "0.1",
+)