From bf612a81f777b3f8c5d224c2c4732261c1cea41b Mon Sep 17 00:00:00 2001 From: sagi lefler Date: Wed, 3 Jun 2026 16:43:40 -0600 Subject: [PATCH 1/4] ci: harden CI pipeline for external/fork PRs (GTI-846) - Add least-privilege permissions (contents: read) to build.yml - Add concurrency groups to cancel superseded runs in build, early-access, and release workflows - Pin all third-party actions to immutable commit SHAs - Remove styfle/cancel-workflow-action in favour of native concurrency blocks - Use jreleaser/release-action 2.5.0 tag SHA instead of mutable v2 branch ref Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build.yml | 17 ++++++++++++----- .github/workflows/docs.yml | 16 ++++++++-------- .github/workflows/early-access.yml | 26 ++++++++++++++------------ .github/workflows/release.yml | 30 ++++++++++++++++-------------- 4 files changed, 50 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0501a07..10009b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,22 +3,29 @@ name: Build on: pull_request: +permissions: + contents: read + +concurrency: + group: build-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: build: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: 21 distribution: 'zulu' - name: Cache Gradle - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} @@ -26,7 +33,7 @@ jobs: ${{ runner.os }}-gradle- - name: Cache Gradle wrapper - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} @@ -42,7 +49,7 @@ jobs: - name: Upload test reports if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: test-reports path: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cbb7f69..c2e3ea8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,23 +23,23 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: fetch-depth: 0 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: 21 distribution: 'zulu' - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 - name: Cache Gradle dependencies - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} @@ -47,7 +47,7 @@ jobs: ${{ runner.os }}-gradle- - name: Cache Gradle wrapper - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle-wrapper.properties') }} @@ -72,13 +72,13 @@ jobs: run: touch docs/build/site/.nojekyll - name: Setup Pages - uses: actions/configure-pages@v4 + uses: actions/configure-pages@1f0c5cde4bc74cd7e1254d0cb4de8d49e9068c7d # v4 - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: path: 'docs/build/site' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/early-access.yml b/.github/workflows/early-access.yml index 1aa191b..b802ee2 100644 --- a/.github/workflows/early-access.yml +++ b/.github/workflows/early-access.yml @@ -4,29 +4,31 @@ on: push: branches: [ main ] +permissions: + contents: write + +concurrency: + group: early-access-${{ github.ref }} + cancel-in-progress: true + jobs: earlyaccess: name: 'Early Access' runs-on: ubuntu-latest steps: - - name: Cancel previous run - uses: styfle/cancel-workflow-action@0.12.1 - with: - access_token: ${{ secrets.GIT_ACCESS_TOKEN }} - - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: fetch-depth: 0 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: 21 distribution: 'zulu' - name: Cache Gradle - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} @@ -34,7 +36,7 @@ jobs: ${{ runner.os }}-gradle- - name: Cache Gradle wrapper - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} @@ -46,7 +48,7 @@ jobs: - name: Upload test reports if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: test-report path: | @@ -79,7 +81,7 @@ jobs: - name: Release Snapshot if: ${{ steps.vars.outputs.SHOULD_RELEASE_SNAPSHOT == 'true' }} - uses: jreleaser/release-action@v2 + uses: jreleaser/release-action@90ac653bb9c79d11179e65d81499f3f34527dcd5 # 2.5.0 with: arguments: release version: 'latest' @@ -97,7 +99,7 @@ jobs: - name: JReleaser output if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: artifact path: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a953b47..6dfa710 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,29 +8,31 @@ on: required: true type: string +permissions: + contents: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + jobs: release: name: Release runs-on: ubuntu-latest steps: - - name: Cancel previous run - uses: styfle/cancel-workflow-action@0.12.1 - with: - access_token: ${{ secrets.GIT_ACCESS_TOKEN }} - - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: fetch-depth: 0 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: 21 distribution: 'zulu' - name: Cache Gradle - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} @@ -38,7 +40,7 @@ jobs: ${{ runner.os }}-gradle- - name: Cache Gradle wrapper - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} @@ -66,21 +68,21 @@ jobs: - name: Upload test reports if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: test-reports path: | build/reports/tests/aggregate/ - name: Assemble - uses: jreleaser/release-action@v2 + uses: jreleaser/release-action@97b5e2f0e845de2fe1dbbdf451ac6a21233fafff # v2 with: arguments: assemble env: JRELEASER_PROJECT_VERSION: ${{ inputs.version }} - name: Release - uses: jreleaser/release-action@v2 + uses: jreleaser/release-action@97b5e2f0e845de2fe1dbbdf451ac6a21233fafff # v2 with: arguments: full-release env: @@ -97,7 +99,7 @@ jobs: - name: JReleaser output if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: artifact path: | @@ -105,7 +107,7 @@ jobs: out/jreleaser/output.properties - name: Trigger documentation build - uses: peter-evans/repository-dispatch@v2 + uses: peter-evans/repository-dispatch@bf47d102fdb849e755b0b0023ea3e81a44b6f570 # v2 with: token: ${{ secrets.GIT_ACCESS_TOKEN }} event-type: docs-build From feefadc010fb150bde076d8eb4cd2adc82778f42 Mon Sep 17 00:00:00 2001 From: sagi lefler Date: Wed, 3 Jun 2026 16:56:50 -0600 Subject: [PATCH 2/4] ci: fix upload-artifact permissions and jreleaser SHA consistency - Add actions: write to build, early-access, and release workflows so actions/upload-artifact can write artifacts when permissions are explicit - Pin jreleaser/release-action to 2.5.0 tag SHA (90ac653) in release.yml, matching early-access.yml (was still pointing at mutable v2 branch ref) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build.yml | 1 + .github/workflows/early-access.yml | 1 + .github/workflows/release.yml | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 10009b3..cb2083b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,7 @@ on: permissions: contents: read + actions: write # required for actions/upload-artifact concurrency: group: build-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/early-access.yml b/.github/workflows/early-access.yml index b802ee2..7ace733 100644 --- a/.github/workflows/early-access.yml +++ b/.github/workflows/early-access.yml @@ -6,6 +6,7 @@ on: permissions: contents: write + actions: write # required for actions/upload-artifact concurrency: group: early-access-${{ github.ref }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6dfa710..ee6c1a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,7 @@ on: permissions: contents: write + actions: write # required for actions/upload-artifact concurrency: group: release-${{ github.ref }} @@ -75,14 +76,14 @@ jobs: build/reports/tests/aggregate/ - name: Assemble - uses: jreleaser/release-action@97b5e2f0e845de2fe1dbbdf451ac6a21233fafff # v2 + uses: jreleaser/release-action@90ac653bb9c79d11179e65d81499f3f34527dcd5 # 2.5.0 with: arguments: assemble env: JRELEASER_PROJECT_VERSION: ${{ inputs.version }} - name: Release - uses: jreleaser/release-action@97b5e2f0e845de2fe1dbbdf451ac6a21233fafff # v2 + uses: jreleaser/release-action@90ac653bb9c79d11179e65d81499f3f34527dcd5 # 2.5.0 with: arguments: full-release env: From 7fc85a4abef51e444986242ef77f1248163c538d Mon Sep 17 00:00:00 2001 From: sagi lefler Date: Wed, 3 Jun 2026 16:58:49 -0600 Subject: [PATCH 3/4] ci: split monolithic build job into style/compile/test/spotbugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - style: spotlessCheck only — fast (~30s), no Docker, runs independently - compile: assemble only — fast fail gate before heavier jobs - test: :core:test — unit tests (testcontainers/Redis), gated on compile - spotbugs: :core:spotbugsMain — static analysis, runs in parallel with test Mirrors the multi-job structure already used in redis-om-spring. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build.yml | 120 +++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb2083b..02d1407 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,21 +12,53 @@ concurrency: cancel-in-progress: true jobs: - build: - name: Build + + # ── 1. Format check (fast, ~30s, no Docker needed) ────────────────────────── + style: + name: Code Style runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - name: Set up Java + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 + with: + java-version: 21 + distribution: 'zulu' + + - name: Cache Gradle + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Gradle wrapper + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.gradle/wrapper + key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} + restore-keys: ${{ runner.os }}-gradlew- + + - name: Spotless check + run: ./gradlew spotlessCheck -S + + # ── 2. Compile (fast, ~1–2 min, no tests, no Docker) ──────────────────────── + compile: + name: Compile + runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Set up Java - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: 21 distribution: 'zulu' - name: Cache Gradle - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} @@ -34,24 +66,82 @@ jobs: ${{ runner.os }}-gradle- - name: Cache Gradle wrapper - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} restore-keys: ${{ runner.os }}-gradlew- - - name: Code Style Check - run: | - ./gradlew spotlessCheck -S + - name: Compile (no tests) + run: ./gradlew assemble -S + + # ── 3. Unit tests (requires Docker for testcontainers/Redis) ──────────────── + test: + name: Test + runs-on: ubuntu-latest + needs: compile + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Build - run: | - ./gradlew build -S + - name: Set up Java + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 + with: + java-version: 21 + distribution: 'zulu' + + - name: Cache Gradle + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Gradle wrapper + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.gradle/wrapper + key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} + restore-keys: ${{ runner.os }}-gradlew- + + - name: Run tests + run: ./gradlew :core:test -S - name: Upload test reports if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: test-reports - path: | - build/reports/tests/aggregate/ + path: core/build/reports/tests/test/ + + # ── 4. Static analysis (runs in parallel with tests after compile) ─────────── + spotbugs: + name: SpotBugs + runs-on: ubuntu-latest + needs: compile + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Set up Java + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 + with: + java-version: 21 + distribution: 'zulu' + + - name: Cache Gradle + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Gradle wrapper + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.gradle/wrapper + key: ${{ runner.os }}-gradlew-${{ hashFiles('**/gradlew') }} + restore-keys: ${{ runner.os }}-gradlew- + + - name: SpotBugs + run: ./gradlew :core:spotbugsMain -S From e0b57aef6b002b5eebe715cae4f4ead480fbc248 Mon Sep 17 00:00:00 2001 From: sagi lefler Date: Wed, 3 Jun 2026 17:08:08 -0600 Subject: [PATCH 4/4] ci: include VCR demo module tests in CI test job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit langchain4j-vcr and spring-ai-vcr tests use PLAYBACK mode with pre-recorded cassettes — no API key needed, safe to run in CI. rag-multimodal tests are excluded as they require live API keys (OpenAI, LangCache etc). Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02d1407..43b902d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -105,14 +105,17 @@ jobs: restore-keys: ${{ runner.os }}-gradlew- - name: Run tests - run: ./gradlew :core:test -S + run: ./gradlew :core:test :demos:langchain4j-vcr:test :demos:spring-ai-vcr:test -S - name: Upload test reports if: failure() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: test-reports - path: core/build/reports/tests/test/ + path: | + core/build/reports/tests/test/ + demos/langchain4j-vcr/build/reports/tests/test/ + demos/spring-ai-vcr/build/reports/tests/test/ # ── 4. Static analysis (runs in parallel with tests after compile) ─────────── spotbugs: