name: integration-main # Stage 2: is the merged state healthy. # # The question this stage answers is different from stage 1's. Stage 1 asks whether a diff is safe # and blocks a merge; stage 2 asks whether main is healthy and does not — the merge has already # happened. That difference is the point, and it is what lets a control exist without being an # obstacle: a gate here still fails loudly, it just fails after the thing it is reporting on. # # Two kinds of work live here. # # 1. The documentation-drift gates. They used to be `dependsOn` of the root `check`, so a README # sentence about a renamed task failed a compile-and-test run and the fix was to edit a document # before unrelated code could build. src/build.gradle now aggregates them as # `verifyDocumentationContracts` and leaves them out of `check`. That demotion is only half a # change: a gate nothing invokes has not been demoted, it has been deleted. This job is the other # half, and it is the reason the four gates still run at all. # # 2. The lanes that need a machine that is not simultaneously compiling something else — load, # abuse, graceful shutdown, TCP fault injection, resource bounds. They were web-nightly.yml and # httpclient-nightly.yml, two module-shaped files whose only real difference was the cadence they # shared. They now run on every push to main as well as nightly, which is strictly more often # than before. # # What is deliberately NOT here: the web and WebSocket "Advanced capability" nightly lanes that used # to exist as web-advanced-nightly.yml and websocket-advanced-nightly.yml. Both leaves' build files # say it outright — "They also run inside `test`, deliberately ... excluding them from the PR gate to # make this lane look meaningful would mean the PR gate stopped covering a fifth of the leaf" — so # `webAdvancedTest` and `websocketAdvancedTest` select tagged tests that `::test` already runs, # and `::test` runs inside the root `check` on every pull request and every push to main. The # strict lanes themselves survive in release.yml, where their fail-on-nothing-discovered guard is # worth a job. on: push: branches: ["main"] schedule: # 03:00 UTC. Late enough that the day's merges are in, early enough that a failure is triaged # before the next working day starts. - cron: '0 3 * * *' workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false jobs: # verifyReadmeCommands, verifyDocumentedLeafCount, verifyRunbookReferences and # verifyTestSourceSetRegistry, as one task. Named as the aggregate rather than as four steps so # that adding a fifth documentation gate is a build-file edit and not a workflow edit — and so # that the demotion out of `check` has exactly one consumer to point at. documentation-contracts: runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - name: Validate Gradle wrapper id: gradle-wrapper-validation uses: gradle/actions/wrapper-validation@3f131e8634966bd73d06cc69884922b02e6faf92 # gradle/actions@v6 - uses: ./.github/actions/setup-gradle-java - name: Verify the documentation contracts working-directory: src run: ./gradlew verifyDocumentationContracts --no-daemon --stacktrace # Load, abuse and graceful shutdown measure behaviour that degrades gradually rather than breaking # outright — which is exactly the kind of regression a per-PR gate never catches. web-load-abuse-and-shutdown: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - name: Validate Gradle wrapper id: gradle-wrapper-validation uses: gradle/actions/wrapper-validation@3f131e8634966bd73d06cc69884922b02e6faf92 # gradle/actions@v6 - uses: ./.github/actions/setup-gradle-java - name: Run the load, abuse and shutdown lanes on every container working-directory: src run: >- ./gradlew :adapter:inbound:web:test :adapter:inbound:web:webJettyCompatTest :adapter:inbound:web:webFluxContractTest --no-daemon --stacktrace - name: Publish the test reports if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 with: name: web-integration-reports path: src/adapter/inbound/web/build/reports/tests/ if-no-files-found: warn # Needs a container runtime and real time (design §29). Separated from the per-PR gate rather than # made optional inside it: a lane that cannot run here fails, it does not skip. httpclient-fault-injection: runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - name: Validate Gradle wrapper id: gradle-wrapper-validation uses: gradle/actions/wrapper-validation@3f131e8634966bd73d06cc69884922b02e6faf92 # gradle/actions@v6 - uses: ./.github/actions/setup-gradle-java - name: Inject TCP faults against a real upstream working-directory: src run: >- ./gradlew :adapter:outbound:httpclient:httpClientFailureInjectionTest --no-daemon --stacktrace httpclient-performance: runs-on: ubuntu-latest timeout-minutes: 45 env: # A project property rather than a command-line flag, so the run command stays a plain, # verifiable task invocation while the machine-dependent bounds are still asserted. GRADLE_OPTS: -Dorg.gradle.project.performance.assertions.enabled=true steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - name: Validate Gradle wrapper id: gradle-wrapper-validation uses: gradle/actions/wrapper-validation@3f131e8634966bd73d06cc69884922b02e6faf92 # gradle/actions@v6 - uses: ./.github/actions/setup-gradle-java - name: Certify pool, streaming, retry, and rotation bounds working-directory: src run: >- ./gradlew :adapter:outbound:httpclient:httpClientPerformanceTest --no-daemon --stacktrace httpclient-http3-experimental: runs-on: ubuntu-latest timeout-minutes: 30 # Experimental by design (D-08): the result is reported, never used to block a merge. Registered # in .github/ci-gate-matrix.yml as release_blocking: false so that "this job cannot fail the # build" is written down rather than inferred from a field two hundred lines into a workflow. continue-on-error: true steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - name: Validate Gradle wrapper id: gradle-wrapper-validation uses: gradle/actions/wrapper-validation@3f131e8634966bd73d06cc69884922b02e6faf92 # gradle/actions@v6 - uses: ./.github/actions/setup-gradle-java - name: Exercise the experimental HTTP/3 opt-in working-directory: src run: >- ./gradlew :adapter:outbound:httpclient:test -Phttp3.tests.enabled=true --no-daemon --stacktrace