From 39793466d42b208c382e4881acf5a9a6db245b0a Mon Sep 17 00:00:00 2001 From: Pascal Zimmermann Date: Sat, 3 Oct 2026 21:04:26 +0200 Subject: [PATCH] feat: Add GitHub workflow for integration tests on multiple Java versions --- .github/scripts/resolve-capi-version.sh | 48 +++++++++ .github/workflows/ci-java.yml | 6 ++ .github/workflows/integration-test.yml | 125 ++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100755 .github/scripts/resolve-capi-version.sh create mode 100644 .github/workflows/integration-test.yml diff --git a/.github/scripts/resolve-capi-version.sh b/.github/scripts/resolve-capi-version.sh new file mode 100755 index 0000000000..5ff2dfec33 --- /dev/null +++ b/.github/scripts/resolve-capi-version.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Prints the newest capi-release version whose CC API (v2) version equals SUPPORTED_API_VERSION +# from CloudFoundryClient.java. Needs only git and curl, no GitHub API and so no token. +# +# A capi-release tag pins cloud_controller_ng as a submodule, and that repo records its v2 API +# version in config/version_v2. The release notes aren't reliable for this. The v2 version only +# grows with the tags, so a binary search over the sorted tags is enough. +set -euo pipefail + +source_file="$(dirname "$0")/../../cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java" +target="$(sed -n 's/.*String SUPPORTED_API_VERSION = "\([0-9.]*\)";.*/\1/p' "$source_file")" +[ -n "$target" ] || { echo "SUPPORTED_API_VERSION not found in $source_file" >&2; exit 1; } + +workdir="$(mktemp -d)" +trap 'rm -rf "$workdir"' EXIT +# commits and trees only, which is all it takes to read the submodule commit of a tag +git clone --quiet --bare --filter=blob:none https://github.com/cloudfoundry/capi-release.git "$workdir/capi-release" + +mapfile -t tags < <(git -C "$workdir/capi-release" tag -l | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V) + +v2_version() { + local sha + sha="$(git -C "$workdir/capi-release" ls-tree "$1" src/cloud_controller_ng | awk '{print $3}')" + curl -fsSL --retry 3 "https://raw.githubusercontent.com/cloudfoundry/cloud_controller_ng/$sha/config/version_v2" | tr -d '[:space:]' +} + +# highest index whose v2 version is <= target +lo=0 +hi=$((${#tags[@]} - 1)) +found=-1 +while [ "$lo" -le "$hi" ]; do + mid=$(((lo + hi) / 2)) + v="$(v2_version "${tags[$mid]}")" + if [ "$(printf '%s\n%s\n' "$v" "$target" | sort -V | tail -1)" = "$target" ]; then + found=$mid + lo=$((mid + 1)) + else + hi=$((mid - 1)) + fi +done + +if [ "$found" -lt 0 ] || [ "$(v2_version "${tags[$found]}")" != "$target" ]; then + echo "No capi-release found for CC API version $target" >&2 + exit 1 +fi + +echo "CC API $target -> capi-release ${tags[$found]}" >&2 +echo "${tags[$found]}" diff --git a/.github/workflows/ci-java.yml b/.github/workflows/ci-java.yml index 5ccde5ca07..7083e0093c 100644 --- a/.github/workflows/ci-java.yml +++ b/.github/workflows/ci-java.yml @@ -39,3 +39,9 @@ jobs: name: Check style with Spotless run: ./mvnw spotless:check -Pintegration-test + integration-test: + needs: build + uses: ./.github/workflows/integration-test.yml + with: + java-versions: '[21]' + diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 0000000000..b703c62b45 --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,125 @@ +name: Integration Tests + +on: + workflow_dispatch: + inputs: + java-versions: + description: 'JSON array of Java versions to test' + type: string + required: false + default: '[8, 11, 17, 21]' + preview: + description: 'Deploy the latest cf-deployment versions instead of the default ones' + type: boolean + required: false + default: false + schedule: + - cron: '0 3 * * 1-5' + workflow_call: + inputs: + java-versions: + description: 'JSON array of Java versions to test' + type: string + required: false + default: '[8, 11, 17, 21]' + preview: + description: 'Deploy the latest cf-deployment versions instead of the default ones' + type: boolean + required: false + default: false + +permissions: + contents: read + +# A new push supersedes the running test. The group name differs from the caller's workflow name on +# purpose: a called workflow shares github.workflow with its caller, which would cancel the caller. +concurrency: + group: integration-test-${{ github.event_name == 'schedule' && 'schedule' || github.ref }} + cancel-in-progress: true + +env: + CF_API_HOST: api.cf.127-0-0-1.nip.io + CF_UAA_HOST: uaa.cf.127-0-0-1.nip.io + +jobs: + integration-test: + if: ${{ github.event_name != 'schedule' || github.repository == 'cloudfoundry/cf-java-client' }} + runs-on: ubuntu-latest + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + java: ${{ fromJSON(inputs.java-versions || (github.event_name == 'schedule' && '[21]' || '[8, 11, 17, 21]')) }} + name: Java ${{ matrix.java }} integration test${{ (inputs.preview || github.event_name == 'schedule') && ' (preview)' || '' }} + steps: + - uses: actions/checkout@v7 + with: + submodules: recursive + # Validation runs deploy the capi-release that matches SUPPORTED_API_VERSION. Preview runs + # (scheduled, or started by hand with `preview`) keep the latest versions. + - name: Resolve CAPI version + id: capi + if: ${{ !(inputs.preview || github.event_name == 'schedule') }} + run: | + # separate assignment, so that a failing resolver fails the step + version="$(.github/scripts/resolve-capi-version.sh)" + echo "version=$version" >> "$GITHUB_OUTPUT" + # All components share one Postgres, which runs out of its default 100 connections during a full + # test run. Written first, the CAPI override below appends to the same file. + - name: Write PostgreSQL values override + run: | + cat > "$RUNNER_TEMP/cf-values.yaml" <> "$RUNNER_TEMP/cf-values.yaml" <