Merge to main
diff --git a/.github/workflows/test_php_ext.yml b/.github/workflows/test_php_ext.yml
deleted file mode 100644
index dfe1951..0000000
--- a/.github/workflows/test_php_ext.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: PHP Extension Tests
-
-on:
-  workflow_call:
-    inputs:
-      safe-checkout:
-        required: true
-        description: "The SHA key for the commit we want to run over"
-        type: string
-
-permissions:
-  contents: read  #  to fetch code (actions/checkout)
-
-jobs:
-  package:
-    name: Package
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout
-        uses: protocolbuffers/protobuf-ci/checkout@v3
-        with:
-          ref: ${{ inputs.safe-checkout }}
-
-      - name: Package extension
-        uses: protocolbuffers/protobuf-ci/bazel@v3
-        with:
-          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
-          bazel-cache: php_ext/${{ matrix.version }}
-          bash: >
-            ./regenerate_stale_files.sh $BAZEL_FLAGS;
-            bazel build //php:release $BAZEL_FLAGS;
-            cp bazel-bin/php/protobuf-*.tgz .
-
-      - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
-        with:
-          name: protobuf-php-release
-          path: protobuf-*.tgz
-
-  build:
-    needs: [package]
-    strategy:
-      fail-fast: false   # Don't cancel all jobs if one fails.
-      matrix:
-        version: ["8.1", "8.2", "8.3"]
-    name: Build ${{ matrix.version }}
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
-        with:
-          name: protobuf-php-release
-
-      - name: Run tests
-        uses: protocolbuffers/protobuf-ci/docker@v3
-        with:
-          image: us-docker.pkg.dev/protobuf-build/containers/test/linux/php-extension:${{ matrix.version }}-a48f26c08d9a803dd0177dda63563f6ea6f7b2d4
-          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
-          skip-staleness-check: true
-          command: protobuf-*.tgz
diff --git a/.github/workflows/test_ruby_install.yml b/.github/workflows/test_ruby_install.yml
new file mode 100644
index 0000000..db86d35
--- /dev/null
+++ b/.github/workflows/test_ruby_install.yml
@@ -0,0 +1,44 @@
+name: Ruby Install Tests
+
+on:
+  workflow_call:
+    inputs:
+      safe-checkout:
+        required: true
+        description: "The SHA key for the commit we want to run over"
+        type: string
+
+jobs:
+  test_ruby_gems:
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - { name: Ruby 2.6, ruby: ruby-2.6.0, bazel: 5.1.1}
+          - { name: Ruby 2.7, ruby: ruby-2.7.0, bazel: 5.1.1}
+          - { name: Ruby 3.0, ruby: ruby-3.0.2, bazel: 5.1.1}
+          - { name: Ruby 3.1, ruby: ruby-3.1.0, bazel: 5.1.1}
+          - { name: Ruby 3.2, ruby: ruby-3.2.0, bazel: 5.1.1}
+          - { name: JRuby 9.2, ruby: jruby-9.2.20.1, bazel: 5.1.1}
+          - { name: JRuby 9.3, ruby: jruby-9.3.4.0, bazel: 5.1.1}
+          - { name: Ruby 2.6 (Bazel6), ruby: ruby-2.6.0, bazel: 6.0.0}
+          - { name: JRuby 9.2 (Bazel6), ruby: jruby-9.2.20.1, bazel: 6.0.0}
+          - { name: Ruby 2.6 (Bazel4), ruby: ruby-2.6.0, bazel: 4.2.3}
+          - { name: JRuby 9.2 (Bazel4), ruby: jruby-9.2.20.1, bazel: 4.2.3}
+
+    name: matrix${{ matrix.none }}
+    uses: ./.github/workflows/tool_docker.yml
+    with:
+      name: Linux ${{ matrix.name }}
+      safe-checkout: ${{ inputs.safe-checkout }}
+      image: us-docker.pkg.dev/protobuf-build/containers/test/linux/ruby:${{ matrix.ruby }}-${{ matrix.bazel }}-a1ac107476f27e4b0e18e8b8510a5a0bd828399f
+      bazel-cache: ruby_install/${{ matrix.ruby }}_${{ matrix.bazel }}
+      run-flags: --entrypoint "/bin/bash"
+      command: >
+          -l -c "
+          bazel build //ruby:release //:protoc $BAZEL_CACHE &&
+          gem install bazel-bin/ruby/google-protobuf-* &&
+          bazel-bin/protoc --proto_path=src --proto_path=ruby/tests --proto_path=ruby --ruby_out=ruby tests/test_import_proto2.proto &&
+          bazel-bin/protoc --proto_path=src --proto_path=ruby/tests --proto_path=ruby --ruby_out=ruby tests/basic_test.proto &&
+          ruby ruby/tests/basic.rb"
+    secrets: inherit
diff --git a/.github/workflows/tool_docker.yml b/.github/workflows/tool_docker.yml
new file mode 100644
index 0000000..c683677
--- /dev/null
+++ b/.github/workflows/tool_docker.yml
@@ -0,0 +1,127 @@
+name: Run a Docker workflow
+
+on:
+  workflow_call:
+    inputs:
+      name:
+        required: True
+        description: "The name to display for the test"
+        type: string
+      image:
+        required: false
+        default: 'us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-aec4d74f2eb6938fc53ef7d9a79a4bf2da24abc1'
+        description: "The docker image to use"
+        type: string
+      safe-checkout:
+        required: true
+        description: "The SHA key for the commit we want to run over"
+        type: string
+      run-flags:
+        required: false
+        description: "Additional flags to pass to docker run"
+        type: string
+      bazel-cache:
+        required: false
+        description: >
+          A unique path for the Bazel cache.  This will trigger the generation
+          of a BAZEL_CACHE environment variant that provides the appropriate
+          flags for any bazel command.
+        type: string
+
+      # WARNING: loading from cache appears to be slower than pull!
+      docker-cache:
+        required: false
+        description: "Enabled caching of pulled docker images."
+        default: false
+        type: boolean
+
+      # Non-Bazel options
+      command:
+        required: false
+        description: "A raw docker command to run"
+        type: string
+
+      # Bazel options
+      bazel:
+        required: false
+        description: "The Bazel command to run"
+        type: string
+
+jobs:
+  run:
+    name: ${{ inputs.name }}
+    timeout-minutes: 120
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout pending changes
+        uses: actions/checkout@v3
+        with:
+          ref: ${{ inputs.safe-checkout }}
+
+      # Authentication
+      - name: Setup QEMU for possible emulation
+        id: qemu-arm64
+        uses: docker/setup-qemu-action@v1
+
+      - name: Authenticate to Google Cloud
+        uses: google-github-actions/auth@v0
+        with:
+          credentials_json: ${{ secrets.GAR_SERVICE_ACCOUNT }}
+          export_environment_variables: true
+      - name: Set up Cloud SDK
+        uses: google-github-actions/setup-gcloud@v1
+      - name: Use gcloud CLI
+        run: gcloud info
+      - name: Authenticate for GAR use
+        run: gcloud auth configure-docker -q us-docker.pkg.dev
+
+      # Create the docker command
+      - name: Validate Docker command
+        if: ${{ inputs.command && inputs.bazel}}
+        run: echo "Invalid specification of both non-Bazel and Bazel command"; exit 1
+      - name: Configure Bazel caching
+        # Skip bazel cache for local act runs due to issue with credential files
+        # and nested docker images
+        if: ${{ inputs.bazel-cache && !github.event.act_local_test }}
+        run: >
+          echo "BAZEL_CACHE=
+          --google_credentials=/workspace/$(basename $GOOGLE_APPLICATION_CREDENTIALS)
+          --remote_cache=https://storage.googleapis.com/protobuf-bazel-cache/protobuf/gha/${{ inputs.bazel-cache }}" >> $GITHUB_ENV
+      - name: Configure Bazel cache updating
+        # External runs should never write to our caches.
+        if: ${{ inputs.bazel-cache && !inputs.safe-checkout && !github.event.act_local_test }}
+        run: echo "BAZEL_CACHE=$BAZEL_CACHE --remote_upload_local_results" >> $GITHUB_ENV
+      - name: Configure Bazel command
+        if: ${{ inputs.bazel }}
+        run: >
+          echo "DOCKER_COMMAND=${{ inputs.bazel }}
+          --keep_going --test_output=errors --test_timeout=600
+          $BAZEL_CACHE" >> $GITHUB_ENV
+
+      # Grab Docker image
+      - name: Check docker cache
+        if: ${{ inputs.docker-cache }}
+        id: check-docker-cache
+        uses: actions/cache@v3
+        with:
+          path: ci/docker/
+          key: ${{ inputs.image }}
+      - name: Pull and store if cache miss
+        if: ${{ inputs.docker-cache && steps.check-docker-cache.outputs.cache-hit != 'true' }}
+        run: >
+          docker pull ${{ inputs.image }} &&
+          mkdir -p ci/docker/$(dirname ${{ inputs.image }}) &&
+          docker image save ${{ inputs.image }} --output ./ci/docker/${{ inputs.image }}.tar
+      - name: Use the cached image on cache hit
+        if: ${{ inputs.docker-cache && steps.check-docker-cache.outputs.cache-hit == 'true' }}
+        run: docker image load --input ./ci/docker/${{ inputs.image }}.tar
+      - name: Pull fresh docker image
+        if: ${{ !inputs.docker-cache }}
+        run: docker pull ${{ inputs.image }}
+
+      - name: Run docker
+        run: >
+          docker run ${{ inputs.run-flags}}
+          -v${{ github.workspace }}:/workspace
+          ${{ inputs.image }}
+          ${{ inputs.command || '$DOCKER_COMMAND' }}
diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel
index 97caa0e..93b5d80 100644
--- a/src/google/protobuf/BUILD.bazel
+++ b/src/google/protobuf/BUILD.bazel
@@ -1541,6 +1541,7 @@
             "-Wno-deprecated-declarations",
         ],
     }),
+    timeout = "long",
     data = [":testdata"],
     deps = [
         ":cc_test_protos",
diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h
index a3cd7b7..24b7ffd 100644
--- a/src/google/protobuf/arena.h
+++ b/src/google/protobuf/arena.h
@@ -44,6 +44,8 @@
 #error "You cannot SWIG proto headers"
 #endif
 
+// NOOP
+
 namespace google {
 namespace protobuf {
 
diff --git a/src/google/protobuf/json/BUILD.bazel b/src/google/protobuf/json/BUILD.bazel
index 72c4136..9b4e2be 100644
--- a/src/google/protobuf/json/BUILD.bazel
+++ b/src/google/protobuf/json/BUILD.bazel
@@ -153,6 +153,7 @@
     timeout = "long",
     srcs = ["internal/lexer_test.cc"],
     copts = COPTS,
+    timeout = "long",
     deps = [
         ":lexer",
         "//src/google/protobuf:port",