CASSNODEJS-12: Deno and Bun support
patch by Jane He; reviewed by Jorge Bay
diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml
new file mode 100644
index 0000000..80726a6
--- /dev/null
+++ b/.github/actions/setup-bun/action.yml
@@ -0,0 +1,14 @@
+name: Set up Bun
+description: Install Bun from the official install script
+
+runs:
+  using: composite
+  steps:
+    - name: Install Bun
+      shell: bash
+      run: |
+        export BUN_INSTALL="$HOME/.bun"
+        curl -fsSL https://bun.com/install | bash
+        echo "BUN_INSTALL=$BUN_INSTALL" >> "$GITHUB_ENV"
+        echo "$BUN_INSTALL/bin" >> "$GITHUB_PATH"
+        "$BUN_INSTALL/bin/bun" --version
diff --git a/.github/actions/setup-ccm/action.yml b/.github/actions/setup-ccm/action.yml
new file mode 100644
index 0000000..dde05d8
--- /dev/null
+++ b/.github/actions/setup-ccm/action.yml
@@ -0,0 +1,136 @@
+name: Set up CCM for integration tests
+description: >
+  Install ccm and its Python toolchain, the Zulu JDKs Cassandra needs,
+  SSL certificates, Simulacron, and pre-download the Cassandra distribution.
+
+inputs:
+  server-version:
+    description: 'Cassandra minor version (e.g. "4.1"); resolved to the latest patch release.'
+    required: true
+
+runs:
+  using: composite
+  steps:
+    # ---- Python for ccm ----
+    - name: Set up Python 3.9.16
+      uses: actions/setup-python@v5
+      with:
+        python-version: "3.9.16"
+
+    - name: Install ccm
+      shell: bash
+      run: |
+        python -m pip install --upgrade pip
+        git clone --depth 1 --single-branch -b cassandra-test https://github.com/apache/cassandra-ccm.git
+        cd cassandra-ccm
+        pip install -r requirements.txt
+        ./setup.py install
+
+    # ---- Install required Zulu JDKs (8/11/17) and capture their homes ----
+    - name: Install Zulu 11
+      id: z11
+      uses: actions/setup-java@v5
+      with:
+        distribution: zulu
+        java-version: "11"
+
+    - name: Install Zulu 17
+      id: z17
+      uses: actions/setup-java@v5
+      with:
+        distribution: zulu
+        java-version: "17"
+
+    - name: Install Zulu 8
+      id: z8
+      uses: actions/setup-java@v5
+      with:
+        distribution: zulu
+        java-version: "8"
+
+    - name: Export JAVA*_HOME variables
+      shell: bash
+      run: |
+        echo "JAVA8_HOME=${{ steps.z8.outputs.path }}"  >> "$GITHUB_ENV"
+        echo "JAVA11_HOME=${{ steps.z11.outputs.path }}" >> "$GITHUB_ENV"
+        echo "JAVA17_HOME=${{ steps.z17.outputs.path }}" >> "$GITHUB_ENV"
+        echo "JAVA_HOME=${{ steps.z8.outputs.path }}"    >> "$GITHUB_ENV"
+        echo "CCM_UPDATE_PID_DEFAULT_TIMEOUT=120"        >> "$GITHUB_ENV"
+
+    - name: Generate SSL certificates
+      shell: bash
+      run: |
+        mkdir -p /home/runner/workspace/tools/ccm/ssl/
+        cd /home/runner/workspace/tools/ccm/ssl/
+        keytool -genkey \
+          -keyalg RSA \
+          -alias cassandra \
+          -keystore keystore.jks \
+          -storepass cassandra \
+          -keypass cassandra \
+          -validity 364635 \
+          -dname "CN=Jane He, OU=TE, O=ASF, L=Santa Clara, ST=CA, C=TE"
+        keytool -export \
+          -alias cassandra \
+          -file cassandra.crt \
+          -keystore keystore.jks \
+          -storepass cassandra
+        openssl x509 \
+          -inform der \
+          -in cassandra.crt \
+          -out cassandra.pem
+        keytool -genkeypair \
+          -keyalg RSA \
+          -alias client \
+          -keystore truststore.jks \
+          -storepass cassandra \
+          -keypass cassandra \
+          -validity 364635 \
+          -dname "CN=Philip Thompson, OU=TE, O=DataStax, L=Santa Clara, ST=CA, C=TE"
+        keytool -importkeystore \
+          -srckeystore truststore.jks \
+          -destkeystore client.p12 \
+          -srcstorepass cassandra \
+          -deststorepass cassandra \
+          -deststoretype PKCS12
+        openssl pkcs12 \
+          -in client.p12 \
+          -passin pass:cassandra \
+          -nokeys \
+          -out client_cert.pem -legacy
+        openssl pkcs12 \
+          -in client.p12 \
+          -passin pass:cassandra \
+          -nodes \
+          -nocerts \
+          -out client_key.pem -legacy
+
+    - name: Install Simulacron
+      shell: bash
+      run: |
+        wget https://github.com/datastax/simulacron/releases/download/0.12.0/simulacron-standalone-0.12.0.jar -O /home/runner/simulacron.jar
+
+    - name: Resolve Cassandra latest patch version
+      shell: bash
+      env:
+        SERVER_VERSION: ${{ inputs.server-version }}
+      run: |
+        PATCH_SERVER_VERSION=$(
+          curl -s https://downloads.apache.org/cassandra/ \
+          | grep -oP '(?<=href=")[0-9]+\.[0-9]+\.[0-9]+(?=)' \
+          | sort -rV \
+          | uniq -w 3 \
+          | grep "^${SERVER_VERSION}\."
+        )
+        echo "Resolved Cassandra ${SERVER_VERSION}.x -> ${PATCH_SERVER_VERSION}"
+        echo "CCM_VERSION=$PATCH_SERVER_VERSION" >> "$GITHUB_ENV"
+
+    - name: Print environment
+      shell: bash
+      run: printenv | sort
+
+    - name: Pre-download Cassandra distribution
+      shell: bash
+      run: |
+        ccm create predownload -v $CCM_VERSION
+        ccm remove
diff --git a/.github/actions/setup-deno/action.yml b/.github/actions/setup-deno/action.yml
new file mode 100644
index 0000000..2f3cd95
--- /dev/null
+++ b/.github/actions/setup-deno/action.yml
@@ -0,0 +1,14 @@
+name: Set up Deno
+description: Install Deno from the official install script (no third-party actions)
+
+runs:
+  using: composite
+  steps:
+    - name: Install Deno
+      shell: bash
+      run: |
+        export DENO_INSTALL="$HOME/.deno"
+        curl -fsSL https://deno.land/install.sh | sh
+        echo "DENO_INSTALL=$DENO_INSTALL" >> "$GITHUB_ENV"
+        echo "$DENO_INSTALL/bin" >> "$GITHUB_PATH"
+        "$DENO_INSTALL/bin/deno" --version
diff --git a/.github/workflows/integration-bun-deno.yml b/.github/workflows/integration-bun-deno.yml
index b5be8cc..7025e32 100644
--- a/.github/workflows/integration-bun-deno.yml
+++ b/.github/workflows/integration-bun-deno.yml
@@ -24,135 +24,19 @@
       - name: Checkout
         uses: actions/checkout@v4
 
-      # ---- Python for ccm ----
-      - name: Set up Python 3.9.16
-        uses: actions/setup-python@v5
+      - name: Set up CCM
+        uses: ./.github/actions/setup-ccm
         with:
-          python-version: "3.9.16"
-
-      - name: Install ccm
-        run: |
-          python -m pip install --upgrade pip
-          git clone --depth 1 --single-branch -b cassandra-test https://github.com/apache/cassandra-ccm.git
-          cd cassandra-ccm
-          pip install -r requirements.txt
-          ./setup.py install
-
-      # ---- Install required Zulu JDKs (8/11/17) and capture their homes ----
-      - name: Install Zulu 11
-        id: z11
-        uses: actions/setup-java@v5
-        with:
-          distribution: zulu
-          java-version: "11"
-
-      - name: Install Zulu 17
-        id: z17
-        uses: actions/setup-java@v5
-        with:
-          distribution: zulu
-          java-version: "17"
-
-      - name: Install Zulu 8
-        id: z8
-        uses: actions/setup-java@v5
-        with:
-          distribution: zulu
-          java-version: "8"
-
-      - name: Export JAVA*_HOME variables
-        run: |
-          echo "JAVA8_HOME=${{ steps.z8.outputs.path }}"  >> "$GITHUB_ENV"
-          echo "JAVA11_HOME=${{ steps.z11.outputs.path }}" >> "$GITHUB_ENV"
-          echo "JAVA17_HOME=${{ steps.z17.outputs.path }}" >> "$GITHUB_ENV"
-          echo "JAVA_HOME=${{ steps.z8.outputs.path }}"    >> "$GITHUB_ENV"
-          echo "CCM_UPDATE_PID_DEFAULT_TIMEOUT=120"        >> "$GITHUB_ENV"
-
-      - name: Generate SSL certificates
-        run: |
-          mkdir -p /home/runner/workspace/tools/ccm/ssl/
-          cd /home/runner/workspace/tools/ccm/ssl/
-          keytool -genkey \
-            -keyalg RSA \
-            -alias cassandra \
-            -keystore keystore.jks \
-            -storepass cassandra \
-            -keypass cassandra \
-            -validity 364635 \
-            -dname "CN=Jane He, OU=TE, O=ASF, L=Santa Clara, ST=CA, C=TE"
-          keytool -export \
-            -alias cassandra \
-            -file cassandra.crt \
-            -keystore keystore.jks \
-            -storepass cassandra
-          openssl x509 \
-            -inform der \
-            -in cassandra.crt \
-            -out cassandra.pem
-          keytool -genkeypair \
-            -keyalg RSA \
-            -alias client \
-            -keystore truststore.jks \
-            -storepass cassandra \
-            -keypass cassandra \
-            -validity 364635 \
-            -dname "CN=Philip Thompson, OU=TE, O=DataStax, L=Santa Clara, ST=CA, C=TE"
-          keytool -importkeystore \
-            -srckeystore truststore.jks \
-            -destkeystore client.p12 \
-            -srcstorepass cassandra \
-            -deststorepass cassandra \
-            -deststoretype PKCS12
-          openssl pkcs12 \
-            -in client.p12 \
-            -passin pass:cassandra \
-            -nokeys \
-            -out client_cert.pem -legacy
-          openssl pkcs12 \
-            -in client.p12 \
-            -passin pass:cassandra \
-            -nodes \
-            -nocerts \
-            -out client_key.pem -legacy
-
-      - name: Install Simulacron
-        run: |
-          wget https://github.com/datastax/simulacron/releases/download/0.12.0/simulacron-standalone-0.12.0.jar -O /home/runner/simulacron.jar
+          server-version: ${{ matrix.SERVER_VERSION }}
 
       # ---- Set up the runtime under test ----
       - name: Set up Bun
         if: matrix.RUNTIME == 'bun'
-        uses: oven-sh/setup-bun@v2
-        with:
-          bun-version: latest
+        uses: ./.github/actions/setup-bun
 
       - name: Set up Deno
         if: matrix.RUNTIME == 'deno'
-        uses: denoland/setup-deno@v2
-        with:
-          deno-version: latest
-
-      - name: Resolve Cassandra latest patch version
-        env:
-          SERVER_VERSION: ${{ matrix.SERVER_VERSION }}
-        run: |
-          PATCH_SERVER_VERSION=$(
-            curl -s https://downloads.apache.org/cassandra/ \
-            | grep -oP '(?<=href=")[0-9]+\.[0-9]+\.[0-9]+(?=)' \
-            | sort -rV \
-            | uniq -w 3 \
-            | grep "^${SERVER_VERSION}\."
-          )
-          echo "Resolved Cassandra ${SERVER_VERSION}.x -> ${PATCH_SERVER_VERSION}"
-          echo "CCM_VERSION=$PATCH_SERVER_VERSION" >> "$GITHUB_ENV"
-
-      - name: Print environment
-        run: printenv | sort
-
-      - name: Pre-download Cassandra distribution
-        run: |
-          ccm create predownload -v $CCM_VERSION
-          ccm remove
+        uses: ./.github/actions/setup-deno
 
       - name: Run integration tests (${{ matrix.RUNTIME }})
         run: |
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1decd2c..930ed1d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -51,9 +51,7 @@
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: oven-sh/setup-bun@v2
-        with:
-          bun-version: latest
+      - uses: ./.github/actions/setup-bun
       - run: bun install
       - run: bunx mocha test/unit --recursive -R spec -t 5000
 
@@ -62,9 +60,7 @@
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: denoland/setup-deno@v2
-        with:
-          deno-version: latest
+      - uses: ./.github/actions/setup-deno
       # --allow-scripts runs lifecycle scripts so the kerberos prebuild is fetched.
       - run: deno install --allow-scripts=npm:kerberos
       - run: deno run -A --unstable-detect-cjs npm:mocha@10.8.2 test/unit --recursive -R spec -t 5000
@@ -81,100 +77,10 @@
       - name: Checkout
         uses: actions/checkout@v4
 
-      # ---- Python for ccm ----
-      - name: Set up Python 3.9.16
-        uses: actions/setup-python@v5
+      - name: Set up CCM
+        uses: ./.github/actions/setup-ccm
         with:
-          python-version: "3.9.16"
-
-      - name: Install ccm
-        run: |
-          python -m pip install --upgrade pip
-          git clone --depth 1 --single-branch -b cassandra-test https://github.com/apache/cassandra-ccm.git
-          cd cassandra-ccm
-          pip install -r requirements.txt
-          ./setup.py install
-
-      # ---- Install required Zulu JDKs (8/11/17) and capture their homes ----
-      - name: Install Zulu 11
-        id: z11
-        uses: actions/setup-java@v5
-        with:
-          distribution: zulu
-          java-version: "11"
-
-      - name: Install Zulu 17
-        id: z17
-        uses: actions/setup-java@v5
-        with:
-          distribution: zulu
-          java-version: "17"
-
-      - name: Install Zulu 8
-        id: z8
-        uses: actions/setup-java@v5
-        with:
-          distribution: zulu
-          java-version: "8"
-
-      - name: Export JAVA*_HOME variables
-        run: |
-          echo "JAVA8_HOME=${{ steps.z8.outputs.path }}"  >> "$GITHUB_ENV"
-          echo "JAVA11_HOME=${{ steps.z11.outputs.path }}" >> "$GITHUB_ENV"
-          echo "JAVA17_HOME=${{ steps.z17.outputs.path }}" >> "$GITHUB_ENV"
-          echo "JAVA_HOME=${{ steps.z8.outputs.path }}"    >> "$GITHUB_ENV"
-          echo "CCM_UPDATE_PID_DEFAULT_TIMEOUT=120"        >> "$GITHUB_ENV"
-
-      - name: Generate SSL certificates
-        run: |
-          mkdir -p /home/runner/workspace/tools/ccm/ssl/
-          cd /home/runner/workspace/tools/ccm/ssl/
-          keytool -genkey \
-            -keyalg RSA \
-            -alias cassandra \
-            -keystore keystore.jks \
-            -storepass cassandra \
-            -keypass cassandra \
-            -validity 364635 \
-            -dname "CN=Jane He, OU=TE, O=ASF, L=Santa Clara, ST=CA, C=TE"
-          keytool -export \
-            -alias cassandra \
-            -file cassandra.crt \
-            -keystore keystore.jks \
-            -storepass cassandra
-          openssl x509 \
-            -inform der \
-            -in cassandra.crt \
-            -out cassandra.pem
-          keytool -genkeypair \
-            -keyalg RSA \
-            -alias client \
-            -keystore truststore.jks \
-            -storepass cassandra \
-            -keypass cassandra \
-            -validity 364635 \
-            -dname "CN=Philip Thompson, OU=TE, O=DataStax, L=Santa Clara, ST=CA, C=TE"
-          keytool -importkeystore \
-            -srckeystore truststore.jks \
-            -destkeystore client.p12 \
-            -srcstorepass cassandra \
-            -deststorepass cassandra \
-            -deststoretype PKCS12
-          openssl pkcs12 \
-            -in client.p12 \
-            -passin pass:cassandra \
-            -nokeys \
-            -out client_cert.pem -legacy
-          openssl pkcs12 \
-            -in client.p12 \
-            -passin pass:cassandra \
-            -nodes \
-            -nocerts \
-            -out client_key.pem -legacy
-
-      - name: Install Simulacron
-        run: |
-          wget https://github.com/datastax/simulacron/releases/download/0.12.0/simulacron-standalone-0.12.0.jar -O /home/runner/simulacron.jar
+          server-version: ${{ matrix.SERVER_VERSION }}
 
       - name: Set up Node.js
         uses: actions/setup-node@v4
@@ -182,28 +88,6 @@
           node-version: ${{ matrix.NODE_VERSION }}
           cache: "npm"
 
-      - name: Resolve Cassandra latest patch version
-        env:
-          SERVER_VERSION: ${{ matrix.SERVER_VERSION }}
-        run: |
-          PATCH_SERVER_VERSION=$(
-            curl -s https://downloads.apache.org/cassandra/ \
-            | grep -oP '(?<=href=")[0-9]+\.[0-9]+\.[0-9]+(?=)' \
-            | sort -rV \
-            | uniq -w 3 \
-            | grep "^${SERVER_VERSION}\."
-          )
-          echo "Resolved Cassandra ${SERVER_VERSION}.x -> ${PATCH_SERVER_VERSION}"
-          echo "CCM_VERSION=$PATCH_SERVER_VERSION" >> "$GITHUB_ENV"
-
-      - name: Print environment
-        run: printenv | sort
-
-      - name: Pre-download Cassandra distribution
-        run: |
-          ccm create predownload -v $CCM_VERSION
-          ccm remove
-
       - name: Run tests
         run: |
           npm ci