name: Docker Compose Cucumber tests # Reusable workflow called from build.yml when project / docker / testing # sources change. Boots the docker-compose stack and runs the cucumber # scenarios in testing/cucumber. on: workflow_call: inputs: docker-base-changed: description: "Whether the docker base image changed (forwarded from files-changed)." required: false type: string default: "false" permissions: contents: read jobs: docker-compose-tests: environment: name: ci-unsigned deployment: false runs-on: ubuntu-latest permissions: actions: write contents: read checks: write steps: - name: Harden Runner uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 with: egress-policy: audit - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Restore cache Gradle User Home uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: gradle-v1-${{ runner.os }}-${{ runner.arch }}-jdk-25-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/libs.versions.toml', 'buildSrc/**', 'settings.gradle', 'build.gradle', 'app/**/build.gradle', 'gradle/**/*.gradle') }} - name: Set up JDK 25 uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 with: java-version: "25" distribution: "temurin" # When the PR changes the base image, test.sh builds it locally # (stirling-pdf-base:local) into the daemon image store. A buildx # container builder can't see that store, so skip it here and let # `docker buildx build` fall back to the default docker driver, which # resolves the local base. The gha cache backend is also skipped (its # runtime token isn't exposed) since the docker driver can't use it. - name: Set up Docker Buildx if: inputs.docker-base-changed != 'true' uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 # Expose ACTIONS_RUNTIME_TOKEN / ACTIONS_RESULTS_URL for docker buildx type=gha cache backend. - name: Expose GitHub runtime for Buildx cache if: inputs.docker-base-changed != 'true' uses: crazy-max/ghaction-github-runtime@04d248b84655b509d8c44dc1d6f990c879747487 # v4.0.0 - name: Install Docker Compose run: | sudo curl -SL "https://github.com/docker/compose/releases/download/v5.4.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose - name: Install uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true cache-dependency-glob: | engine/pyproject.toml engine/uv.lock - name: Install Cucumber and coverage dependencies run: | uv sync --project engine --locked --group cucumber --group tools - name: Extract JaCoCo agent for cucumber coverage # Stages build/jacoco/jacocoagent.jar where the coverage override # file bind-mounts it into the cucumber container. The agent jar # never goes into the published image - this is host-only. run: ./gradlew copyJacocoAgent -PnoSpotless - name: Run Docker Compose Tests run: | chmod +x ./testing/test_webpages.sh chmod +x ./testing/test.sh chmod +x ./testing/test_disabledEndpoints.sh ./testing/test.sh env: MAVEN_USER: ${{ secrets.MAVEN_USER }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }} DOCKER_BASE_CHANGED: ${{ inputs.docker-base-changed }} # Tells test.sh to layer testing/compose/docker-compose-coverage.override.yml # over the cucumber compose so the container starts with the # JaCoCo agent attached via JAVA_CUSTOM_OPTS. STIRLING_PDF_TEST_COVERAGE: "1" - name: Generate cucumber JaCoCo report # `if: always()` so a behave failure still produces partial # coverage from whatever endpoints did run. The exec file only # exists when the container shut down cleanly - guard so the step # is silent on the (rare) crash path. if: always() id: cucumber-coverage run: | if [ -s testing/cucumber-coverage/cucumber.exec ]; then ./gradlew jacocoReportFromExec \ -PexecFile=testing/cucumber-coverage/cucumber.exec \ -PreportDir=build/reports/jacoco/cucumber \ -PnoSpotless echo "report=true" >> "$GITHUB_OUTPUT" else echo "::warning::No cucumber .exec at testing/cucumber-coverage/cucumber.exec (container may have crashed before flushing)" echo "report=false" >> "$GITHUB_OUTPUT" fi - name: Cucumber coverage step summary if: always() && steps.cucumber-coverage.outputs.report == 'true' run: | uv run --project engine --locked --group tools python scripts/coverage-summary.py \ --title "Cucumber (docker) JaCoCo coverage" \ --jacoco "cucumber=build/reports/jacoco/cucumber/jacocoTestReport.xml" \ --github-step-summary - name: Upload cucumber JaCoCo report if: always() && steps.cucumber-coverage.outputs.report == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: jacoco-cucumber-${{ github.run_id }} path: build/reports/jacoco/cucumber/ retention-days: 7 - name: Upload raw cucumber .exec for aggregate merge # Picked up by the coverage-aggregate workflow via the # `jacoco-exec-*` artifact name pattern. if: always() && steps.cucumber-coverage.outputs.report == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: jacoco-exec-cucumber path: testing/cucumber-coverage/cucumber.exec retention-days: 7 if-no-files-found: warn - name: Upload Cucumber Report if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: cucumber-report path: testing/cucumber/report.html retention-days: 7 if-no-files-found: warn - name: Upload Test Reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: docker-compose-test-reports path: testing/reports/ retention-days: 7 if-no-files-found: warn - name: Cucumber Test Report if: always() uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0 with: name: Cucumber Tests path: testing/cucumber/junit/*.xml reporter: java-junit fail-on-error: false