diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d46f76eb..b0736f82 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,8 +4,8 @@ ## Verification -- [ ] I ran the focused test for each changed leaf. -- [ ] I ran `cd src && ./gradlew check verifyPublicPathSnapshot verifyDependencyLocks`. +- [ ] I ran focused `::check` tasks for the modules I changed. +- [ ] I ran `cd src && ./gradlew architectureCheck verifyPublicPathSnapshot verifyDependencyLocks` when the change touched repository structure, dependencies, or public paths. - [ ] I did not add an unregistered production module dependency. - [ ] Dependency changes include refreshed `gradle.lockfile` files and a strict-lock verification. - [ ] Trivy suppressions include an owner-reviewed reason and an expiry within 90 days. diff --git a/.github/workflows/_reusable-gradle.yml b/.github/workflows/_reusable-gradle.yml index 55377f2f..8392b08f 100644 --- a/.github/workflows/_reusable-gradle.yml +++ b/.github/workflows/_reusable-gradle.yml @@ -23,7 +23,7 @@ on: description: Flags appended after the task list. required: false type: string - default: "--no-daemon --stacktrace" + default: "--stacktrace" working-directory: description: Directory the wrapper is invoked from. required: false diff --git a/.github/workflows/ci-quality-gates.yml b/.github/workflows/ci-quality-gates.yml index a92582a3..40a90da5 100644 --- a/.github/workflows/ci-quality-gates.yml +++ b/.github/workflows/ci-quality-gates.yml @@ -45,31 +45,26 @@ jobs: - uses: ./.github/actions/setup-gradle-java # `ci`, not `check`. A leaf's `check` is that leaf's — compile, its tests, Spotless, Checkstyle # and Error Prone — and the repository-wide gates are named tasks of their own: - # ci = every leaf check + architectureCheck + qualityCheck + configContractCheck + # ci = every leaf check + architectureCheck + qualityCheck + configContractCheck + qualificationCheck # so CI runs strictly more than it used to while `./gradlew :domain-core:check` runs strictly # less. - name: Run the pull-request gate working-directory: src - run: ./gradlew ci verifyPublicPathSnapshot verifyDependencyLocks --warning-mode=fail --no-daemon --stacktrace + run: ./gradlew :ci :verifyPublicPathSnapshot :verifyDependencyLocks --warning-mode=fail --stacktrace # build-logic is an included build: its own suite is not reachable from the root project's # `check`, so the convention plugins every leaf applies would otherwise ship untested. - name: Test the build-logic convention plugins working-directory: src - run: ./gradlew -p build-logic test --no-daemon --stacktrace + run: ./gradlew -p build-logic test --stacktrace # Named as its own step because nothing else runs it: `check` does not depend on # graphqlStableTest, so the lane's required-class guard — the check that its module-boundary # suite has not silently stopped being discovered — would protect nothing in CI. - name: Qualify the GraphQL Stable lane working-directory: src - run: ./gradlew :adapter:inbound:graphql:graphqlStableTest --no-daemon --stacktrace + run: ./gradlew :adapter:inbound:graphql:graphqlStableTest --stacktrace - name: Qualify opt-in inbound transports without skips working-directory: src - run: ./gradlew conditionalTransportQualification --no-daemon --stacktrace - - sample-off: - uses: ./.github/workflows/_reusable-gradle.yml - with: - tasks: ":app-bootstrap:sampleOffTest verifyCleanArchitectureDependencies" + run: ./gradlew :conditionalTransportQualification --stacktrace redis-sdk: # Milestone A of the Redis wrapper/typed API plan: policy catalog, typed API parity, permit @@ -82,12 +77,43 @@ jobs: # `configContractCheck`, which the quality-gates job covers. uses: ./.github/workflows/_reusable-gradle.yml with: - tasks: >- - :shared-contract:edgeRateLimitContractTest - :adapter:outbound:cache-redis:check - :app-bootstrap:verifyEnvKeys - verifyCleanArchitectureDependencies - verifyPublicPathSnapshot + tasks: ":shared-contract:edgeRateLimitContractTest" + + optional-platforms: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 + - uses: ./.github/actions/setup-gradle-java + - name: Verify the optional gRPC platform build + working-directory: src + run: ./gradlew -p optional-platforms ci --stacktrace + + configuration-cache: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 + - uses: ./.github/actions/setup-gradle-java + - name: Store configuration cache for the everyday core build + working-directory: src + run: >- + ./gradlew + :domain-core:check + :application-core:check + --configuration-cache + --configuration-cache-problems=fail + --stacktrace + - name: Require configuration-cache reuse + working-directory: src + shell: bash + run: | + set -euo pipefail + output="$({ ./gradlew :domain-core:check :application-core:check \ + --configuration-cache --configuration-cache-problems=fail --stacktrace; } 2>&1)" + printf '%s\n' "${output}" + grep -Fq 'Reusing configuration cache.' <<<"${output}" || { + echo '::error::Gradle did not reuse the configuration cache on the second identical build.' + exit 1 + } jpa-candidate-evidence: runs-on: ubuntu-latest @@ -100,7 +126,7 @@ jobs: run: >- ./gradlew :adapter:outbound:persistence-jpa:verifyJpaCandidateEvidence - --no-daemon + --stacktrace - name: Retain content-addressed JPA candidate manifests uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7.0.1 @@ -116,85 +142,40 @@ jobs: quarantine: uses: ./.github/workflows/_reusable-gradle.yml with: - tasks: quarantineTest - gradle-args: "--no-daemon" + tasks: ":quarantineTest" + gradle-args: "--stacktrace" continue-on-error: true release-gate: needs: - quality-gates - - sample-off - redis-sdk - jpa-candidate-evidence + - optional-platforms + - configuration-cache if: always() runs-on: ubuntu-latest permissions: contents: read - checks: read steps: - name: Require every current blocking job to succeed env: QUALITY_RESULT: ${{ needs.quality-gates.result }} - SAMPLE_OFF_RESULT: ${{ needs.sample-off.result }} REDIS_RESULT: ${{ needs.redis-sdk.result }} JPA_CANDIDATE_RESULT: ${{ needs.jpa-candidate-evidence.result }} + OPTIONAL_PLATFORMS_RESULT: ${{ needs.optional-platforms.result }} + CONFIGURATION_CACHE_RESULT: ${{ needs.configuration-cache.result }} run: | set -euo pipefail for result in \ "${QUALITY_RESULT}" \ - "${SAMPLE_OFF_RESULT}" \ "${REDIS_RESULT}" \ - "${JPA_CANDIDATE_RESULT}"; do + "${JPA_CANDIDATE_RESULT}" \ + "${OPTIONAL_PLATFORMS_RESULT}" \ + "${CONFIGURATION_CACHE_RESULT}"; do if [[ "${result}" != "success" ]]; then echo "::error::release-gate: required job result was ${result}" exit 1 fi done echo "release-gate: all current blocking quality jobs succeeded." - - # `needs` cannot reach another workflow, so a blocking check in another file has to be required - # by result. dependency-vulnerability.yml answers the same pull_request and push-to-main - # triggers as this workflow and trivy-fs carries no `if:` guard, so its check run always exists - # for this SHA — which is what makes it requirable rather than a matter of scheduling luck. - # Only `success` passes: a skipped or cancelled security scan is not a scan. - # - # The release-tag and path-filtered workflows (release.yml, jpa-release.yml, - # fileserver-certification.yml, object-storage-qualification.yml, messaging-certification.yml) - # run on triggers this job does not share, so they cannot be required here without changing - # when they run. That is a stated gap, not a hidden one. - - name: Require the cross-workflow release-blocking checks to have succeeded - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - COMMIT_SHA: ${{ github.sha }} - REQUIRED_CHECKS: trivy-fs - run: | - set -euo pipefail - deadline=$(( $(date +%s) + 1800 )) - for required in ${REQUIRED_CHECKS}; do - while :; do - result="$( - gh api \ - "repos/${GH_REPO}/commits/${COMMIT_SHA}/check-runs?check_name=${required}&per_page=100" \ - --jq '[.check_runs[]] | sort_by(.started_at) | last - | if . == null then "absent none" - else "\(.status) \(.conclusion // "none")" end' - )" - check_status="${result%% *}" - check_conclusion="${result##* }" - if [[ "${check_status}" == "completed" ]]; then - break - fi - if (( $(date +%s) >= deadline )); then - echo "::error::release-gate: release-blocking check ${required} reported no result for ${COMMIT_SHA} (status ${check_status})" - exit 1 - fi - echo "release-gate: waiting for ${required} (status ${check_status})" - sleep 30 - done - if [[ "${check_conclusion}" != "success" ]]; then - echo "::error::release-gate: release-blocking check ${required} concluded ${check_conclusion}" - exit 1 - fi - echo "release-gate: ${required} concluded success." - done diff --git a/.github/workflows/fileserver-certification.yml b/.github/workflows/fileserver-certification.yml index 5ba686f3..84e14968 100644 --- a/.github/workflows/fileserver-certification.yml +++ b/.github/workflows/fileserver-certification.yml @@ -47,8 +47,8 @@ jobs: working-directory: src run: >- ./gradlew - verifyCleanArchitectureDependencies - --no-daemon + :verifyCleanArchitectureDependencies + --stacktrace - name: Run the complete fileserver suite across every leaf working-directory: src @@ -57,7 +57,7 @@ jobs: :application-core:check :adapter:inbound:web:check :adapter:outbound:fileserver:check - --no-daemon + --stacktrace fileserver-documentation-gate: @@ -71,7 +71,7 @@ jobs: run: >- ./gradlew :app-bootstrap:test --tests '*FileserverDocumentationCoverageTest' - --no-daemon + --stacktrace fileserver-pvc-certification: @@ -118,5 +118,5 @@ jobs: run: >- ./gradlew :application-core:test --tests '*FileserverObservabilityTest' - --no-daemon + --stacktrace diff --git a/.github/workflows/fileserver-nightly.yml b/.github/workflows/fileserver-nightly.yml index 98cbf71b..be123f32 100644 --- a/.github/workflows/fileserver-nightly.yml +++ b/.github/workflows/fileserver-nightly.yml @@ -32,7 +32,7 @@ jobs: run: >- ./gradlew :adapter:outbound:fileserver:test --tests '*NfsAmbiguityIntegrationTest' - --no-daemon + --stacktrace - name: Tear down the NFS environment if: always() @@ -51,7 +51,7 @@ jobs: :adapter:outbound:fileserver:test --tests '*CrashRecoveryMatrixTest' :application-core:test --tests '*FileReconciliationServiceTest' --rerun-tasks - --no-daemon + --stacktrace fileserver-large-file-performance: @@ -69,7 +69,7 @@ jobs: :adapter:outbound:fileserver:test --tests '*LargeFileBoundedMemoryTest' :adapter:outbound:fileserver:test --tests '*LocalAppendMemoryTest' --rerun-tasks - --no-daemon + --stacktrace fileserver-multi-instance-lease: @@ -84,5 +84,5 @@ jobs: ./gradlew :application-core:test --tests '*MultiInstanceWriterLeaseTest' --rerun-tasks - --no-daemon + --stacktrace diff --git a/.github/workflows/fileserver-pr.yml b/.github/workflows/fileserver-pr.yml index ffc247e7..2181300b 100644 --- a/.github/workflows/fileserver-pr.yml +++ b/.github/workflows/fileserver-pr.yml @@ -51,7 +51,7 @@ jobs: ./gradlew :application-core:test :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*' --tests '*Fileserver*' - --no-daemon + --stacktrace fileserver-local-ext4-contract: @@ -65,7 +65,7 @@ jobs: run: >- ./gradlew :adapter:outbound:fileserver:test - --no-daemon + --stacktrace fileserver-http-contract: @@ -79,7 +79,7 @@ jobs: run: >- ./gradlew :adapter:inbound:web:test - --no-daemon + --stacktrace fileserver-security-suite: @@ -94,7 +94,7 @@ jobs: ./gradlew :adapter:inbound:web:test --tests '*FileserverHardeningContractTest' :adapter:outbound:fileserver:test --tests '*PhysicalPathResolverTest' - --no-daemon + --stacktrace fileserver-bounded-memory: @@ -109,5 +109,5 @@ jobs: ./gradlew :adapter:outbound:fileserver:test --tests '*LargeFileBoundedMemoryTest' :adapter:inbound:web:test --tests '*DataBufferReleaseTest' - --no-daemon + --stacktrace diff --git a/.github/workflows/integration-main.yml b/.github/workflows/integration-main.yml index e12af1eb..747005f8 100644 --- a/.github/workflows/integration-main.yml +++ b/.github/workflows/integration-main.yml @@ -73,7 +73,7 @@ jobs: :adapter:inbound:web:test :adapter:inbound:web:webJettyCompatTest :adapter:inbound:web:webFluxContractTest - --no-daemon + --stacktrace - name: Publish the test reports if: always() @@ -96,7 +96,7 @@ jobs: run: >- ./gradlew :adapter:outbound:httpclient:httpClientFailureInjectionTest - --no-daemon + --stacktrace httpclient-performance: @@ -114,7 +114,7 @@ jobs: run: >- ./gradlew :adapter:outbound:httpclient:httpClientPerformanceTest - --no-daemon + --stacktrace httpclient-http3-experimental: @@ -133,7 +133,7 @@ jobs: ./gradlew :adapter:outbound:httpclient:test -Phttp3.tests.enabled=true - --no-daemon + --stacktrace # The six Docker-backed MongoDB lanes. Until now they ran in no workflow at all: the leaf excludes @@ -161,22 +161,18 @@ jobs: - uses: ./.github/actions/setup-gradle-java - name: Single-node replica set contract lane working-directory: src - run: ./gradlew :adapter:outbound:persistence-mongo:mongoReplicaSetTest --no-daemon --stacktrace - - name: Three-node failover lane + run: ./gradlew :adapter:outbound:persistence-mongo:mongoReplicaSetTest --stacktrace + - name: Run MongoDB integration lanes working-directory: src - run: ./gradlew :adapter:outbound:persistence-mongo:mongoFailoverTest --no-daemon --stacktrace - - name: Migration and backfill restart lane - working-directory: src - run: ./gradlew :adapter:outbound:persistence-mongo:mongoMigrationTest --no-daemon --stacktrace - - name: MongoDB 7.0 compatibility lane - working-directory: src - run: ./gradlew :adapter:outbound:persistence-mongo:mongoCompatibilityTest --no-daemon --stacktrace - - name: Credential, TLS and redaction lane - working-directory: src - run: ./gradlew :adapter:outbound:persistence-mongo:mongoSecurityIntegrationTest --no-daemon --stacktrace - - name: Resource-bound certification lane - working-directory: src - run: ./gradlew :adapter:outbound:persistence-mongo:mongoPerformanceTest --no-daemon --stacktrace + run: >- + ./gradlew + :adapter:outbound:persistence-mongo:mongoReplicaSetTest + :adapter:outbound:persistence-mongo:mongoFailoverTest + :adapter:outbound:persistence-mongo:mongoMigrationTest + :adapter:outbound:persistence-mongo:mongoCompatibilityTest + :adapter:outbound:persistence-mongo:mongoSecurityIntegrationTest + :adapter:outbound:persistence-mongo:mongoPerformanceTest + --stacktrace - name: Publish the MongoDB lane reports if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 @@ -186,8 +182,8 @@ jobs: if-no-files-found: warn # The messaging contract evidence DAG. `verifyMessagingContracts` is the root of a chain that ran - # nowhere: it depends on five qualification tasks (application-core, shared-contract, - # sample-portfolio and two in adapter:outbound:messaging), each of which depends on + # nowhere: it depends on four production qualification tasks (application-core, + # shared-contract and two in adapter:outbound:messaging), each of which depends on # `prepareMessagingContractEvidence`; it is finalizedBy # `validateMessagingContractsEvidenceManifestSchema`; and it depends on # `validateMessagingJsonSchemaV1EvidenceManifestSchema`, which depends on @@ -199,7 +195,7 @@ jobs: # — a manifest that claims a qualification nobody executed is exactly the failure they exist to # catch, and until now nothing executed them either. # - # Stage 2 rather than stage 1: no containers, but it runs five qualification suites across four + # Stage 2 rather than stage 1: no containers, but it runs four qualification suites across three # leaves plus two JavaExec validators, and the tests it re-runs are already inside the PR gate's # `check`. What this job adds is the evidence manifest, which is a main-branch artifact. messaging-contract-evidence: @@ -210,7 +206,7 @@ jobs: - uses: ./.github/actions/setup-gradle-java - name: Qualify the messaging contract, catalog, binding and schema evidence working-directory: src - run: ./gradlew verifyMessagingContracts --no-daemon --stacktrace + run: ./gradlew :verifyMessagingContracts --stacktrace - name: Publish the messaging evidence manifest if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 @@ -233,4 +229,4 @@ jobs: - uses: ./.github/actions/setup-gradle-java - name: Run the real-PostgreSQL integration contracts working-directory: src - run: ./gradlew :app-bootstrap:integrationTest --no-daemon --stacktrace + run: ./gradlew :app-bootstrap:integrationTest --stacktrace diff --git a/.github/workflows/jpa-next-hibernate8.yml b/.github/workflows/jpa-next-hibernate8.yml deleted file mode 100644 index 6a8744e5..00000000 --- a/.github/workflows/jpa-next-hibernate8.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: jpa-next-hibernate8 - -# Hibernate ORM 8 compatibility lane (experimental plan Task 8). -# -# Re-runs the contracts most likely to move between provider majors: collection fetch pagination, -# StatementInspector, Statistics, JSONB, batch, and StatelessSession. Differences are recorded, not -# accommodated — weakening the 7.x gate to make this lane green would delete the evidence that 7.x -# behaves as documented. - -on: - workflow_dispatch: - schedule: - - cron: '0 5 * * 1' - -permissions: - contents: read - -jobs: - hibernate8-compatibility: - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - - uses: ./.github/actions/setup-gradle-java - - name: Report Hibernate ORM 8 compatibility - id: compatibility-probe - working-directory: src - run: >- - ./gradlew - :adapter:outbound:persistence-jpa:test --tests '*HibernateCompatibilityPolicyTest' - --no-daemon - --stacktrace - - name: Record what this lane did and did not execute - if: always() - env: - PROBE_OUTCOME: ${{ steps.compatibility-probe.outcome }} - run: | - mkdir -p compatibility-evidence - { - echo "target=Hibernate 8" - echo "target-coordinate=org.hibernate.orm:hibernate-core:8.x" - echo "status=NOT_EXECUTABLE" - echo "probe-result=${PROBE_OUTCOME}" - echo "reason=Hibernate 8 is not resolvable from this build, so nothing has been compiled or run against it" - echo "what-ran=the current runtime's own policy and lane-definition tests" - echo "sha=${{ github.sha }}" - } > compatibility-evidence/status.properties - echo "::notice::Hibernate 8 compatibility is NOT_EXECUTABLE: Hibernate 8 is not resolvable from this build, so nothing has been compiled or run against it" - - name: Upload the compatibility status - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 - with: - name: compatibility-status-hibernate-8 - path: compatibility-evidence/status.properties - retention-days: 30 - if-no-files-found: error diff --git a/.github/workflows/jpa-next-jpa4.yml b/.github/workflows/jpa-next-jpa4.yml deleted file mode 100644 index c69b0333..00000000 --- a/.github/workflows/jpa-next-jpa4.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: jpa-next-jpa4 - -# Jakarta Persistence 4.0 compatibility lane (experimental plan Task 7). -# -# Non-blocking by design: it reports whether the Stable public API still compiles and whether the -# selected mapping contracts still hold on JPA 4. It publishes nothing, and a red result here never -# changes a Stable contract — the 3.2 gate keeps asserting what 3.2 must do, because that is what -# deployments run. - -on: - workflow_dispatch: - schedule: - - cron: '0 4 * * 1' - -permissions: - contents: read - -jobs: - jpa4-compatibility: - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - - uses: ./.github/actions/setup-gradle-java - - name: Report Jakarta Persistence 4.0 compatibility - id: compatibility-probe - working-directory: src - run: >- - ./gradlew - :adapter:outbound:persistence-jpa:test --tests '*CompatibilityLaneDefinitionTest' - --no-daemon - --stacktrace - - name: Record what this lane did and did not execute - if: always() - env: - PROBE_OUTCOME: ${{ steps.compatibility-probe.outcome }} - run: | - mkdir -p compatibility-evidence - { - echo "target=Jakarta Persistence 4" - echo "target-coordinate=jakarta.persistence:jakarta.persistence-api:4.x" - echo "status=NOT_EXECUTABLE" - echo "probe-result=${PROBE_OUTCOME}" - echo "reason=the JPA 4 API is not on any configuration this build resolves, so nothing has been compiled against it" - echo "what-ran=the current runtime's own policy and lane-definition tests" - echo "sha=${{ github.sha }}" - } > compatibility-evidence/status.properties - echo "::notice::Jakarta Persistence 4 compatibility is NOT_EXECUTABLE: the JPA 4 API is not on any configuration this build resolves, so nothing has been compiled against it" - - name: Upload the compatibility status - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 - with: - name: compatibility-status-jakarta-persistence-4 - path: compatibility-evidence/status.properties - retention-days: 30 - if-no-files-found: error diff --git a/.github/workflows/jpa-next-postgresql19.yml b/.github/workflows/jpa-next-postgresql19.yml deleted file mode 100644 index 54900c5e..00000000 --- a/.github/workflows/jpa-next-postgresql19.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: jpa-next-postgresql19 - -# PostgreSQL 19 compatibility lane (experimental plan Task 9). -# -# This lane is NOT_EXECUTABLE against its target. -# -# It runs the current runtime's policy and lane-definition tests; it does not resolve the target -# dependency or start a container of the target version. A green run therefore says "the target is -# absent from this build", which is not the same claim as "we are compatible with the target" — and -# the workflow's name reads as the second one. The status artifact says which it is. -# -# Promotion needs evidence, not availability. Two supported patch runs with no unresolved semantic -# regression, plus a reviewed ADR, before the Stable support matrix changes — which is what -# ExperimentalPromotionGate encodes. - -on: - workflow_dispatch: - schedule: - - cron: '0 6 * * 1' - -permissions: - contents: read - -jobs: - postgresql19-compatibility: - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - - uses: ./.github/actions/setup-gradle-java - - name: Report PostgreSQL 19 compatibility - id: compatibility-probe - working-directory: src - run: >- - ./gradlew - :adapter:outbound:persistence-jpa:test --tests '*ExperimentalPromotionGateTest' - --no-daemon - --stacktrace - - name: Record what this lane did and did not execute - if: always() - env: - PROBE_OUTCOME: ${{ steps.compatibility-probe.outcome }} - run: | - mkdir -p compatibility-evidence - { - echo "target=PostgreSQL 19" - echo "target-coordinate=postgres:19-alpine" - echo "status=NOT_EXECUTABLE" - echo "probe-result=${PROBE_OUTCOME}" - echo "reason=no PostgreSQL 19 image is published yet, so no container of that major has ever been started by this lane" - echo "what-ran=the current runtime's own policy and lane-definition tests" - echo "sha=${{ github.sha }}" - } > compatibility-evidence/status.properties - echo "::notice::PostgreSQL 19 compatibility is NOT_EXECUTABLE: no PostgreSQL 19 image is published yet, so no container of that major has ever been started by this lane" - - name: Upload the compatibility status - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 - with: - name: compatibility-status-postgresql-19 - path: compatibility-evidence/status.properties - retention-days: 30 - if-no-files-found: error diff --git a/.github/workflows/jpa-next.yml b/.github/workflows/jpa-next.yml new file mode 100644 index 00000000..b7fca39c --- /dev/null +++ b/.github/workflows/jpa-next.yml @@ -0,0 +1,75 @@ +name: jpa-next + +# Advisory compatibility probes for future JPA/Hibernate/PostgreSQL majors. These targets are not +# resolved by the current build, so the artifact records NOT_EXECUTABLE rather than implying that a +# green policy test is compatibility evidence. + +on: + workflow_dispatch: + schedule: + - cron: '0 5 * * 1' + +permissions: + contents: read + +jobs: + compatibility: + strategy: + fail-fast: false + matrix: + include: + - id: jakarta-persistence-4 + target: Jakarta Persistence 4 + coordinate: jakarta.persistence:jakarta.persistence-api:4.x + test: "*CompatibilityLaneDefinitionTest" + reason: the JPA 4 API is not on any configuration this build resolves, so nothing has been compiled against it + - id: hibernate-8 + target: Hibernate 8 + coordinate: org.hibernate.orm:hibernate-core:8.x + test: "*HibernateCompatibilityPolicyTest" + reason: Hibernate 8 is not resolvable from this build, so nothing has been compiled or run against it + - id: postgresql-19 + target: PostgreSQL 19 + coordinate: postgres:19-alpine + test: "*ExperimentalPromotionGateTest" + reason: no PostgreSQL 19 image is published yet, so no container of that major has ever been started by this lane + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 + - uses: ./.github/actions/setup-gradle-java + - name: Run the current-runtime policy probe + id: compatibility-probe + working-directory: src + run: >- + ./gradlew + :adapter:outbound:persistence-jpa:test + --tests '${{ matrix.test }}' + --stacktrace + - name: Record what this lane did and did not execute + if: always() + env: + PROBE_OUTCOME: ${{ steps.compatibility-probe.outcome }} + TARGET: ${{ matrix.target }} + TARGET_COORDINATE: ${{ matrix.coordinate }} + REASON: ${{ matrix.reason }} + run: | + mkdir -p compatibility-evidence + { + echo "target=${TARGET}" + echo "target-coordinate=${TARGET_COORDINATE}" + echo "status=NOT_EXECUTABLE" + echo "probe-result=${PROBE_OUTCOME}" + echo "reason=${REASON}" + echo "what-ran=the current runtime's own policy and lane-definition tests" + echo "sha=${{ github.sha }}" + } > compatibility-evidence/status.properties + echo "::notice::${TARGET} compatibility is NOT_EXECUTABLE: ${REASON}" + - name: Upload the compatibility status + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 + with: + name: compatibility-status-${{ matrix.id }} + path: compatibility-evidence/status.properties + retention-days: 30 + if-no-files-found: error diff --git a/.github/workflows/jpa-nightly.yml b/.github/workflows/jpa-nightly.yml index 338679ce..0d7ad487 100644 --- a/.github/workflows/jpa-nightly.yml +++ b/.github/workflows/jpa-nightly.yml @@ -37,7 +37,7 @@ jobs: ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformContractTest -Pjpa.matrix.versions=${{ matrix.postgresql }} - --no-daemon + --stacktrace jpa-failure-injection: @@ -51,7 +51,7 @@ jobs: run: >- ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformFailureTest - --no-daemon + --stacktrace jpa-query-plan-and-security: @@ -66,7 +66,7 @@ jobs: ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformQueryPlanTest :adapter:outbound:persistence-jpa:jpaPlatformSecurityTest - --no-daemon + --stacktrace jpa-pool-pressure: @@ -86,5 +86,5 @@ jobs: run: >- ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformPoolContractTest - --no-daemon + --stacktrace diff --git a/.github/workflows/jpa-r2-evidence.yml b/.github/workflows/jpa-r2-evidence.yml index f5137df4..752e7830 100644 --- a/.github/workflows/jpa-r2-evidence.yml +++ b/.github/workflows/jpa-r2-evidence.yml @@ -33,7 +33,7 @@ jobs: ./gradlew :adapter:outbound:persistence-jpa:verifyJpaPrimaryFoundationEvidence -PjpaEvidenceProfile=r2 - --no-daemon + --stacktrace - name: Retain JPA R2 attempt manifests if: always() diff --git a/.github/workflows/jpa-release.yml b/.github/workflows/jpa-release.yml index 4ac31f95..c781b117 100644 --- a/.github/workflows/jpa-release.yml +++ b/.github/workflows/jpa-release.yml @@ -50,7 +50,7 @@ jobs: ./gradlew jpaReleaseGate -Pjpa.matrix.versions=${{ matrix.postgresql }} - --no-daemon + --stacktrace - name: Record which major this evidence covers if: always() @@ -120,9 +120,9 @@ jobs: working-directory: src run: >- ./gradlew - verifyCleanArchitectureDependencies + :verifyCleanArchitectureDependencies checkstyleMain :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*' :adapter:outbound:persistence-jpa:test --tests '*JpaReleaseManifestTest' - --no-daemon + --stacktrace diff --git a/.github/workflows/messaging-certification.yml b/.github/workflows/messaging-certification.yml index 26ae5b6b..d20e9ca8 100644 --- a/.github/workflows/messaging-certification.yml +++ b/.github/workflows/messaging-certification.yml @@ -41,7 +41,7 @@ jobs: working-directory: src # GITHUB_SHA is read by the lane and written into every evidence line, because "certified" # is a claim about one source tree. - run: ./gradlew :messaging:messaging-kafka:verifyMessagingCertificationEvidence --no-daemon --stacktrace + run: ./gradlew :messaging:messaging-kafka:verifyMessagingCertificationEvidence --stacktrace - name: Publish the certification evidence if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 diff --git a/.github/workflows/notification-platform.yml b/.github/workflows/notification-platform.yml index 8e2606a3..62adbb61 100644 --- a/.github/workflows/notification-platform.yml +++ b/.github/workflows/notification-platform.yml @@ -81,19 +81,19 @@ jobs: - name: Architecture gates working-directory: src run: | - ./gradlew verifyCleanArchitectureDependencies --console=plain - ./gradlew :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*' --console=plain + ./gradlew :verifyCleanArchitectureDependencies --console=plain + ./gradlew :app-bootstrap:architectureTest --console=plain - name: Configuration surface working-directory: src run: | - ./gradlew :app-bootstrap:verifyEnvKeys verifyPublicPathSnapshot --console=plain - ./gradlew verifyNotificationApiSurface verifyNotificationConfiguration --console=plain + ./gradlew :app-bootstrap:verifyEnvKeys :verifyPublicPathSnapshot --console=plain + ./gradlew :verifyNotificationApiSurface :verifyNotificationConfiguration --console=plain # A support grade is a promise about production behaviour. This refuses one the pipeline # cannot back — the check that would have caught five channels reading "Stable" while no # request had ever left the process. - name: Evidence manifest working-directory: src - run: ./gradlew verifyNotificationEvidence --console=plain + run: ./gradlew :verifyNotificationEvidence --console=plain - name: Static analysis working-directory: src run: ./gradlew :adapter:outbound:notification:check -x test --console=plain @@ -118,9 +118,15 @@ jobs: - name: Restart recovery and lease fencing (real PostgreSQL) working-directory: src run: ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformContractTest :adapter:outbound:persistence-jpa:jpaPlatformFailureTest --console=plain - - name: Full suite + - name: Notification regression suite working-directory: src - run: ./gradlew test --console=plain + run: >- + ./gradlew + :application-core:test + :adapter:outbound:notification:test + :adapter:outbound:persistence-jpa:test + :adapter:inbound:web:test + --console=plain # A filter that matches nothing passes. Each --tests filter above names a class that exists # today; if one is renamed the job must fail rather than quietly stop covering it. - name: Every named suite actually ran diff --git a/.github/workflows/object-storage-qualification.yml b/.github/workflows/object-storage-qualification.yml index 719b50cf..6e21bb79 100644 --- a/.github/workflows/object-storage-qualification.yml +++ b/.github/workflows/object-storage-qualification.yml @@ -30,15 +30,6 @@ env: TESTCONTAINERS_REUSE_ENABLE: "false" jobs: - poster-image-v7-migration: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - - uses: ./.github/actions/setup-gradle-java - - name: Run non-skipping Poster image migration qualification - working-directory: src - run: ./gradlew :sample-portfolio:posterImageMigrationTest --no-daemon --stacktrace - minio-managed-contract: runs-on: ubuntu-latest steps: @@ -46,7 +37,7 @@ jobs: - uses: ./.github/actions/setup-gradle-java - name: Run exact-release MinIO managed contract working-directory: src - run: ./gradlew :adapter:outbound:objectstorage:objectStorageMinioContractTest --no-daemon --stacktrace + run: ./gradlew :adapter:outbound:objectstorage:objectStorageMinioContractTest --stacktrace minio-managed-fault: if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' @@ -56,7 +47,7 @@ jobs: - uses: ./.github/actions/setup-gradle-java - name: Run digest-pinned MinIO and Toxiproxy fault contract working-directory: src - run: ./gradlew :adapter:outbound:objectstorage:objectStorageMinioFaultTest --no-daemon --stacktrace + run: ./gradlew :adapter:outbound:objectstorage:objectStorageMinioFaultTest --stacktrace aws-managed-common-subset: if: >- @@ -76,4 +67,4 @@ jobs: - uses: ./.github/actions/setup-gradle-java - name: Run protected AWS common-subset qualification working-directory: src - run: ./gradlew :adapter:outbound:objectstorage:objectStorageAwsQualificationTest --no-daemon --stacktrace + run: ./gradlew :adapter:outbound:objectstorage:objectStorageAwsQualificationTest --stacktrace diff --git a/.github/workflows/pr-adapters.yml b/.github/workflows/pr-adapters.yml index 4080021d..04fe19a8 100644 --- a/.github/workflows/pr-adapters.yml +++ b/.github/workflows/pr-adapters.yml @@ -142,7 +142,7 @@ jobs: run: >- ./gradlew :adapter:inbound:web:webCrossStackParityTest - --no-daemon + --stacktrace - name: Publish the parity recordings if: always() @@ -168,7 +168,7 @@ jobs: run: >- ./gradlew :adapter:inbound:web:webNginxProxyTest - --no-daemon + --stacktrace websocket-container-matrix: @@ -184,7 +184,7 @@ jobs: run: >- ./gradlew :adapter:inbound:websocket:websocketJettyTest - --no-daemon + --stacktrace # Docker-gated, and the lane fails rather than skipping. Upgrade handling is the single most @@ -202,7 +202,7 @@ jobs: run: >- ./gradlew :adapter:inbound:websocket:websocketNginxTest - --no-daemon + --stacktrace # One transport per job, so a transport that stops satisfying the shared contract fails on its own @@ -225,7 +225,7 @@ jobs: ./gradlew :adapter:outbound:httpclient:httpClientStableContractTest -Phttpclient.contract.transports=${{ matrix.transport }} - --no-daemon + --stacktrace # Only the Spring 7.0 lane. httpClientSecurityTest, httpClientBlockHoundTest and @@ -245,7 +245,7 @@ jobs: run: >- ./gradlew :adapter:outbound:httpclient:spring70CompatibilityTest - --no-daemon + --stacktrace # 16 and 18 — the ends of the Stable matrix. 17 runs in the integration stage. What this does not @@ -269,7 +269,7 @@ jobs: ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformContractTest -Pjpa.matrix.versions=${{ matrix.postgresql }} - --no-daemon + --stacktrace jpa-migration-smoke: @@ -285,5 +285,5 @@ jobs: run: >- ./gradlew :adapter:outbound:persistence-jpa:jpaPlatformMigrationTest - --no-daemon + --stacktrace diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dde50a87..07b4b992 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,11 +59,11 @@ jobs: working-directory: src run: >- ./gradlew - verifyCleanArchitectureDependencies - verifyPublicPathSnapshot + :verifyCleanArchitectureDependencies + :verifyPublicPathSnapshot :app-bootstrap:verifyEnvKeys :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.architecture.*' - --no-daemon + --stacktrace # Every web lane that `check` cannot reach. webCrossStackParityTest depends on `test`, @@ -87,7 +87,7 @@ jobs: :adapter:inbound:web:webCrossStackParityTest :adapter:inbound:web:webNginxProxyTest :adapter:inbound:web:webAdvancedTest - --no-daemon + --stacktrace - name: Publish the release evidence if: always() @@ -114,7 +114,7 @@ jobs: :adapter:inbound:websocket:websocketNginxTest :adapter:inbound:websocket:websocketTransportQualificationTest :adapter:inbound:websocket:websocketAdvancedTest - --no-daemon + --stacktrace - name: Publish the release evidence if: always() @@ -145,15 +145,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - uses: ./.github/actions/setup-gradle-java - - name: In-process contract lane + - name: Run the gRPC Stable certification lanes working-directory: src - run: ./gradlew :grpc:grpc-testkit:grpcInProcessContractTest --no-daemon --stacktrace - - name: Real Netty transport lane - working-directory: src - run: ./gradlew :grpc:grpc-testkit:grpcNettyContractTest --no-daemon --stacktrace - - name: Fault-injection lane - working-directory: src - run: ./gradlew :grpc:grpc-testkit:grpcFaultTest --no-daemon --stacktrace + run: >- + ./gradlew -p optional-platforms + :grpc:grpc-testkit:grpcInProcessContractTest + :grpc:grpc-testkit:grpcNettyContractTest + :grpc:grpc-testkit:grpcFaultTest + --stacktrace - name: Publish the gRPC release evidence if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4.6.2 @@ -178,22 +177,19 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2 - uses: ./.github/actions/setup-gradle-java - - name: Focused module tests - run: ./gradlew :adapter:outbound:httpclient:test --no-daemon --stacktrace - - name: Spring 6.2 API surface lane - run: ./gradlew :adapter:outbound:httpclient:spring62ApiSurfaceScan --no-daemon --stacktrace - - name: Spring 7.0 compatibility lane - run: ./gradlew :adapter:outbound:httpclient:spring70CompatibilityTest --no-daemon --stacktrace - - name: Stable cross-transport contract suite - run: ./gradlew :adapter:outbound:httpclient:httpClientStableContractTest --no-daemon --stacktrace - - name: SSRF and cardinality suite - run: ./gradlew :adapter:outbound:httpclient:httpClientSecurityTest --no-daemon --stacktrace - - name: Event-loop blocking suite - run: ./gradlew :adapter:outbound:httpclient:httpClientBlockHoundTest --no-daemon --stacktrace - - name: Toxiproxy fault-injection suite - run: ./gradlew :adapter:outbound:httpclient:httpClientFailureInjectionTest --no-daemon --stacktrace - - name: Resource-bound performance certification - run: ./gradlew :adapter:outbound:httpclient:httpClientPerformanceTest --no-daemon --stacktrace + - name: Run the HTTP client release qualification graph + run: >- + ./gradlew + :adapter:outbound:httpclient:test + :adapter:outbound:httpclient:spring62ApiSurfaceScan + :adapter:outbound:httpclient:spring70CompatibilityTest + :adapter:outbound:httpclient:httpClientStableContractTest + :adapter:outbound:httpclient:httpClientSecurityTest + :adapter:outbound:httpclient:httpClientBlockHoundTest + :adapter:outbound:httpclient:httpClientFailureInjectionTest + :adapter:outbound:httpclient:httpClientPerformanceTest + --stacktrace + httpclient-documentation: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 6aea7906..cd6cc1c4 100644 --- a/README.md +++ b/README.md @@ -90,14 +90,20 @@ cd src 3. `CaSkeletonApplication`을 새 애플리케이션 이름으로 바꾸고, 목표 도메인의 엔티티·repository port·유스케이스·adapter를 production 모듈에 추가합니다. 예시 코드는 `sample-portfolio`에만 둡니다. 4. 모듈 이름과 경계는 그대로 유지합니다. -검증은 sample-on과 sample-off를 모두 통과시킵니다. +검증은 먼저 composition root의 빠른 테스트와 sample-off 계약을 확인합니다. 루트에서 +`./gradlew test`를 호출하면 등록된 모든 하위 프로젝트의 `test`를 실행하므로 일상적인 로컬 +피드백 명령으로 사용하지 않습니다. 저장소 전체 qualification은 CI 또는 명시적인 `ci` task가 +담당합니다. ```bash cd src -./gradlew test +./gradlew :app-bootstrap:test ./gradlew :app-bootstrap:sampleOffTest +./gradlew architectureCheck ``` +병합 전 저장소 전체 검증이 필요하면 `./gradlew ci`를 실행합니다. + `sample-portfolio`는 템플릿이 유지하는 fixture/reference 모듈이라 production 모듈이 의존하지 않고, runtime에 sample bean이나 endpoint를 넣지 않습니다. 다운스트림 fork에서 fixture가 더 필요 없을 때만 sample-off 테스트를 통과시킨 뒤 정리합니다. ## 아키텍처 규칙과 검증 diff --git a/src/README.md b/src/README.md index 798b23b2..c08295da 100644 --- a/src/README.md +++ b/src/README.md @@ -11,18 +11,23 @@ ## build.gradle — 빌드 / 검증 게이트 -모든 모듈의 `check` 태스크는 아래 verify 게이트에 의존합니다. 빌드를 통과하려면 검사가 -모두 green 이어야 합니다. +검증은 로컬 피드백과 저장소 전체 검증을 분리합니다. `::check`는 해당 모듈의 컴파일, +테스트, Spotless, Checkstyle, Error Prone만 담당합니다. 저장소 전체 정책은 이름이 드러나는 +루트 task로 실행하며, CI가 이를 집계합니다. | 게이트 | 하는 일 | | --- | --- | -| `verifyCleanArchitectureDependencies` | 모듈 간 의존 방향이 허용된 범위 안에 있는지 검사 | -| `verifyRuntimeModuleMembership` | registry의 두 composition root membership과 실제 main project dependency가 정확히 일치하는지 검사 | -| `:app-bootstrap:verifyEnvKeys` | `env-keys.yaml` ↔ `application.yml` ↔ `src/.env.example` ↔ 타입 설정 메타데이터가 어긋나지 않는지 검사 | -| `verifyApplicationCoreDependencyPurity` | application-core의 production 의존이 project-only이고 클래스패스에 프레임워크가 없는지 검사 | -| `verifyNoIgnoredSourcePackages` | Git이 실을 수 없는 Java 소스 파일이 없는지 검사 | +| `architectureCheck` | Clean Architecture 의존 방향, runtime membership, application-core purity, Git에 실리지 않는 source package를 검사 | +| `qualityCheck` | 모든 leaf의 SpotBugs + FindSecBugs를 실행 | +| `configContractCheck` | `:app-bootstrap:verifyEnvKeys`로 환경 설정 계약을 검사 | +| `qualificationCheck` | Gradle TestKit 기반 build/repository behavior 검증을 실행 | +| `integrationCheck` | 명시적으로 선언된 strict integration lane을 실행. 일부 lane은 container runtime이 필요 | +| `ci` | 모든 leaf `check` + `architectureCheck` + `qualityCheck` + `configContractCheck` + `qualificationCheck` | +| `releaseCheck` | `ci`에 release provenance/archive 관련 검증을 추가 | -`architectureCheck` 하나가 위 네 개를 모두 실행합니다. +따라서 로컬에서 한 모듈만 수정했다면 `./gradlew :domain-core:check`처럼 focused task를 +먼저 사용합니다. Gradle TestKit qualification은 일반 leaf `check`나 `:app-bootstrap:build`에 +매달지 않고 `qualificationCheck`/`ci`에서 실행합니다. **2026-09에 삭제한 게이트.** `verifyOneTypePerFile`(Checkstyle의 `OneTopLevelClass`가 같은 규칙을 파싱된 파일에 대해 검사한다), `verifyTrivyignore`·`verifyQuarantineSunset`(빈 레지스트리를 지키는 @@ -34,19 +39,20 @@ ### Local bootstrap `./gradlew bootstrap`은 `bootstrapCompile` → `bootstrapDependencies` → -`bootstrapMigrateAndStart` → `bootstrapSampleContract` → `bootstrapSmoke`를 순서대로 실행합니다. +`bootstrapMigrateAndStart` → `bootstrapSmoke`를 순서대로 실행합니다. DB와 app lifecycle은 저장소 루트의 base/local Compose 조합이 소유하며, app startup Flyway가 끝나 public health endpoint가 준비되어야 다음 단계로 넘어갑니다. `src/.env`는 env 설정의 SSOT이고 bootstrap이 별도 env template을 만들지 않습니다. ### Traceable version + dependency locking -- 모든 project version은 `..+<12자리 git sha>`입니다. base version은 - `-PreleaseVersion`/`RELEASE_VERSION`, revision은 `-PgitRevision`/`GIT_SHA`/`GITHUB_SHA` 순으로 - 주입하고, 로컬에서는 현재 Git commit을 읽습니다. -- 모든 JAR manifest에 `Implementation-Version`과 `Build-Revision`을 기록합니다. Git metadata도 - revision property도 없는 상태는 traceable artifact를 만들 수 없으므로 configuration 단계에서 - 실패합니다. +- 추적 가능한 checkout에서는 project version이 `..+<12자리 git sha>`가 됩니다. + base version은 `-PreleaseVersion`/`RELEASE_VERSION`, revision은 `-PgitRevision`/`GIT_SHA`/ + `GITHUB_SHA` 순으로 주입하고, 로컬에서는 현재 Git commit을 읽습니다. +- Git metadata와 revision property가 모두 없는 일반 source archive build는 configuration 단계에서 + 실패하지 않고 `..-SNAPSHOT`으로 동작합니다. 다만 `releaseCheck`가 실행하는 + `verifyReleaseProvenance`는 source revision이 없으면 실패하므로 추적 불가능한 산출물을 release로 + 취급할 수 없습니다. - 모든 subproject가 `lockAllConfigurations()` + `LockMode.STRICT`를 사용하고, lock state는 Gradle 기본 `/gradle.lockfile`에 둡니다. 이 경로는 Renovate Gradle manager 기본 인식 경로와 같습니다. @@ -87,16 +93,16 @@ vendor/build나 container base image까지 byte-for-byte 같음을 주장하지 registry와 ArchUnit 규칙(`CleanArchitectureTest`)을 함께 갱신해야 합니다. settings와 gate는 같은 registry를 읽고, 등록되지 않은 leaf나 허용되지 않은 edge를 fail-closed로 거부합니다. -### `verifyRuntimeModuleMembership` +### `verifyRuntimeModuleRegistry` -- **하는 일.** 같은 registry의 `runtime_compositions`와 각 leaf의 `runtime_memberships`를 읽어 - `app-bootstrap`/`sample-portfolio`의 실제 `api`/`implementation`/`compileOnly`/`runtimeOnly` - project dependency와 정확히 대조합니다. -- **opt-in의 의미.** membership이 빈 GraphQL/gRPC/WebSocket/Mongo leaf는 독립 빌드 대상이지만 두 - shipped runtime에는 없습니다. app-bootstrap의 `conditionalTransportTest` test-only classpath는 - 실제 채택 전에 세 inbound transport를 함께 qualification하기 위한 evidence composition입니다. -- **변경 규칙.** production edge를 추가하거나 제거할 때 `allowed_dependencies`, - `runtime_memberships`, 실제 Gradle dependency를 같은 변경에서 갱신하지 않으면 `check`가 실패합니다. +- **하는 일.** `composition_roots`가 가리키는 실제 Gradle `runtimeClasspath`를 resolve해서 그 안의 + project dependency가 architecture registry에 등록된 application module인지 확인합니다. +- **SSOT.** 어떤 module이 runtime에 실리는지는 `app-bootstrap/build.gradle`의 Gradle dependency graph가 + 결정합니다. registry가 같은 membership 목록을 다시 저장하지 않습니다. +- **변경 규칙.** production edge를 추가하거나 제거할 때는 실제 Gradle dependency와 + `allowed_dependencies` 같은 architecture policy만 함께 검토합니다. exact runtime snapshot을 두 군데 + 맞추는 작업은 없습니다. +- 기존 `verifyRuntimeModuleMembership` task는 외부 스크립트 호환성을 위한 alias입니다. 세 opt-in inbound transport의 test-only composition, 실제 wire 경계, positive-count/zero-skip 증거는 다음 release-blocking aggregate로 실행합니다. @@ -120,7 +126,7 @@ Checkstyle은 파싱된 파일에 같은 질문을 하고, leaf 단위로 돕니 - **소유.** app-bootstrap. 이 질문("이 애플리케이션의 배포에 무엇을 줘야 하는가")은 composition root의 것이고, `./gradlew :domain-core:check`가 알아야 할 사항이 아닙니다. 루트 집계 이름은 - `configContractCheck`이고 정의는 `src/gradle/config-contract.gradle`입니다. + `configContractCheck`이고 구현은 `build-logic`의 `ca.config-contract` convention plugin입니다. - **하는 일.** `docs/registries/env-keys.yaml`, `application.yml`, `src/.env.example` 세 곳을 lock-step(서로 어긋나지 않게) 으로 유지합니다. `env-keys.yaml` 이 `APP_` 키의 SSOT 이고, drift 가 생기면 빌드를 실패시킵니다. diff --git a/src/adapter/inbound/graphql/CLAUDE.md b/src/adapter/inbound/graphql/CLAUDE.md index 1d6f2755..79db80c1 100644 --- a/src/adapter/inbound/graphql/CLAUDE.md +++ b/src/adapter/inbound/graphql/CLAUDE.md @@ -191,7 +191,7 @@ cd src --console=plain ``` -플랫폼 테스트 레인(`gradle/graphql-platform-conventions.gradle` 등록). 기본 `test` 는 +플랫폼 테스트 레인(`ca.graphql-platform` convention plugin 등록). 기본 `test` 는 `quarantine`·`graphql-performance` 태그를 제외한다: ```bash diff --git a/src/adapter/inbound/graphql/build.gradle b/src/adapter/inbound/graphql/build.gradle index 757ef704..86c6ca90 100644 --- a/src/adapter/inbound/graphql/build.gradle +++ b/src/adapter/inbound/graphql/build.gradle @@ -1,3 +1,10 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'java-test-fixtures' + id 'ca.graphql-platform' +} + // Driving adapter: GraphQL API (skeleton machinery, transport-only). // // Spring for GraphQL is schema-first: schema files live in src/main/resources/graphql/*.graphqls @@ -8,9 +15,6 @@ // spring-graphql / graphql-java versions are managed by the Spring Boot BOM, so no explicit // versions or module-scoped platform imports are needed (unlike the grpc adapter, whose io.grpc // coordinates the BOM does not manage). -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - description = 'Inbound adapter: GraphQL API (Spring for GraphQL, GraphQL execution platform)' // The contract suites, the integration fixtures and the in-memory registries are for the people @@ -20,9 +24,7 @@ description = 'Inbound adapter: GraphQL API (Spring for GraphQL, GraphQL executi // actor without a credential. A separate test-fixtures artifact keeps them consumable by the tests // that want them and out of the jar that runs in production; `verifyGraphQlProductionJar` checks // the second half rather than trusting it. -apply plugin: 'java-test-fixtures' -apply from: "${rootProject.projectDir}/gradle/graphql-platform-conventions.gradle" dependencies { implementation project(':shared-contract') diff --git a/src/adapter/inbound/grpc/build.gradle b/src/adapter/inbound/grpc/build.gradle index 34838737..b33c63ef 100644 --- a/src/adapter/inbound/grpc/build.gradle +++ b/src/adapter/inbound/grpc/build.gradle @@ -1,3 +1,11 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' +} + +String grpcVersion = libs.versions.grpc.get() +String protobufVersion = libs.versions.protobufGrpc.get() + // Driving adapter: gRPC API (skeleton machinery, transport-only). // // A SmartLifecycle bean (GrpcServerRunner) owns the io.grpc Netty server, so this module depends on @@ -17,9 +25,6 @@ // adapter:inbound:websocket) and root `ext.protobufVersion` (used here) — on different majors. They // do not meet today because neither leaf is in a composition root; see the W2A handoff. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - dependencyManagement { imports { mavenBom "io.grpc:grpc-bom:${grpcVersion}" diff --git a/src/adapter/inbound/web/build.gradle b/src/adapter/inbound/web/build.gradle index c15254a1..aab96498 100644 --- a/src/adapter/inbound/web/build.gradle +++ b/src/adapter/inbound/web/build.gradle @@ -1,9 +1,10 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'java-test-fixtures' +} + // Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - -apply plugin: 'java-test-fixtures' - // The inbound HTTP API execution platform design models itself as 23 Stable Gradle modules under // modules/web. This repository's fail-closed module registry outranks that layout, so those modules // are packages here and WebModuleBoundaryTest enforces the design's module dependency table. The diff --git a/src/adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/budget/WebBudgetCatalog.java b/src/adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/budget/WebBudgetCatalog.java index 7300a774..4359ce72 100644 --- a/src/adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/budget/WebBudgetCatalog.java +++ b/src/adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/budget/WebBudgetCatalog.java @@ -67,7 +67,7 @@ public final class WebBudgetCatalog { /** The budget for a profile, when it is registered. */ public Optional find(WebBudgetProfileName name) { - return Optional.ofNullable(profiles.get(name == null ? null : name)); + return name == null ? Optional.empty() : Optional.ofNullable(profiles.get(name)); } /** Every registered profile, for a startup report. */ diff --git a/src/adapter/inbound/websocket/build.gradle b/src/adapter/inbound/websocket/build.gradle index 5c636a51..58d1ee72 100644 --- a/src/adapter/inbound/websocket/build.gradle +++ b/src/adapter/inbound/websocket/build.gradle @@ -1,9 +1,10 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'java-test-fixtures' +} + // Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - -apply plugin: 'java-test-fixtures' - // Driving adapter: WebSocket (STOMP over SockJS) live-push channel (skeleton machinery, transport-only). // // W1 live-bus: LiveEventStompBroadcaster forwards only explicitly allowlisted, bounded primitive diff --git a/src/adapter/outbound/cache-redis/build.gradle b/src/adapter/outbound/cache-redis/build.gradle index e5120be8..ad78c314 100644 --- a/src/adapter/outbound/cache-redis/build.gradle +++ b/src/adapter/outbound/cache-redis/build.gradle @@ -1,11 +1,13 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' +} + // Redis SDK leaf — see docs/superpowers/specs/2026-08-07-redis-wrapper-typed-api-design.md. // // The design models the SDK as separate Gradle modules. This repository's fail-closed module // registry outranks that layout, so the module boundaries are packages under // dev.caskeleton.adapter.outbound.cache.redis.sdk and RedisSdkModuleBoundaryTest enforces them. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - dependencies { // Registered edges the semantic port adapters need. The SDK's *main* source imports nothing from // them today — the semantic cache/session/idempotency/rate-limit adapters that did were removed @@ -193,4 +195,3 @@ strictTestLanes { } } } - diff --git a/src/adapter/outbound/fileserver/build.gradle b/src/adapter/outbound/fileserver/build.gradle index fb21047a..38b7d4b4 100644 --- a/src/adapter/outbound/fileserver/build.gradle +++ b/src/adapter/outbound/fileserver/build.gradle @@ -1,10 +1,12 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' +} + // Driven adapter for provider-neutral file publication and legacy CSV export. The only qualified // R2 provider is local-persistent; shared-mounted/NFS and SFTP are not stand-ins or implemented // capabilities. Its IO path uses only the JDK. Spring Boot autoconfigure supplies explicit, // disabled-default R1/R2 composition and SLF4J remains the diagnostics API. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - description = 'Outbound adapter: file publication (R1 CSV export, R2 local-persistent) plus the ' + \ 'local filesystem content platform behind the HTTP Fileserver' diff --git a/src/adapter/outbound/httpclient/build.gradle b/src/adapter/outbound/httpclient/build.gradle index 43468249..497d5f74 100644 --- a/src/adapter/outbound/httpclient/build.gradle +++ b/src/adapter/outbound/httpclient/build.gradle @@ -1,8 +1,9 @@ +plugins { + id 'ca.spring-library' + id 'java-test-fixtures' +} + // Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. -apply plugin: 'ca.spring-library' - -apply plugin: 'java-test-fixtures' - // Outbound HTTP Client Platform leaf — see // docs/superpowers/specs/2026-08-08-httpclient-platform-design.md (design package) and // docs/httpclient/repository-adaptation.md (how the design's 19 library modules map here). diff --git a/src/adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/security/MutualTlsHandshakeContractTest.java b/src/adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/security/MutualTlsHandshakeContractTest.java index db79b2c0..ef166896 100644 --- a/src/adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/security/MutualTlsHandshakeContractTest.java +++ b/src/adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/security/MutualTlsHandshakeContractTest.java @@ -40,7 +40,7 @@ class MutualTlsHandshakeContractTest { TlsFixture fixture = TlsFixture.trusted(); try (MockHttpServer server = MockHttpServer.startTls(fixture.serverSocketFactory(), true)) { server.enqueueJson(200, "{\"id\":1,\"name\":\"mtls\"}"); - ClientProfile profile = tlsProfile(server.uri("/")); + ClientProfile profile = tlsProfile(server.ipv4Uri("/")); ApacheBlockingTransportProvider provider = new ApacheBlockingTransportProvider( Optional.empty(), @@ -56,7 +56,7 @@ class MutualTlsHandshakeContractTest { NoopLifecycleListener.INSTANCE)) .build() .get() - .uri(server.uri("/users/1")) + .uri(server.ipv4Uri("/users/1")) .retrieve() .body(String.class); assertThat(body).contains("mtls"); @@ -72,7 +72,7 @@ class MutualTlsHandshakeContractTest { TlsFixture fixture = TlsFixture.trusted(); try (MockHttpServer server = MockHttpServer.startTls(fixture.serverSocketFactory(), true)) { server.enqueueJson(200, "{\"id\":1,\"name\":\"never\"}"); - ClientProfile profile = tlsProfile(server.uri("/")); + ClientProfile profile = tlsProfile(server.ipv4Uri("/")); ApacheBlockingTransportProvider provider = new ApacheBlockingTransportProvider( Optional.empty(), @@ -91,7 +91,7 @@ class MutualTlsHandshakeContractTest { // so the failure can surface either during the handshake or on the first read. Both are // failures; what must never happen is the request being served. assertThatThrownBy( - () -> client.get().uri(server.uri("/users/1")).retrieve().body(String.class)) + () -> client.get().uri(server.ipv4Uri("/users/1")).retrieve().body(String.class)) .isInstanceOf(RuntimeException.class); } finally { provider.close( @@ -119,7 +119,7 @@ class MutualTlsHandshakeContractTest { try (MockHttpServer server = MockHttpServer.startTls(serverFixture.serverSocketFactory(), false)) { server.enqueueJson(200, "{\"id\":1,\"name\":\"never\"}"); - ClientProfile profile = tlsProfile(server.uri("/")); + ClientProfile profile = tlsProfile(server.ipv4Uri("/")); Throwable captured = captureFailure(profile, server, unrelatedClientTrust); assertPermanent(captured); } @@ -128,7 +128,7 @@ class MutualTlsHandshakeContractTest { private void assertPermanentTlsFailure(TlsFixture fixture) throws Exception { try (MockHttpServer server = MockHttpServer.startTls(fixture.serverSocketFactory(), false)) { server.enqueueJson(200, "{\"id\":1,\"name\":\"never\"}"); - ClientProfile profile = tlsProfile(server.uri("/")); + ClientProfile profile = tlsProfile(server.ipv4Uri("/")); Throwable captured = captureFailure(profile, server, fixture); assertPermanent(captured); } @@ -150,7 +150,7 @@ class MutualTlsHandshakeContractTest { NoopLifecycleListener.INSTANCE)) .build(); try { - client.get().uri(server.uri("/users/1")).retrieve().body(String.class); + client.get().uri(server.ipv4Uri("/users/1")).retrieve().body(String.class); throw new AssertionError("the handshake was expected to fail"); } catch (RuntimeException failure) { return failure; diff --git a/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/MockHttpServer.java b/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/MockHttpServer.java index 42cce7f7..7beabf99 100644 --- a/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/MockHttpServer.java +++ b/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/MockHttpServer.java @@ -71,6 +71,11 @@ public final class MockHttpServer implements AutoCloseable { return server.url(path).uri(); } + /** Deterministic IPv4 loopback URI for TLS fixtures on dual-stack hosts. */ + public URI ipv4Uri(String path) { + return server.url(path).newBuilder().host("127.0.0.1").build().uri(); + } + public int port() { return server.getPort(); } diff --git a/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/TlsFixture.java b/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/TlsFixture.java index f8ddbf7d..156a748b 100644 --- a/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/TlsFixture.java +++ b/src/adapter/outbound/httpclient/src/testFixtures/java/dev/caskeleton/adapter/outbound/httpclient/testkit/TlsFixture.java @@ -49,6 +49,12 @@ public final class TlsFixture { .signedBy(authority) .commonName(serverCommonName) .addSubjectAlternativeName(serverCommonName); + if ("localhost".equals(serverCommonName)) { + // MockWebServer binds an IPv4 loopback socket in this fixture environment while localhost may + // resolve to both 127.0.0.1 and ::1. Include the explicit IPv4 SAN so TLS tests can use a + // deterministic address without changing the certificate semantics under test. + server.addSubjectAlternativeName("127.0.0.1"); + } if (validity.isNegative()) { long now = System.currentTimeMillis(); server.validityInterval( diff --git a/src/adapter/outbound/messaging/build.gradle b/src/adapter/outbound/messaging/build.gradle index 17ba6b78..5b0ace37 100644 --- a/src/adapter/outbound/messaging/build.gradle +++ b/src/adapter/outbound/messaging/build.gradle @@ -1,6 +1,8 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' +} -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' dependencies { implementation project(':application-core') diff --git a/src/adapter/outbound/notification/build.gradle b/src/adapter/outbound/notification/build.gradle index 90292523..b2b5cbbd 100644 --- a/src/adapter/outbound/notification/build.gradle +++ b/src/adapter/outbound/notification/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' +plugins { + id 'ca.spring-library' + id 'ca.spring-config' +} dependencies { implementation project(':application-core') diff --git a/src/adapter/outbound/objectstorage/build.gradle b/src/adapter/outbound/objectstorage/build.gradle index d72208c3..3098970b 100644 --- a/src/adapter/outbound/objectstorage/build.gradle +++ b/src/adapter/outbound/objectstorage/build.gradle @@ -1,3 +1,8 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' +} + // Driven adapter: provider-neutral semantic object-storage ports plus a bounded local-development // provider. Canonical app.object-storage activation is disabled by default. The old whole-byte[] // filesystem/S3 adapters remain isolated, explicit legacy compatibility only. @@ -10,9 +15,6 @@ // This sentence used to end "and this repo has no version catalog". That is false, and this file // disproves it twice below with `libs.archunit.junit5` and `libs.jqwik`. Module scope is a locking // decision; the catalog just has no awssdk entry. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - description = 'Outbound adapter: object storage (S3/MinIO + local filesystem)' @@ -33,7 +35,7 @@ strictTestLanes { dependencyManagement { imports { - mavenBom "software.amazon.awssdk:bom:${awsSdkVersion}" + mavenBom "software.amazon.awssdk:bom:${libs.versions.awsSdk.get()}" } } diff --git a/src/adapter/outbound/persistence-jpa/build.gradle b/src/adapter/outbound/persistence-jpa/build.gradle index c1c13bf5..3185b6f6 100644 --- a/src/adapter/outbound/persistence-jpa/build.gradle +++ b/src/adapter/outbound/persistence-jpa/build.gradle @@ -1,9 +1,11 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'java-test-fixtures' + id 'ca.jpa-evidence' +} + // Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - -apply plugin: 'java-test-fixtures' - // JPA persistence adapter — merged RDBMS base + PostgreSQL vendor module. // Owns JPA entities, Spring Data repositories, mappers, transaction/audit/lock/outbox port // implementations, and the vendor-neutral SPI interfaces (OutboxClaimRepository / @@ -29,8 +31,6 @@ strictTestLanes { sourceSet('jpaPlatformPerformanceTest') { compilesAgainst 'main', 'testFixtures' } } -ext.jpaPostgreSqlEvidenceImage = 'postgres:16-alpine' - dependencies { implementation project(':application-core') implementation project(':shared-contract') @@ -108,6 +108,8 @@ dependencies { // through `requires(...)` turns on `failOnNoMatchingTests` AND the post-run check that the named // selector actually executed, so a renamed readiness class fails its lane instead of leaving it // with nothing to run. +String jpaPostgreSqlEvidenceImage = providers.gradleProperty('jpaPostgreSqlEvidenceImage') + .getOrElse('postgres:16-alpine') String readinessPackage = 'dev.caskeleton.adapter.outbound.persistence.readiness' Map postgresqlReadinessLanes = [ postgresqlLifecycleIntegrationTest : 'PostgreSqlLifecycleIntegrationTest', @@ -155,7 +157,7 @@ strictTestLanes.lanes.named('postgresqlSecurityBaselineIntegrationTest').configu } // The task itself stays, and its name is not negotiable: config/jpa/readiness-cards.yaml lists it as -// a support task of the `jpa-security-baseline` card, and gradle/jpa-evidence.gradle resolves every +// a support task of the `jpa-security-baseline` card, and the ca.jpa-evidence qualification plugin resolves every // listed path through `tasks.findByName` and fails the build when one is missing. What changed is // what it checks. Grepping the fixture's source text for 'runtimeRoleCannotCreateInApplicationSchema', // 'assertDockerAvailable' and '42501' passed on three strings in a comment and proved nothing about @@ -355,11 +357,11 @@ apiSurface { ] } -apply from: rootProject.file('gradle/jpa-evidence.gradle') + // The JPA readiness registry describes this platform's lanes and resolves their task paths, so it // runs with this leaf's `check` rather than with all 62. The task itself is registered by -// gradle/qualification/jpa-qualification.gradle, which the root applies. +// the ca.jpa-qualification plugin from build-qualification, which the root applies. tasks.named('check') { dependsOn rootProject.tasks.named('verifyJpaReadinessRegistry') } diff --git a/src/adapter/outbound/persistence-jpa/src/testFixtures/java/dev/caskeleton/adapter/outbound/persistence/testkit/release/JpaReleaseRendering.java b/src/adapter/outbound/persistence-jpa/src/testFixtures/java/dev/caskeleton/adapter/outbound/persistence/testkit/release/JpaReleaseRendering.java index c942136a..e5e45dd4 100644 --- a/src/adapter/outbound/persistence-jpa/src/testFixtures/java/dev/caskeleton/adapter/outbound/persistence/testkit/release/JpaReleaseRendering.java +++ b/src/adapter/outbound/persistence-jpa/src/testFixtures/java/dev/caskeleton/adapter/outbound/persistence/testkit/release/JpaReleaseRendering.java @@ -30,7 +30,7 @@ public final class JpaReleaseRendering { private static final Pattern WORKFLOW_PROMOTION_LOOP = Pattern.compile("for major in ([0-9 ]+);"); private static final Pattern QUOTED_MAJOR = Pattern.compile("\"(\\d+)\""); private static final Pattern COMPATIBILITY_TARGET = - Pattern.compile("target=PostgreSQL\\s+(\\d+)"); + Pattern.compile("^\\s*target:\\s*PostgreSQL\\s+(\\d+)\\s*$", Pattern.MULTILINE); private JpaReleaseRendering() {} @@ -128,9 +128,9 @@ public final class JpaReleaseRendering { * the conclusion this repository's own review pass reached before checking the workflow * directory. * - *

The target is read from the status artifact the lane writes rather than from its filename. A - * filename is a label; the artifact is what a promotion decision would actually be read from, so - * a lane that stops recording its target stops counting as a lane. + *

The target is read from the workflow matrix declaration rather than from its filename. The + * status artifact receives that same matrix value through {@code TARGET}; a lane that stops + * declaring its PostgreSQL target therefore stops counting as a lane. */ public static List compatibilityLaneTargets(String workflow) { Objects.requireNonNull(workflow, "workflow"); diff --git a/src/adapter/outbound/persistence-mongo/CLAUDE.md b/src/adapter/outbound/persistence-mongo/CLAUDE.md index 27043c32..b7236930 100644 --- a/src/adapter/outbound/persistence-mongo/CLAUDE.md +++ b/src/adapter/outbound/persistence-mongo/CLAUDE.md @@ -36,10 +36,9 @@ design package's assumed module layout onto this leaf lives in `verifyCleanArchitectureDependencies` only checks that resolved edges are a subset of the declared ones, so an unused permission passes every run; `MongoRegistryPermissionParityTest` checks the other direction and fails when the two sets differ. -- `runtime_memberships` is `["app-bootstrap"]`, and the composition root really does declare - `implementation(project(':adapter:outbound:persistence-mongo'))` — with the reactive starter and - the reactivestreams driver excluded, because there is no reactive port in the shipped Stable - scope. `RuntimeMembershipClasspathAgreementTest` compares the registry against the resolved +- The composition root declares `implementation(project(':adapter:outbound:persistence-mongo'))` — + with the reactive starter and the reactivestreams driver excluded, because there is no reactive + port in the shipped Stable scope. The actual Gradle runtime graph is the runtime SSOT; runtime classpath, so the membership cannot drift from what the jar carries. Property-only activation therefore works here: the switch turns on a module that already ships, and shipping it off is not the same contract as leaving it out, because absence cannot be reversed at deploy time diff --git a/src/adapter/outbound/persistence-mongo/README.md b/src/adapter/outbound/persistence-mongo/README.md index 2a9f6e91..574b4180 100644 --- a/src/adapter/outbound/persistence-mongo/README.md +++ b/src/adapter/outbound/persistence-mongo/README.md @@ -13,11 +13,11 @@ document/repository/mapper와 application 또는 domain port 구현을 추가할 ### shipped runtime에 들어 있고, property가 그 스위치다 -`app-bootstrap`은 이 leaf에 project dependency를 두고(`src/app-bootstrap/build.gradle`, -reactive starter와 reactivestreams driver는 exclude), registry의 `runtime_memberships`도 -`["app-bootstrap"]`이다. 두 사실은 `RuntimeMembershipClasspathAgreementTest`가 runtime classpath와 -비교해 붙잡는다. 그래서 `ca-skeleton.persistence-mongo.enabled=true`는 실제로 동작하는 master -switch다 — 없는 모듈을 부르는 property가 아니라, 이미 jar에 들어 있는 모듈을 켜는 스위치다. +`app-bootstrap`은 이 leaf에 project dependency를 둔다(`src/app-bootstrap/build.gradle`; reactive +starter와 reactivestreams driver는 exclude). runtime 포함 여부의 SSOT는 이 실제 Gradle graph이고 +`MongoRegistryPermissionParityTest`는 composition edge가 존재하는지도 확인한다. 그래서 +`ca-skeleton.persistence-mongo.enabled=true`는 없는 모듈을 부르는 property가 아니라 이미 application +classpath에 들어 있는 모듈을 켜는 master switch다. 빠져 있는 모듈은 꺼진 모듈과 같은 계약이 아니다. 부재는 배포 시점에 되돌릴 수 없고, gating 결함을 전부 가린다. 존재하지 않는 코드는 자기 condition이 무엇이라고 말하든 bean을 하나도 들고 있지 않기 diff --git a/src/adapter/outbound/persistence-mongo/build.gradle b/src/adapter/outbound/persistence-mongo/build.gradle index 0bb8c4a2..ac538637 100644 --- a/src/adapter/outbound/persistence-mongo/build.gradle +++ b/src/adapter/outbound/persistence-mongo/build.gradle @@ -1,10 +1,11 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'java-test-fixtures' +} + // Shared test code as a Gradle test-fixtures variant — ADR-BUILD-001. Applied here rather than // from the root, the way the GraphQL leaf does: only a leaf that has shared test code needs it. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - -apply plugin: 'java-test-fixtures' - // MongoDB Document Persistence Platform leaf — see // docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md (design package) // and docs/mongodb/repository-adaptation.md (how the design's 19 stable + 12 advanced library diff --git a/src/adapter/outbound/support/build.gradle b/src/adapter/outbound/support/build.gradle index 1e76ce38..b4b7342f 100644 --- a/src/adapter/outbound/support/build.gradle +++ b/src/adapter/outbound/support/build.gradle @@ -1,7 +1,9 @@ +plugins { + id 'ca.spring-library' +} + // Shared base for outbound integration adapters: correlation, fail-open dependency // logging, and the @Configuration seam. Depended on by messaging/cache/notification/httpclient. -apply plugin: 'ca.spring-library' - dependencies { implementation 'org.springframework.boot:spring-boot-autoconfigure' implementation 'org.slf4j:slf4j-api' diff --git a/src/app-bootstrap/CLAUDE.md b/src/app-bootstrap/CLAUDE.md index 58cf3f81..92e4c7b5 100644 --- a/src/app-bootstrap/CLAUDE.md +++ b/src/app-bootstrap/CLAUDE.md @@ -19,9 +19,9 @@ Package root: `dev.caskeleton.bootstrap`. ## Allowed -- Runtime leaves whose dependency edge is explicitly allowed and whose `runtime_memberships` - includes `app-bootstrap` in `src/config/architecture/modules.json`; do not duplicate that - dependency list here. +- Runtime leaves explicitly declared by this module's Gradle dependencies and permitted by the + architecture registry. The resolved Gradle runtime graph is the runtime SSOT; do not duplicate a + membership list in metadata. - Spring Boot startup/runtime dependencies. - ArchUnit in tests. diff --git a/src/app-bootstrap/README.md b/src/app-bootstrap/README.md index a093e0f1..be9311ee 100644 --- a/src/app-bootstrap/README.md +++ b/src/app-bootstrap/README.md @@ -8,16 +8,15 @@ 읽는 Gradle gate가 별도로 검사한다. 허용/금지 의존, 책임 범위, 테스트 명령 같은 **모듈 규칙**의 SSOT 는 [CLAUDE.md](CLAUDE.md) 다. -기본 composition은 `build.gradle`의 main project dependency와 registry의 -`runtime_memberships=["app-bootstrap"]`가 정확히 일치해야 한다. GraphQL, gRPC, WebSocket, -MongoDB leaf의 membership은 비어 있어 두 shipped runtime에 포함되지 않는다. object storage는 -sample runtime에만 있고 file server는 기본 app runtime에 이미 포함된다. +기본 composition의 SSOT는 `build.gradle`의 실제 project dependency graph다. architecture registry는 +같은 runtime membership 목록을 다시 저장하지 않는다. `verifyRuntimeModuleRegistry`가 resolved +`runtimeClasspath`에 registry 밖 project가 침투하지 않았는지만 검증한다. -`conditionalTransportTest`는 GraphQL/gRPC/WebSocket 세 leaf를 test-only classpath에서 함께 -resolve하는 명시적 qualification composition이다. main `implementation`/`runtimeOnly` edge가 아니므로 -shipped runtime membership을 바꾸지 않는다. optional leaf를 실제 app에 채택하려면 같은 변경에서 -`allowed_dependencies`, `runtime_memberships`, `app-bootstrap/build.gradle` main dependency, -typed settings/보안 경계/검증을 모두 갱신해야 한다. +`conditionalTransportTest`는 GraphQL/gRPC/WebSocket 세 leaf를 test-only classpath에서 함께 resolve하는 +qualification composition이다. 현재 GraphQL은 app-bootstrap의 실제 dependency이고 master switch로 +활성화되며, gRPC/WebSocket은 production dependency가 아니다. optional leaf를 실제 app에 채택하려면 +`app-bootstrap/build.gradle`의 dependency, architecture의 `allowed_dependencies`, typed settings/보안 +경계/검증을 같은 변경에서 갱신한다. 이 문서는 코드 주석에서 덜어낸 **설계 결정의 근거**를 모아둔 참조용 기록이다 — 코드를 읽다 "왜 이렇게 했나"가 궁금할 때 본다. 본문은 한국어로 쓰고, 클래스·Spring API·메트릭 이름처럼 diff --git a/src/app-bootstrap/build.gradle b/src/app-bootstrap/build.gradle index 6f9634b1..588b8230 100644 --- a/src/app-bootstrap/build.gradle +++ b/src/app-bootstrap/build.gradle @@ -1,30 +1,12 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'org.springframework.boot' + id 'ca.config-contract' +} + // Application entry point. Wires the default runtime module set and runs Spring Boot. // Optional leaves require an explicit registry allowance plus a composition-root dependency. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - -apply plugin: 'org.springframework.boot' - - -// The sample fixture (sampleFixture -> sample-portfolio -> objectstorage) pulls software.amazon.awssdk:s3 -// onto the sample-on test classpath; its version is managed by the AWS SDK v2 BOM (NOT the Spring Boot -// BOM). Import that BOM at this module's scope so the transitive s3 dependency resolves for the ArchUnit -// sample-on analysis. AWS-SDK version SSOT = ext.awsSdkVersion. -dependencyManagement { - imports { - mavenBom "software.amazon.awssdk:bom:${awsSdkVersion}" - } -} - -// feature-sample-removal-adoption-contract D7 — ordinary tests may inspect the sample fixture, -// while sampleOffTest compiles the same core suite without the sample project on either classpath. -configurations { - sampleFixture { - canBeConsumed = false - canBeResolved = false - } -} - // The infrastructure lane. Testcontainers-backed tests boot a real PostgreSQL, so they belong to a // source set of their own rather than to `test`: the ordinary focused command for this leaf — // `./gradlew :app-bootstrap:test` — must not require a Docker daemon, and a leaf whose fastest @@ -36,20 +18,30 @@ strictTestLanes { compilesAgainst 'main' inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor' } + sourceSet('architectureTest') { + compilesAgainst 'main', 'test' + inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor' + } + sourceSet('bootCompositionTest') { + compilesAgainst 'main', 'test' + inherits 'implementation', 'compileOnly', 'runtimeOnly', 'annotationProcessor' + } lane('integrationTest') { description = 'Testcontainers-backed real-PostgreSQL integration contracts.' sourceSet = 'integrationTest' } } +// These two lanes were split physically from `test`, but they must preserve the ordinary test +// runtime semantics. The generic auxiliary-source-set helper includes compileClasspath in runtime, +// which is useful for some tool/qualification lanes but would leak testCompileOnly dependencies +// (notably Spring Cloud refresh scope) into these behavioral tests. +sourceSets.architectureTest.runtimeClasspath = + sourceSets.architectureTest.output + sourceSets.test.runtimeClasspath +sourceSets.bootCompositionTest.runtimeClasspath = + sourceSets.bootCompositionTest.output + sourceSets.test.runtimeClasspath + sourceSets { - sampleOffTest { - java.srcDirs = sourceSets.test.java.srcDirs - java.srcDir 'src/sampleOffTest/java' - resources.srcDirs = sourceSets.test.resources.srcDirs - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } functionalTest { java.srcDir 'src/functionalTest/java' resources.srcDir 'src/functionalTest/resources' @@ -62,15 +54,6 @@ sourceSets { } } -configurations { - testCompileClasspath.extendsFrom sampleFixture - testRuntimeClasspath.extendsFrom sampleFixture - sampleOffTestImplementation.extendsFrom testImplementation - sampleOffTestCompileOnly.extendsFrom testCompileOnly - sampleOffTestRuntimeOnly.extendsFrom testRuntimeOnly - sampleOffTestAnnotationProcessor.extendsFrom testAnnotationProcessor -} - dependencies { implementation project(':domain-core') implementation project(':application-core') @@ -175,8 +158,6 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-json' // test-only: parses checked-in Messaging capability registries for the fail-closed drift gate. testImplementation 'org.yaml:snakeyaml' - // sample-on only: ArchUnit analyses the reference impl. sampleOffTest intentionally omits it. - sampleFixture project(':sample-portfolio') // test-only: ArchUnit violation fixtures intentionally import forbidden types. See README. testCompileOnly 'org.springframework:spring-tx' // test-only: streaming/websocket violation fixtures import these forbidden packages. See README. @@ -202,8 +183,7 @@ dependencies { // since an incomplete analysis finds fewer bugs while still reporting success. It surfaced when // the messaging platform's leaves widened the classpath enough to reach those classes. // - // compileOnly, so it resolves for analysis and reaches no runtime. sampleOffTestCompileOnly - // extendsFrom testCompileOnly, so the sample-off analysis inherits it. + // compileOnly, so it resolves for analysis and reaches no runtime. testCompileOnly 'org.apache.groovy:groovy' // groovy.lang.GroovyObject // JSON log encoder; implementation (not runtimeOnly) because StartupFailures uses StructuredArguments at compile time. See README. @@ -237,35 +217,46 @@ def contractRegistriesDirectory = rootProject.projectDir.parentFile.toPath() .resolve('docs/registries').toFile() tasks.withType(Test).configureEach { systemProperty 'ca.repository.root', repositoryRootForContractTests - // This module's tests hold whole-repository ArchUnit class graphs. Gradle's 512m default runs - // out once more than one @AnalyzeClasses suite is resident, and the failure surfaces as an - // executor crash rather than as a named failing test — which is a bad afternoon to debug. - maxHeapSize = '2g' } -// Pin UTC for the TEST JVM so timestamp tests are host-locale-independent (production UTC owned elsewhere). See README. -tasks.named('test') { +// Fast developer test lane. Whole-repository ArchUnit graphs and full composition-root boot +// characterizations have their own source sets, so the ordinary lane does not compile or execute +// them. CI still runs both through architectureCheck / qualificationCheck. +tasks.named('test', Test) { + // Two forks are the measured sweet spot after moving whole-repository ArchUnit and full boot + // composition tests out of the fast lane. More forks duplicate Spring contexts for little gain. + maxParallelForks = 2 + inputs.dir(contractRegistriesDirectory) + .withPathSensitivity(PathSensitivity.RELATIVE) + // Pin UTC for the TEST JVM so timestamp tests are host-locale-independent (production UTC is + // owned elsewhere). + jvmArgs '-Duser.timezone=UTC' +} + +tasks.register('architectureTest', Test) { + group = 'verification' + description = 'Runs the whole-repository ArchUnit and architecture contract source set.' + testClassesDirs = sourceSets.architectureTest.output.classesDirs + classpath = sourceSets.architectureTest.runtimeClasspath + useJUnitPlatform() + failOnNoDiscoveredTests = true + shouldRunAfter tasks.named('test') + maxHeapSize = '2g' inputs.dir(contractRegistriesDirectory) .withPathSensitivity(PathSensitivity.RELATIVE) jvmArgs '-Duser.timezone=UTC' } -tasks.register('sampleOffCompile') { +tasks.register('bootCompositionTest', Test) { group = 'verification' - description = 'Compiles the complete app-bootstrap test corpus without sample-portfolio.' - dependsOn tasks.named(sourceSets.sampleOffTest.classesTaskName) -} - -def sampleOffQualification = registerStrictQualificationTest( - name: 'sampleOffTest', - sourceSet: sourceSets.sampleOffTest, - requiredClasses: [ - 'dev.caskeleton.bootstrap.contract.SampleOffClasspathContractTest' - ], - description: 'Runs the exact no-skip sample-off classpath qualification.') -sampleOffQualification.configure { + description = 'Runs slow full-composition startup and shipped-profile characterization tests.' + testClassesDirs = sourceSets.bootCompositionTest.output.classesDirs + classpath = sourceSets.bootCompositionTest.runtimeClasspath + useJUnitPlatform() + failOnNoDiscoveredTests = true shouldRunAfter tasks.named('test') - systemProperty 'ca.sample.mode', 'off' + maxHeapSize = '1g' + jvmArgs '-Duser.timezone=UTC' } tasks.register('functionalTest', Test) { @@ -276,6 +267,9 @@ tasks.register('functionalTest', Test) { useJUnitPlatform() failOnNoDiscoveredTests = true shouldRunAfter tasks.named('test') + // The expensive memory is in the nested Gradle invocations, not this JUnit worker. Keeping the + // TestKit worker bounded prevents a repository build from reserving another multi-GiB JVM. + maxHeapSize = '768m' jvmArgs '-Duser.timezone=UTC' } @@ -290,25 +284,6 @@ conditionalTransportCompositionQualification.configure { shouldRunAfter tasks.named('test') } -tasks.named('check') { - dependsOn tasks.named('functionalTest') -} - -// feature-developer-experience-contract D2/D7 delegation: the DX entrypoint verifies production -// isolation and the sample-on build contract. sample-off is the separate sampleOffTest task. -tasks.register('bootstrapSampleContract', Test) { - group = 'developer experience' - description = 'Runs the delegated sample production-isolation/build contract for bootstrap.' - testClassesDirs = sourceSets.test.output.classesDirs - classpath = sourceSets.test.runtimeClasspath - useJUnitPlatform() - filter { - includeTestsMatching 'dev.caskeleton.bootstrap.contract.SampleRemovalSmokeContractTest' - } - outputs.upToDateWhen { false } - jvmArgs '-Duser.timezone=UTC' -} - bootJar { mainClass = 'dev.caskeleton.bootstrap.CaSkeletonApplication' } @@ -376,49 +351,9 @@ tasks.register('bootRunH2', org.springframework.boot.gradle.tasks.run.BootRun) { environment 'APP_DATASOURCE_DDL_AUTO', 'create-drop' } -// Wave 0 / spec MSG-INT-002 — the membership gate must judge what actually ships. -// A direct-dependency comparison cannot see a leaf a starter pulls in transitively, so the leaf -// reaches the bootJar while the registry records it as belonging to no runtime at all. -tasks.register('runtimeClasspathManifest') { - description = 'Writes the registry IDs of every project on the resolved runtime classpath.' - group = 'verification' - - File registryFile = file("${rootProject.projectDir}/config/architecture/modules.json") - File manifest = layout.buildDirectory.file('architecture/runtime-project-closure.txt').get().asFile - Provider> projectPaths = provider { - configurations.runtimeClasspath.incoming.resolutionResult.allComponents - .findAll { it.id instanceof org.gradle.api.artifacts.component.ProjectComponentIdentifier } - .collect { (it.id as org.gradle.api.artifacts.component.ProjectComponentIdentifier).projectPath } - .toSet() - } - - inputs.file registryFile - inputs.property 'projectPaths', projectPaths - outputs.file manifest - - doLast { - Map idByGradlePath = new groovy.json.JsonSlurper() - .parse(registryFile) - .modules - .collectEntries { [(it.gradle_path): it.id] } - - List unknown = projectPaths.get().findAll { !idByGradlePath.containsKey(it) }.sort() - if (!unknown.isEmpty()) { - throw new GradleException( - "runtime classpath contains project(s) absent from the architecture registry: " + - "${unknown}. Register the leaf before shipping it.") - } - - manifest.parentFile.mkdirs() - manifest.text = projectPaths.get().collect { idByGradlePath[it] }.sort().join('\n') + '\n' - } -} - -tasks.named('test') { - dependsOn tasks.named('runtimeClasspathManifest') -} +// Runtime project membership is derived directly by verifyRuntimeModuleRegistry from the +// resolved runtimeClasspath. No generated snapshot is maintained here. // The environment configuration contract. Owned here because the question it answers — what must a // deployment of this application be given — is the composition root's, and reached through // `configContractCheck` rather than through every leaf's `check`. -apply from: rootProject.file('gradle/config-contract.gradle') diff --git a/src/app-bootstrap/gradle.lockfile b/src/app-bootstrap/gradle.lockfile index 655a2ba3..8d5803ae 100644 --- a/src/app-bootstrap/gradle.lockfile +++ b/src/app-bootstrap/gradle.lockfile @@ -1,116 +1,100 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -aopalliance:aopalliance:1.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -at.yawk.lz4:lz4-java:1.10.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=compileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -ch.qos.logback:logback-classic:1.5.34=sampleFixture -ch.qos.logback:logback-classic:1.5.38=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.5.34=sampleFixture -ch.qos.logback:logback-core:1.5.38=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.approvaltests:approvaltests-util:31.0.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.approvaltests:approvaltests:31.0.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.ethlo.time:itu:1.14.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-core:2.21.4=sampleFixture -com.fasterxml.jackson.core:jackson-core:2.21.5=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.core:jackson-databind:2.21.4=sampleFixture -com.fasterxml.jackson.core:jackson-databind:2.21.5=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.21.4=sampleFixture -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.4=sampleFixture -com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.21.4=sampleFixture -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.4=sampleFixture -com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.fasterxml.jackson.module:jackson-module-parameter-names:2.21.4=sampleFixture -com.fasterxml.jackson:jackson-bom:2.21.4=sampleFixture -com.fasterxml.jackson:jackson-bom:2.21.5=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.fasterxml:classmate:1.7.3=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.ben-manes.caffeine:caffeine:3.2.4=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.github.docker-java:docker-java-api:3.7.1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.docker-java:docker-java-transport-zerodep:3.7.1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.docker-java:docker-java-transport:3.7.1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.github.f4b6a3:uuid-creator:6.1.1=sampleFixture,testRuntimeClasspath -com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.github.luben:zstd-jni:1.5.6-10=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath +aopalliance:aopalliance:1.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +at.yawk.lz4:lz4-java:1.10.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +biz.aQute.bnd:biz.aQute.bnd.annotation:7.1.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath +ch.qos.logback:logback-classic:1.5.38=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.5.38=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.approvaltests:approvaltests-util:31.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.approvaltests:approvaltests:31.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.ethlo.time:itu:1.14.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-annotations:2.21=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-core:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.core:jackson-databind:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.fasterxml.jackson:jackson-bom:2.21.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.fasterxml:classmate:1.7.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.github.ben-manes.caffeine:caffeine:3.2.4=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.github.docker-java:docker-java-api:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-transport-zerodep:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.github.docker-java:docker-java-transport:3.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.github.luben:zstd-jni:1.5.6-10=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.github.spotbugs:spotbugs-annotations:4.10.2=spotbugs com.github.spotbugs:spotbugs:4.10.2=spotbugs -com.github.stephenc.jcip:jcip-annotations:1.0-1=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +com.github.stephenc.jcip:jcip-annotations:1.0-1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath com.google.android:annotations:4.1.1.4=conditionalTransportTestRuntimeClasspath com.google.api.grpc:proto-google-common-protos:2.41.0=conditionalTransportTestRuntimeClasspath -com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.google.auto:auto-common:1.2.2=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.google.auto:auto-common:1.2.2=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor com.google.code.findbugs:jsr305:3.0.2=checkstyle,conditionalTransportTestRuntimeClasspath,spotbugs -com.google.code.gson:gson:2.13.2=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,spotbugs,testRuntimeClasspath -com.google.errorprone:error_prone_annotation:2.49.0=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.38.0=compileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -com.google.errorprone:error_prone_annotations:2.41.0=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,spotbugs,testRuntimeClasspath +com.google.code.gson:gson:2.13.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,spotbugs,testRuntimeClasspath +com.google.errorprone:error_prone_annotation:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_annotations:2.38.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath +com.google.errorprone:error_prone_annotations:2.41.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,spotbugs,testRuntimeClasspath com.google.errorprone:error_prone_annotations:2.47.0=checkstyle -com.google.errorprone:error_prone_annotations:2.49.0=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_check_api:2.49.0=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_core:2.49.0=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.google.googlejavaformat:google-java-format:1.35.0=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_annotations:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_check_api:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_core:2.49.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.google.googlejavaformat:google-java-format:1.35.0=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor com.google.guava:failureaccess:1.0.2=conditionalTransportTestRuntimeClasspath -com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:failureaccess:1.0.3=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor com.google.guava:guava:33.2.1-jre=conditionalTransportTestRuntimeClasspath -com.google.guava:guava:33.5.0-jre=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:guava:33.5.0-jre=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor com.google.guava:guava:33.6.0-jre=checkstyle -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,conditionalTransportTestRuntimeClasspath,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,conditionalTransportTestRuntimeClasspath,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor com.google.j2objc:j2objc-annotations:2.8=conditionalTransportTestRuntimeClasspath -com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,checkstyle,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor com.google.protobuf:protobuf-java-util:3.25.5=conditionalTransportTestRuntimeClasspath com.google.protobuf:protobuf-java:3.25.5=conditionalTransportTestRuntimeClasspath -com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -com.graphql-java:graphql-java:25.0=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.graphql-java:java-dataloader:6.0.0=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.h2database:h2:2.4.240=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +com.graphql-java:graphql-java:25.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.graphql-java:java-dataloader:6.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.h2database:h2:2.4.240=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0=spotbugsPlugins -com.jayway.jsonpath:json-path:2.10.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.networknt:json-schema-validator:3.0.2=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.nimbusds:content-type:2.3=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.nimbusds:lang-tag:1.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.nimbusds:nimbus-jose-jwt:10.4=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.nimbusds:nimbus-jose-jwt:9.37.4=sampleFixture -com.nimbusds:oauth2-oidc-sdk:11.26.1=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.jayway.jsonpath:json-path:2.10.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.networknt:json-schema-validator:3.0.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.nimbusds:content-type:2.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.nimbusds:lang-tag:1.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.nimbusds:nimbus-jose-jwt:10.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.nimbusds:oauth2-oidc-sdk:11.26.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:13.5.0=checkstyle -com.rabbitmq:amqp-client:5.27.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp-jvm:5.2.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:4.12.0=sampleFixture -com.squareup.okhttp3:okhttp:5.2.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.squareup.okio:okio-jvm:3.16.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.squareup.okio:okio-jvm:3.6.0=sampleFixture -com.squareup.okio:okio:3.16.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.squareup.okio:okio:3.6.0=sampleFixture -com.sun.istack:istack-commons-runtime:4.1.2=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.tngtech.archunit:archunit-junit5-api:1.3.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.tngtech.archunit:archunit-junit5-engine:1.3.0=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -com.tngtech.archunit:archunit-junit5:1.3.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.tngtech.archunit:archunit:1.3.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.vaadin.external.google:android-json:0.0.20131108.vaadin1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.zaxxer:HikariCP:6.3.3=sampleFixture -com.zaxxer:HikariCP:7.0.2=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.rabbitmq:amqp-client:5.27.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp-jvm:5.2.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:5.2.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.squareup.okio:okio-jvm:3.16.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.squareup.okio:okio:3.16.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.sun.istack:istack-commons-runtime:4.1.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-api:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-engine-api:1.3.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5-engine:1.3.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit-junit5:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.tngtech.archunit:archunit:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.vaadin.external.google:android-json:0.0.20131108.vaadin1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.zaxxer:HikariCP:7.0.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath commons-beanutils:commons-beanutils:1.11.0=checkstyle -commons-codec:commons-codec:1.19.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +commons-codec:commons-codec:1.19.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath commons-collections:commons-collections:3.2.2=checkstyle -commons-io:commons-io:2.20.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath commons-io:commons-io:2.21.0=spotbugs -commons-logging:commons-logging:1.3.6=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -eu.rekawek.toxiproxy:toxiproxy-java:2.1.11=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath +commons-logging:commons-logging:1.3.6=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +eu.rekawek.toxiproxy:toxiproxy-java:2.1.11=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath info.picocli:picocli:4.7.7=checkstyle -io.cloudevents:cloudevents-api:4.0.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.cloudevents:cloudevents-core:4.0.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.cdimascio:dotenv-java:3.0.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -io.github.resilience4j:resilience4j-bulkhead:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.resilience4j:resilience4j-circuitbreaker:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.resilience4j:resilience4j-core:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.resilience4j:resilience4j-micrometer:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.resilience4j:resilience4j-ratelimiter:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.resilience4j:resilience4j-retry:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.github.resilience4j:resilience4j-timelimiter:2.2.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath +io.cloudevents:cloudevents-api:4.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.cloudevents:cloudevents-core:4.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.cdimascio:dotenv-java:3.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +io.github.resilience4j:resilience4j-bulkhead:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.resilience4j:resilience4j-circuitbreaker:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.resilience4j:resilience4j-core:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.resilience4j:resilience4j-micrometer:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.resilience4j:resilience4j-ratelimiter:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.resilience4j:resilience4j-retry:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.github.resilience4j:resilience4j-timelimiter:2.2.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.68.1=conditionalTransportTestRuntimeClasspath io.grpc:grpc-context:1.68.1=conditionalTransportTestRuntimeClasspath io.grpc:grpc-core:1.68.1=conditionalTransportTestRuntimeClasspath @@ -120,434 +104,310 @@ io.grpc:grpc-protobuf:1.68.1=conditionalTransportTestRuntimeClasspath io.grpc:grpc-services:1.68.1=conditionalTransportTestRuntimeClasspath io.grpc:grpc-stub:1.68.1=conditionalTransportTestRuntimeClasspath io.grpc:grpc-util:1.68.1=conditionalTransportTestRuntimeClasspath -io.lettuce:lettuce-core:6.8.2.RELEASE=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.micrometer:context-propagation:1.1.4=sampleFixture -io.micrometer:context-propagation:1.2.1=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-commons:1.15.12=sampleFixture -io.micrometer:micrometer-commons:1.16.7=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-core:1.15.12=sampleFixture -io.micrometer:micrometer-core:1.16.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-jakarta9:1.15.12=sampleFixture -io.micrometer:micrometer-jakarta9:1.16.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-observation:1.15.12=sampleFixture -io.micrometer:micrometer-observation:1.16.7=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-registry-prometheus:1.15.12=sampleFixture -io.micrometer:micrometer-registry-prometheus:1.16.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-tracing-bridge-otel:1.5.12=sampleFixture -io.micrometer:micrometer-tracing-bridge-otel:1.6.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.micrometer:micrometer-tracing:1.5.12=sampleFixture -io.micrometer:micrometer-tracing:1.6.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-base:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-classes-quic:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-compression:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-dns:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-http2:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-http3:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-marshalling:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-native-quic:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-protobuf:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec-socks:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-codec:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-common:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-handler-proxy:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-handler:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-resolver-dns-classes-macos:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-resolver-dns-native-macos:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-resolver-dns:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-transport-classes-epoll:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-transport-native-epoll:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.netty:netty-transport:4.2.17.Final=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.opentelemetry.semconv:opentelemetry-semconv:1.32.0=sampleFixture -io.opentelemetry.semconv:opentelemetry-semconv:1.37.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-api:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-api:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-common:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-context:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-context:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-exporter-common:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-exporter-common:1.55.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-exporter-otlp-common:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-exporter-otlp-common:1.55.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-exporter-otlp:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-exporter-otlp:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.55.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-extension-trace-propagators:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-extension-trace-propagators:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-sdk-common:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-sdk-common:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.55.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-sdk-logs:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-sdk-logs:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-sdk-metrics:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-sdk-metrics:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-sdk-trace:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-sdk-trace:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.opentelemetry:opentelemetry-sdk:1.49.0=sampleFixture -io.opentelemetry:opentelemetry-sdk:1.55.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +io.lettuce:lettuce-core:6.8.2.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.micrometer:context-propagation:1.2.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-commons:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-core:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-jakarta9:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-observation:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-registry-prometheus:1.16.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-tracing-bridge-otel:1.6.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.micrometer:micrometer-tracing:1.6.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-base:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-classes-quic:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-compression:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-dns:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-http2:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-http3:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-marshalling:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-native-quic:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-protobuf:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec-socks:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-codec:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-common:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-handler-proxy:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-handler:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-resolver-dns-classes-macos:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-resolver-dns-native-macos:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-resolver-dns:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-transport-classes-epoll:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-transport-native-epoll:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.netty:netty-transport:4.2.17.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opentelemetry.semconv:opentelemetry-semconv:1.37.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-api:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-common:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-context:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-exporter-common:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-exporter-otlp-common:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-exporter-otlp:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-extension-trace-propagators:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-sdk-common:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.55.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-sdk-logs:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-sdk-metrics:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-sdk-trace:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.opentelemetry:opentelemetry-sdk:1.55.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.perfmark:perfmark-api:0.27.0=conditionalTransportTestRuntimeClasspath -io.projectreactor.netty:reactor-netty-core:1.3.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.projectreactor.netty:reactor-netty-http:1.3.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.projectreactor:reactor-core:3.7.19=sampleFixture -io.projectreactor:reactor-core:3.8.7=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.prometheus:prometheus-metrics-config:1.3.10=sampleFixture -io.prometheus:prometheus-metrics-config:1.4.3=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.prometheus:prometheus-metrics-core:1.3.10=sampleFixture -io.prometheus:prometheus-metrics-core:1.4.3=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.prometheus:prometheus-metrics-exposition-formats:1.3.10=sampleFixture -io.prometheus:prometheus-metrics-exposition-formats:1.4.3=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.prometheus:prometheus-metrics-exposition-textformats:1.3.10=sampleFixture -io.prometheus:prometheus-metrics-exposition-textformats:1.4.3=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.prometheus:prometheus-metrics-model:1.3.10=sampleFixture -io.prometheus:prometheus-metrics-model:1.4.3=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.prometheus:prometheus-metrics-tracer-common:1.3.10=sampleFixture -io.prometheus:prometheus-metrics-tracer-common:1.4.3=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.smallrye:jandex:3.2.0=sampleFixture -io.smallrye:jandex:3.3.2=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-annotations-jakarta:2.2.29=sampleFixture -io.swagger.core.v3:swagger-annotations-jakarta:2.2.38=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-core-jakarta:2.2.29=sampleFixture -io.swagger.core.v3:swagger-core-jakarta:2.2.38=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -io.swagger.core.v3:swagger-models-jakarta:2.2.29=sampleFixture -io.swagger.core.v3:swagger-models-jakarta:2.2.38=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -jakarta.activation:jakarta.activation-api:2.1.4=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.annotation:jakarta.annotation-api:2.1.1=sampleFixture -jakarta.annotation:jakarta.annotation-api:3.0.0=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.inject:jakarta.inject-api:2.0.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -jakarta.mail:jakarta.mail-api:2.1.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -jakarta.persistence:jakarta.persistence-api:3.1.0=sampleFixture -jakarta.persistence:jakarta.persistence-api:3.2.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.servlet:jakarta.servlet-api:6.1.0=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -jakarta.transaction:jakarta.transaction-api:2.0.1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.validation:jakarta.validation-api:3.0.2=sampleFixture -jakarta.validation:jakarta.validation-api:3.1.1=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jakarta.websocket:jakarta.websocket-api:2.2.0=integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -jakarta.ws.rs:jakarta.ws.rs-api:4.0.0=integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -jakarta.xml.bind:jakarta.xml.bind-api:4.0.5=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -javax.inject:javax.inject:1=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor +io.projectreactor.netty:reactor-netty-core:1.3.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.projectreactor.netty:reactor-netty-http:1.3.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.projectreactor:reactor-core:3.8.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-config:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-core:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-exposition-formats:1.4.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-exposition-textformats:1.4.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-model:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.prometheus:prometheus-metrics-tracer-common:1.4.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.smallrye:jandex:3.3.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-annotations-jakarta:2.2.38=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-core-jakarta:2.2.38=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +io.swagger.core.v3:swagger-models-jakarta:2.2.38=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +jakarta.activation:jakarta.activation-api:2.1.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +jakarta.annotation:jakarta.annotation-api:3.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +jakarta.inject:jakarta.inject-api:2.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +jakarta.mail:jakarta.mail-api:2.1.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +jakarta.persistence:jakarta.persistence-api:3.2.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +jakarta.servlet:jakarta.servlet-api:6.1.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +jakarta.transaction:jakarta.transaction-api:2.0.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +jakarta.validation:jakarta.validation-api:3.1.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +jakarta.websocket:jakarta.websocket-api:2.2.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath +jakarta.ws.rs:jakarta.ws.rs-api:4.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath +jakarta.xml.bind:jakarta.xml.bind-api:4.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +javax.inject:javax.inject:1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor jaxen:jaxen:2.0.6=spotbugs -me.paulschwarz:spring-dotenv:4.0.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy-agent:1.17.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy:1.17.8=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -net.java.dev.jna:jna:5.18.1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -net.logstash.logback:logstash-logback-encoder:8.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -net.minidev:accessors-smart:2.6.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -net.minidev:json-smart:2.6.0=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +me.paulschwarz:spring-dotenv:4.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.17.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.17.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna:5.18.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +net.logstash.logback:logstash-logback-encoder:8.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.minidev:accessors-smart:2.6.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.minidev:json-smart:2.6.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath net.sf.saxon:Saxon-HE:12.9=checkstyle,spotbugs -ognl:ognl:3.3.4=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.antlr:antlr4-runtime:4.13.2=checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +ognl:ognl:3.3.4=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.antlr:antlr4-runtime:4.13.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.bcel:bcel:6.12.0=spotbugs -org.apache.commons:commons-compress:1.28.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.commons:commons-lang3:3.20.0=checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +org.apache.commons:commons-compress:1.28.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.commons:commons-lang3:3.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,checkstyle,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath org.apache.commons:commons-text:1.15.0=spotbugs org.apache.commons:commons-text:1.3=checkstyle -org.apache.groovy:groovy-bom:5.0.8=integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.apache.groovy:groovy:5.0.8=integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.apache.httpcomponents.client5:httpclient5:5.5.2=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.apache.httpcomponents.core5:httpcore5-h2:5.3.6=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.apache.httpcomponents.core5:httpcore5:5.3.6=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.apache.httpcomponents:httpclient:4.5.13=checkstyle,testRuntimeClasspath -org.apache.httpcomponents:httpcore:4.4.16=checkstyle,testRuntimeClasspath -org.apache.kafka:kafka-clients:4.1.2=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.logging.log4j:log4j-api:2.24.3=sampleFixture -org.apache.logging.log4j:log4j-api:2.25.5=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +org.apache.groovy:groovy-bom:5.0.8=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.apache.groovy:groovy:5.0.8=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.apache.httpcomponents.client5:httpclient5:5.5.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.apache.httpcomponents.core5:httpcore5-h2:5.3.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.apache.httpcomponents.core5:httpcore5:5.3.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.apache.httpcomponents:httpclient:4.5.13=checkstyle +org.apache.httpcomponents:httpcore:4.4.16=checkstyle +org.apache.kafka:kafka-clients:4.1.2=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.logging.log4j:log4j-api:2.25.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath org.apache.logging.log4j:log4j-core:2.25.5=spotbugs -org.apache.logging.log4j:log4j-to-slf4j:2.24.3=sampleFixture -org.apache.logging.log4j:log4j-to-slf4j:2.25.5=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.logging.log4j:log4j-to-slf4j:2.25.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.maven.doxia:doxia-core:1.12.0=checkstyle org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle -org.apache.tomcat.embed:tomcat-embed-core:10.1.55=sampleFixture -org.apache.tomcat.embed:tomcat-embed-core:11.0.24=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.tomcat.embed:tomcat-embed-el:10.1.55=sampleFixture -org.apache.tomcat.embed:tomcat-embed-el:11.0.24=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.apache.tomcat.embed:tomcat-embed-websocket:10.1.55=sampleFixture -org.apache.tomcat.embed:tomcat-embed-websocket:11.0.24=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.tomcat.embed:tomcat-embed-core:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.tomcat.embed:tomcat-embed-el:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apache.tomcat.embed:tomcat-embed-websocket:11.0.24=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.apache.xbean:xbean-reflect:3.7=checkstyle -org.apiguardian:apiguardian-api:1.1.2=conditionalTransportTestCompileClasspath,functionalTestCompileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.aspectj:aspectjweaver:1.9.25.1=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.assertj:assertj-core:3.27.7=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.attoparser:attoparser:2.0.7.RELEASE=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.awaitility:awaitility:4.3.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.apiguardian:apiguardian-api:1.1.2=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,conditionalTransportTestCompileClasspath,functionalTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.aspectj:aspectjweaver:1.9.25.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.assertj:assertj-core:3.27.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.attoparser:attoparser:2.0.7.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.awaitility:awaitility:4.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.42.0=conditionalTransportTestRuntimeClasspath -org.checkerframework:checker-qual:3.55.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath +org.checkerframework:checker-qual:3.55.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.codehaus.mojo:animal-sniffer-annotations:1.24=conditionalTransportTestRuntimeClasspath org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.dom4j:dom4j:2.2.0=spotbugs -org.eclipse.angus:angus-activation:2.0.3=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.angus:angus-mail:2.0.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty.compression:jetty-compression-common:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty.compression:jetty-compression-gzip:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-alpn-client:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-client:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-http:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-io:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.eclipse.jetty:jetty-util:12.1.12=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:11.14.1=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:11.7.2=sampleFixture -org.flywaydb:flyway-database-postgresql:11.14.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:11.7.2=sampleFixture -org.glassfish.jaxb:jaxb-core:4.0.9=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.glassfish.jaxb:jaxb-runtime:4.0.9=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.glassfish.jaxb:txw2:4.0.9=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.hamcrest:hamcrest:3.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.hdrhistogram:HdrHistogram:2.2.2=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.hibernate.common:hibernate-commons-annotations:7.0.3.Final=sampleFixture -org.hibernate.models:hibernate-models:1.0.1=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.hibernate.orm:hibernate-core:6.6.53.Final=sampleFixture -org.hibernate.orm:hibernate-core:7.2.24.Final=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.hibernate.orm:hibernate-envers:7.2.24.Final=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.hibernate.validator:hibernate-validator:8.0.3.Final=sampleFixture -org.hibernate.validator:hibernate-validator:9.0.1.Final=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.eclipse.angus:angus-activation:2.0.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.angus:angus-mail:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty.compression:jetty-compression-common:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty.compression:jetty-compression-gzip:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-alpn-client:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-client:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-http:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-io:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.eclipse.jetty:jetty-util:12.1.12=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:11.14.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:11.14.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.glassfish.jaxb:jaxb-core:4.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.glassfish.jaxb:jaxb-runtime:4.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.glassfish.jaxb:txw2:4.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.hamcrest:hamcrest:3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.hdrhistogram:HdrHistogram:2.2.2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.hibernate.models:hibernate-models:1.0.1=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.hibernate.orm:hibernate-core:7.2.24.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.hibernate.orm:hibernate-envers:7.2.24.Final=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +org.hibernate.validator:hibernate-validator:9.0.1.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.javassist:javassist:3.28.0-GA=checkstyle -org.javassist:javassist:3.29.0-GA=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.jboss.logging:jboss-logging:3.6.3.Final=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.25=sampleFixture -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.25=sampleFixture -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25=sampleFixture -org.jetbrains.kotlin:kotlin-stdlib:1.9.25=sampleFixture -org.jetbrains.kotlin:kotlin-stdlib:2.2.21=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.jetbrains:annotations:13.0=productionRuntimeClasspath,runtimeClasspath,sampleFixture -org.jetbrains:annotations:17.0.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jspecify:jspecify:1.0.1=annotationProcessor,checkstyle,compileClasspath,conditionalTransportTestAnnotationProcessor,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestAnnotationProcessor,functionalTestCompileClasspath,integrationTestAnnotationProcessor,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestAnnotationProcessor,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:6.0.3=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:6.0.3=conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:6.0.3=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter:6.0.3=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:6.0.3=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:6.0.3=conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-launcher:6.0.3=conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-testkit:6.0.3=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit:junit-bom:6.0.3=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.javassist:javassist:3.29.0-GA=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.jboss.logging:jboss-logging:3.6.3.Final=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:2.2.21=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.jetbrains:annotations:13.0=productionRuntimeClasspath,runtimeClasspath +org.jetbrains:annotations:17.0.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.jspecify:jspecify:1.0.1=annotationProcessor,architectureTestAnnotationProcessor,architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestAnnotationProcessor,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,checkstyle,compileClasspath,conditionalTransportTestAnnotationProcessor,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestAnnotationProcessor,functionalTestCompileClasspath,integrationTestAnnotationProcessor,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:6.0.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-testkit:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit:junit-bom:6.0.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.junit:junit-bom:6.1.0=spotbugs -org.latencyutils:LatencyUtils:2.0.3=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.mockito:mockito-core:5.20.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,mockitoAgent,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.mockito:mockito-junit-jupiter:5.20.0=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.mongodb:bson-record-codec:5.6.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.mongodb:bson:5.6.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.mongodb:mongodb-driver-core:5.6.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.mongodb:mongodb-driver-sync:5.6.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.objenesis:objenesis:3.3=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.openapitools:jackson-databind-nullable:0.2.6=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.opentest4j:opentest4j:1.3.0=conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.osgi:org.osgi.annotation.bundle:2.0.0=compileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.osgi:org.osgi.annotation.versioning:1.1.2=compileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.osgi:org.osgi.resource:1.0.0=compileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.osgi:org.osgi.service.serviceloader:1.0.0=compileClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath +org.latencyutils:LatencyUtils:2.0.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.mockito:mockito-core:5.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,mockitoAgent,testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-junit-jupiter:5.20.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.mongodb:bson-record-codec:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.mongodb:bson:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.mongodb:mongodb-driver-core:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.mongodb:mongodb-driver-sync:5.6.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.objenesis:objenesis:3.3=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +org.openapitools:jackson-databind-nullable:0.2.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,functionalTestCompileClasspath,functionalTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.osgi:org.osgi.annotation.bundle:2.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.osgi:org.osgi.annotation.versioning:1.1.2=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.osgi:org.osgi.resource:1.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.osgi:org.osgi.service.serviceloader:1.0.0=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,compileClasspath,integrationTestCompileClasspath,testCompileClasspath org.ow2.asm:asm-analysis:9.10.1=spotbugs org.ow2.asm:asm-commons:9.10.1=spotbugs org.ow2.asm:asm-tree:9.10.1=spotbugs org.ow2.asm:asm-util:9.10.1=spotbugs org.ow2.asm:asm:9.10.1=spotbugs -org.ow2.asm:asm:9.7.1=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.pcollections:pcollections:4.0.1=annotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,sampleOffTestAnnotationProcessor,testAnnotationProcessor -org.postgresql:postgresql:42.7.11=sampleFixture -org.postgresql:postgresql:42.7.13=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.reactivestreams:reactive-streams:1.0.4=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.7.1=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.pcollections:pcollections:4.0.1=annotationProcessor,architectureTestAnnotationProcessor,bootCompositionTestAnnotationProcessor,conditionalTransportTestAnnotationProcessor,functionalTestAnnotationProcessor,integrationTestAnnotationProcessor,testAnnotationProcessor +org.postgresql:postgresql:42.7.13=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.reactivestreams:reactive-streams:1.0.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.reflections:reflections:0.10.2=checkstyle -org.rnorth.duct-tape:duct-tape:1.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.skyscreamer:jsonassert:1.5.3=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:2.0.18=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:2.0.18=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleFixture,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath +org.rnorth.duct-tape:duct-tape:1.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.skyscreamer:jsonassert:1.5.3=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:2.0.18=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:2.0.18=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,spotbugs,spotbugsSlf4j,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j org.slf4j:slf4j-simple:2.0.18=checkstyle -org.springdoc:springdoc-openapi-starter-common:2.8.6=sampleFixture -org.springdoc:springdoc-openapi-starter-common:3.0.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.6=sampleFixture -org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.0=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.amqp:spring-amqp:4.0.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.amqp:spring-rabbit:4.0.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-actuator-autoconfigure:3.5.16=sampleFixture -org.springframework.boot:spring-boot-actuator-autoconfigure:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-actuator:3.5.16=sampleFixture -org.springframework.boot:spring-boot-actuator:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-autoconfigure:3.5.16=sampleFixture -org.springframework.boot:spring-boot-autoconfigure:4.0.8=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-common:3.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.0=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.amqp:spring-amqp:4.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.amqp:spring-rabbit:4.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-actuator-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-actuator:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-configuration-processor:4.0.8=annotationProcessor -org.springframework.boot:spring-boot-data-commons:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-data-jpa-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-data-jpa:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-data-mongodb:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-flyway:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-graphql:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-health:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-hibernate:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-http-converter:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-jackson:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-jdbc-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-jdbc:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-jpa-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-jpa:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-mail:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-micrometer-metrics:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-micrometer-observation:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-mongodb:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-persistence:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-reactor:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-resttestclient:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-security-oauth2-resource-server:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-security:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-servlet:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-sql:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-actuator:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-actuator:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-data-jpa:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-data-jpa:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-data-mongodb:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-flyway:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-graphql:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-jackson-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-jackson:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-jdbc:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-jdbc:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-json:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-json:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-logging:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-logging:4.0.8=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-mail:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-mongodb:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-oauth2-resource-server:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-oauth2-resource-server:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-security:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-security:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-tomcat-runtime:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-tomcat:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-tomcat:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-validation:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-validation:4.0.8=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-web:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter-web:4.0.8=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-webmvc-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-starter-webmvc:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-data-commons:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-data-jpa-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-data-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-data-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-flyway:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-graphql:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-health:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-hibernate:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-http-converter:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-jackson:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-jdbc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-jdbc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-jpa-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-mail:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-micrometer-metrics:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-micrometer-observation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-persistence:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-reactor:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-resttestclient:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-security-oauth2-resource-server:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-security:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-servlet:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-sql:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-actuator:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-data-jpa:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-data-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-flyway:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-graphql:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-jackson-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-jackson:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-jdbc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-json:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-logging:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-mail:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-micrometer-metrics:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-mongodb:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-oauth2-resource-server:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-security:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-tomcat-runtime:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-tomcat:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-validation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-web:4.0.8=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-webmvc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter-webmvc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-starter-websocket:4.0.8=conditionalTransportTestRuntimeClasspath -org.springframework.boot:spring-boot-starter:3.5.16=sampleFixture -org.springframework.boot:spring-boot-starter:4.0.8=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-test-autoconfigure:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-tomcat:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-transaction:4.0.8=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-validation:4.0.8=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-web-server:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-webmvc-test:4.0.8=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.boot:spring-boot-webmvc:4.0.8=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-starter:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-test-autoconfigure:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-tomcat:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-transaction:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-validation:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-web-server:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-webmvc-test:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot-webmvc:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.springframework.boot:spring-boot-websocket:4.0.8=conditionalTransportTestRuntimeClasspath -org.springframework.boot:spring-boot:3.5.16=sampleFixture -org.springframework.boot:spring-boot:4.0.8=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.cloud:spring-cloud-context:4.1.4=integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.springframework.data:spring-data-commons:3.5.13=sampleFixture -org.springframework.data:spring-data-commons:4.0.7=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.data:spring-data-jpa:3.5.13=sampleFixture -org.springframework.data:spring-data-jpa:4.0.7=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.data:spring-data-mongodb:5.0.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.graphql:spring-graphql:2.0.5=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.integration:spring-integration-core:6.5.10=sampleFixture -org.springframework.integration:spring-integration-core:7.0.6=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.integration:spring-integration-jdbc:6.5.10=sampleFixture -org.springframework.integration:spring-integration-jdbc:7.0.6=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.kafka:spring-kafka:4.0.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.retry:spring-retry:2.0.13=sampleFixture -org.springframework.security:spring-security-config:6.5.11=sampleFixture -org.springframework.security:spring-security-config:7.0.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-core:6.5.11=sampleFixture -org.springframework.security:spring-security-core:7.0.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-crypto:6.5.11=sampleFixture -org.springframework.security:spring-security-crypto:7.0.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-client:7.0.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-core:6.5.11=sampleFixture -org.springframework.security:spring-security-oauth2-core:7.0.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-jose:6.5.11=sampleFixture -org.springframework.security:spring-security-oauth2-jose:7.0.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.security:spring-security-oauth2-resource-server:6.5.11=sampleFixture -org.springframework.security:spring-security-oauth2-resource-server:7.0.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework.security:spring-security-test:7.0.7=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.security:spring-security-web:6.5.11=sampleFixture -org.springframework.security:spring-security-web:7.0.7=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework.session:spring-session-core:4.0.5=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework:spring-aop:6.2.19=sampleFixture -org.springframework:spring-aop:7.0.9=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-aspects:6.2.19=sampleFixture -org.springframework:spring-aspects:7.0.9=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-beans:6.2.19=sampleFixture -org.springframework:spring-beans:7.0.9=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-context-support:7.0.9=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework:spring-context:6.2.19=sampleFixture -org.springframework:spring-context:7.0.9=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-core:6.2.19=sampleFixture -org.springframework:spring-core:7.0.9=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-expression:6.2.19=sampleFixture -org.springframework:spring-expression:7.0.9=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-jcl:6.2.19=sampleFixture -org.springframework:spring-jdbc:6.2.19=sampleFixture -org.springframework:spring-jdbc:7.0.9=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-messaging:6.2.19=sampleFixture -org.springframework:spring-messaging:7.0.9=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework:spring-orm:6.2.19=sampleFixture -org.springframework:spring-orm:7.0.9=integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-test:7.0.9=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-tx:6.2.19=sampleFixture -org.springframework:spring-tx:7.0.9=compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-web:6.2.19=sampleFixture -org.springframework:spring-web:7.0.9=compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-webflux:7.0.9=conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.springframework:spring-webmvc:6.2.19=sampleFixture -org.springframework:spring-webmvc:7.0.9=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.springframework:spring-websocket:7.0.9=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,sampleOffTestCompileClasspath,testCompileClasspath -org.testcontainers:testcontainers-database-commons:2.0.5=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.testcontainers:testcontainers-jdbc:2.0.5=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.testcontainers:testcontainers-junit-jupiter:2.0.5=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.testcontainers:testcontainers-postgresql:2.0.5=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.testcontainers:testcontainers-toxiproxy:2.0.5=integrationTestRuntimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.testcontainers:testcontainers:2.0.5=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.thymeleaf:thymeleaf:3.1.5.RELEASE=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.unbescape:unbescape:1.1.6.RELEASE=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -org.xerial.snappy:snappy-java:1.1.10.7=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath +org.springframework.boot:spring-boot:4.0.8=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.cloud:spring-cloud-context:4.1.4=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,integrationTestCompileClasspath,testCompileClasspath +org.springframework.data:spring-data-commons:4.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.data:spring-data-jpa:4.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.data:spring-data-mongodb:5.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.graphql:spring-graphql:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.integration:spring-integration-core:7.0.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.integration:spring-integration-jdbc:7.0.6=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.kafka:spring-kafka:4.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-config:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-core:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-crypto:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-client:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-core:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-jose:7.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-oauth2-resource-server:7.0.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework.security:spring-security-test:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.security:spring-security-web:7.0.7=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework.session:spring-session-core:4.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework:spring-aop:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-aspects:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-beans:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-context-support:7.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework:spring-context:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-core:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-expression:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-jdbc:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-messaging:7.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework:spring-orm:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-test:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-tx:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-web:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-webflux:7.0.9=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.springframework:spring-webmvc:7.0.9=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.springframework:spring-websocket:7.0.9=architectureTestCompileClasspath,bootCompositionTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,testCompileClasspath +org.testcontainers:testcontainers-database-commons:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.testcontainers:testcontainers-jdbc:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.testcontainers:testcontainers-junit-jupiter:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +org.testcontainers:testcontainers-postgresql:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.testcontainers:testcontainers-toxiproxy:2.0.5=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,testRuntimeClasspath +org.testcontainers:testcontainers:2.0.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.thymeleaf:thymeleaf:3.1.5.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.unbescape:unbescape:1.1.6.RELEASE=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +org.xerial.snappy:snappy-java:1.1.10.7=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.xmlresolver:xmlresolver:5.3.3=checkstyle,spotbugs -org.xmlunit:xmlunit-core:2.10.4=integrationTestCompileClasspath,integrationTestRuntimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.yaml:snakeyaml:2.4=sampleFixture -org.yaml:snakeyaml:2.5=compileClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -redis.clients.authentication:redis-authx-core:0.1.1-beta2=integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestRuntimeClasspath,testRuntimeClasspath -software.amazon.awssdk:annotations:2.30.0=testRuntimeClasspath -software.amazon.awssdk:apache-client:2.30.0=testRuntimeClasspath -software.amazon.awssdk:arns:2.30.0=testRuntimeClasspath -software.amazon.awssdk:auth:2.30.0=testRuntimeClasspath -software.amazon.awssdk:aws-core:2.30.0=testRuntimeClasspath -software.amazon.awssdk:aws-query-protocol:2.30.0=testRuntimeClasspath -software.amazon.awssdk:aws-xml-protocol:2.30.0=testRuntimeClasspath -software.amazon.awssdk:checksums-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:checksums:2.30.0=testRuntimeClasspath -software.amazon.awssdk:crt-core:2.30.0=testRuntimeClasspath -software.amazon.awssdk:endpoints-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:http-auth-aws-eventstream:2.30.0=testRuntimeClasspath -software.amazon.awssdk:http-auth-aws:2.30.0=testRuntimeClasspath -software.amazon.awssdk:http-auth-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:http-auth:2.30.0=testRuntimeClasspath -software.amazon.awssdk:http-client-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:identity-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:json-utils:2.30.0=testRuntimeClasspath -software.amazon.awssdk:metrics-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:netty-nio-client:2.30.0=testRuntimeClasspath -software.amazon.awssdk:profiles:2.30.0=testRuntimeClasspath -software.amazon.awssdk:protocol-core:2.30.0=testRuntimeClasspath -software.amazon.awssdk:regions:2.30.0=testRuntimeClasspath -software.amazon.awssdk:retries-spi:2.30.0=testRuntimeClasspath -software.amazon.awssdk:retries:2.30.0=testRuntimeClasspath -software.amazon.awssdk:s3:2.30.0=testRuntimeClasspath -software.amazon.awssdk:sdk-core:2.30.0=testRuntimeClasspath -software.amazon.awssdk:third-party-jackson-core:2.30.0=testRuntimeClasspath -software.amazon.awssdk:utils:2.30.0=testRuntimeClasspath -software.amazon.eventstream:eventstream:1.0.1=testRuntimeClasspath -tools.jackson.core:jackson-core:3.1.5=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -tools.jackson.core:jackson-databind:3.1.5=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -tools.jackson:jackson-bom:3.1.5=conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,sampleOffTestCompileClasspath,sampleOffTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.xmlunit:xmlunit-core:2.10.4=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.yaml:snakeyaml:2.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,compileClasspath,conditionalTransportTestCompileClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +redis.clients.authentication:redis-authx-core:0.1.1-beta2=architectureTestRuntimeClasspath,bootCompositionTestRuntimeClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testRuntimeClasspath +tools.jackson.core:jackson-core:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +tools.jackson.core:jackson-databind:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +tools.jackson:jackson-bom:3.1.5=architectureTestCompileClasspath,architectureTestRuntimeClasspath,bootCompositionTestCompileClasspath,bootCompositionTestRuntimeClasspath,conditionalTransportTestRuntimeClasspath,integrationTestCompileClasspath,integrationTestRuntimeClasspath,productionRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath empty=developmentOnly,testAndDevelopmentOnly diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/AdapterBoundaryArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/AdapterBoundaryArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/AdapterBoundaryArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/AdapterBoundaryArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ApplicationBoundaryArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ApplicationBoundaryArchitectureTest.java similarity index 96% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ApplicationBoundaryArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ApplicationBoundaryArchitectureTest.java index a0008fac..db0b306e 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ApplicationBoundaryArchitectureTest.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ApplicationBoundaryArchitectureTest.java @@ -288,38 +288,6 @@ class ApplicationBoundaryArchitectureTest { + "outbound adapters") .allowEmptyShould(true); - @ArchTest - static final ArchRule SAMPLE_APPLICATION_HAS_NO_SLF4J = - noClasses() - .that() - .resideInAPackage("dev.caskeleton.sample.portfolio.application..") - .should() - .dependOnClassesThat() - .resideInAnyPackage("org.slf4j..") - .as( - "SAMPLE_APPLICATION_HAS_NO_SLF4J: sample application collaborators read " - + "correlation context through application-core CorrelationIdPort; inbound " - + "adapters own MDC") - .allowEmptyShould(true); - - @ArchTest - static final ArchRule SAMPLE_APPLICATION_LEGACY_OBJECT_STORAGE_IMPORT_IS_FROZEN = - noClasses() - .that() - .resideInAPackage("dev.caskeleton.sample.portfolio.application..") - .and() - .doNotHaveFullyQualifiedName( - "dev.caskeleton.sample.portfolio.application.poster.UploadPosterImageUseCase") - .should() - .dependOnClassesThat() - .resideInAPackage("dev.caskeleton.application.storage") - .as( - "Only the characterized UploadPosterImageUseCase may temporarily import the legacy " - + "ObjectStoragePort/StoredObject contract. New sample business code must use " - + "the semantic objectstorage ports. Task 23 moves compatibility into the exact " - + "legacy slice; Task 24 separately reviews the administrative migration seam.") - .allowEmptyShould(true); - @ArchTest static final ArchRule APPLICATION_DOES_NOT_USE_SPRING_TRANSACTIONAL_ANNOTATION = noClasses() diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java similarity index 97% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java index 8dadbe46..195d5b49 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ArchRuleDiscoveryContractTest.java @@ -146,13 +146,10 @@ class ArchRuleDiscoveryContractTest { "PERSISTENCE_ENTITIES_STAY_INSIDE_A_PERSISTENCE_ADAPTER", "PERSISTENCE_RDBMS_ENTITIES_DO_NOT_PIN_VENDOR_COLUMN_DEFINITIONS", "PERSISTENCE_RDBMS_STAYS_NEUTRAL_OF_H2", - "PRODUCTION_CODE_DOES_NOT_DEPEND_ON_SAMPLE_PORTFOLIO", "PRODUCTION_CODE_DOES_NOT_DEPEND_ON_TEST_FIXTURES", "QUERY_PORTS_DO_NOT_LEAK_DOMAIN_JPA_OR_WEB_TYPES", "READ_ONLY_USE_CASES_DO_NOT_CALL_REPOSITORY_WRITE_METHODS", "REQUEST_DTOS_DO_NOT_SILENCE_UNKNOWN_FIELDS", - "SAMPLE_APPLICATION_HAS_NO_SLF4J", - "SAMPLE_APPLICATION_LEGACY_OBJECT_STORAGE_IMPORT_IS_FROZEN", "SHARED_CONTRACT_CONTAINS_ONLY_OPERATIONAL_CONTRACT_PACKAGES", "STARTUP_FAILURES_ARE_RAISED_THROUGH_THE_TYPED_FACTORY", "THROWABLES_ARE_NAMED_EXCEPTION", @@ -202,7 +199,8 @@ class ArchRuleDiscoveryContractTest { Map rules = new LinkedHashMap<>(); Path architecture = repositoryRoot() - .resolve("src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture"); + .resolve( + "src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture"); try (Stream sources = Files.walk(architecture)) { for (Path source : sources.filter(p -> p.toString().endsWith(".java")).sorted().toList()) { String text = Files.readString(source); diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchitectureViolationFixtureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ArchitectureViolationFixtureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchitectureViolationFixtureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ArchitectureViolationFixtureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureRuleSupport.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureRuleSupport.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureRuleSupport.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureRuleSupport.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CompositionRootBoundaryArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/CompositionRootBoundaryArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CompositionRootBoundaryArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/CompositionRootBoundaryArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ContractSuiteIsolationArchTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ContractSuiteIsolationArchTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ContractSuiteIsolationArchTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ContractSuiteIsolationArchTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DisabledAdapterArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DisabledAdapterArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DisabledAdapterArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DisabledAdapterArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DomainFeatureOnboardingContractTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DomainFeatureOnboardingContractTest.java similarity index 94% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DomainFeatureOnboardingContractTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DomainFeatureOnboardingContractTest.java index 7718fbc7..6ef69a32 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DomainFeatureOnboardingContractTest.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DomainFeatureOnboardingContractTest.java @@ -7,7 +7,7 @@ import com.tngtech.archunit.core.importer.ClassFileImporter; import java.util.List; import org.junit.jupiter.api.Test; -/** Contract dry-run for adding a real domain feature without importing sample-portfolio. */ +/** Contract dry-run for adding a real domain feature against the production boundaries. */ class DomainFeatureOnboardingContractTest { private static final String ROOT = "dev.caskeleton.onboarding"; @@ -102,11 +102,6 @@ class DomainFeatureOnboardingContractTest { .evaluate(ONBOARDING_CLASSES) .hasViolation()) .isFalse(); - assertThat( - TypeBoundaryArchitectureTest.PRODUCTION_CODE_DOES_NOT_DEPEND_ON_SAMPLE_PORTFOLIO - .evaluate(ONBOARDING_CLASSES) - .hasViolation()) - .isFalse(); assertThat(getClass().getResource("/db/onboarding-migration/V999__feature_aggregate.sql")) .as("write onboarding dry-run must include a persistence migration artifact") .isNotNull(); diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DomainPurityArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DomainPurityArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/DomainPurityArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/DomainPurityArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/FileserverUseCaseContractDeviationTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/FileserverUseCaseContractDeviationTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/FileserverUseCaseContractDeviationTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/FileserverUseCaseContractDeviationTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ForbiddenApiArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ForbiddenApiArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ForbiddenApiArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ForbiddenApiArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/GraphQlInboundOwnershipBoundaryTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/GraphQlInboundOwnershipBoundaryTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/GraphQlInboundOwnershipBoundaryTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/GraphQlInboundOwnershipBoundaryTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/JpaProductionArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/JpaProductionArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/JpaProductionArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/JpaProductionArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/MongoRawAccessBoundaryTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/MongoRawAccessBoundaryTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/MongoRawAccessBoundaryTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/MongoRawAccessBoundaryTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/NamingConventionTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/NamingConventionTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/NamingConventionTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/NamingConventionTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/NotificationArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/NotificationArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/NotificationArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/NotificationArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ProductionClassImportOption.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ProductionClassImportOption.java similarity index 79% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ProductionClassImportOption.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ProductionClassImportOption.java index ee17badc..5e756bfa 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ProductionClassImportOption.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/ProductionClassImportOption.java @@ -3,7 +3,7 @@ package dev.caskeleton.bootstrap.architecture; import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.core.importer.Location; -/** Excludes both Gradle's default test output and the sample-off custom test output. */ +/** Excludes every test-only output from production architecture scans. */ public final class ProductionClassImportOption implements ImportOption { private final ImportOption defaultTestExclusion = new ImportOption.DoNotIncludeTests(); @@ -11,7 +11,11 @@ public final class ProductionClassImportOption implements ImportOption { @Override public boolean includes(Location location) { return defaultTestExclusion.includes(location) - && !location.contains("/sampleOffTest/") + // Gradle/ArchUnit's default test exclusion knows `test`, not repository-defined source + // sets. + // Keep production scans source-set neutral as verification lanes are split physically. + && !location.contains("/architectureTest/") + && !location.contains("/bootCompositionTest/") // The JPA testkit is on this module's test classpath so the production architecture suite // can use its rule pack. Its fixtures exist to be violations — an entity with a // varchar(255) id, a controller returning an entity — so importing them would have the diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/StartupFailureContractArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/StartupFailureContractArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/StartupFailureContractArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/StartupFailureContractArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/TestTaxonomyArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/TestTaxonomyArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/TestTaxonomyArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/TestTaxonomyArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/TypeBoundaryArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/TypeBoundaryArchitectureTest.java similarity index 98% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/TypeBoundaryArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/TypeBoundaryArchitectureTest.java index 4177adf6..2f9fd686 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/TypeBoundaryArchitectureTest.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/TypeBoundaryArchitectureTest.java @@ -65,9 +65,7 @@ class TypeBoundaryArchitectureTest { classes() .that(ARE_PERSISTENCE_ENTITIES) .should() - .resideInAnyPackage( - "..adapter.outbound.persistence..", - "..sample.portfolio.adapter.outbound.persistence..") + .resideInAnyPackage("..adapter.outbound.persistence..") .as( "a type the ORM maps belongs to a persistence adapter whatever it is called and " + "wherever it is filed (NTF-022 semantic selection)") @@ -93,15 +91,6 @@ class TypeBoundaryArchitectureTest { .resideInAnyPackage("..adapter.inbound.web..", "..bootstrap..") .allowEmptyShould(true); - @ArchTest - static final ArchRule PRODUCTION_CODE_DOES_NOT_DEPEND_ON_SAMPLE_PORTFOLIO = - noClasses() - .that() - .resideOutsideOfPackage("..sample.portfolio..") - .should() - .dependOnClassesThat() - .resideInAPackage("..sample.portfolio.."); - @ArchTest static final ArchRule PRODUCTION_CODE_DOES_NOT_DEPEND_ON_TEST_FIXTURES = noClasses() diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/WebProductionArchitectureTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/WebProductionArchitectureTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/WebProductionArchitectureTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/WebProductionArchitectureTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/application/CleanProjectionQueryPort.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/application/CleanProjectionQueryPort.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/application/CleanProjectionQueryPort.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/application/CleanProjectionQueryPort.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/application/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/application/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/application/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/application/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/SampleFeatureUsingContractFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/SampleFeatureUsingContractFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/SampleFeatureUsingContractFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/SampleFeatureUsingContractFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/contract/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/SampleFeatureFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/SampleFeatureFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/SampleFeatureFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/SampleFeatureFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/contractisolation/features/sample/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/slice/SingleSliceWebMvcFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/slice/SingleSliceWebMvcFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/slice/SingleSliceWebMvcFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/slice/SingleSliceWebMvcFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/slice/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/slice/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/slice/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/slice/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/StreamingResponseBodyAllowedFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/StreamingResponseBodyAllowedFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/StreamingResponseBodyAllowedFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/StreamingResponseBodyAllowedFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/allowed/streaming/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/fixtures/application/RootWriteTransactionBoundaryUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/fixtures/application/RootWriteTransactionBoundaryUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/fixtures/application/RootWriteTransactionBoundaryUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/fixtures/application/RootWriteTransactionBoundaryUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/DomainReturningControllerFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/DomainReturningControllerFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/DomainReturningControllerFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/DomainReturningControllerFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/KebabPathControllerFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/KebabPathControllerFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/KebabPathControllerFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/controller/KebabPathControllerFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/FakeRequestDto.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/FakeRequestDto.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/FakeRequestDto.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/FakeRequestDto.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/JsonIgnoreUnknownRequestFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/JsonIgnoreUnknownRequestFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/JsonIgnoreUnknownRequestFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/JsonIgnoreUnknownRequestFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/MergePatchJsonFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/MergePatchJsonFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/MergePatchJsonFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/MergePatchJsonFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/cascade/DeepCascadeRequestFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/cascade/DeepCascadeRequestFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/cascade/DeepCascadeRequestFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/inbound/web/dto/cascade/DeepCascadeRequestFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawExternalResponseFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawExternalResponseFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawExternalResponseFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawExternalResponseFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawTypeLeakingAdapterFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawTypeLeakingAdapterFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawTypeLeakingAdapterFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/RawTypeLeakingAdapterFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/httpclient/activation/EvilActivationLeak.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/httpclient/activation/EvilActivationLeak.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/httpclient/activation/EvilActivationLeak.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/httpclient/activation/EvilActivationLeak.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/messaging/GenericTypeLeakingAdapterFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/messaging/GenericTypeLeakingAdapterFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/messaging/GenericTypeLeakingAdapterFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/messaging/GenericTypeLeakingAdapterFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/persistence/notification/MisfiledNotificationRecordMapper.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/persistence/notification/MisfiledNotificationRecordMapper.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/persistence/notification/MisfiledNotificationRecordMapper.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/persistence/notification/MisfiledNotificationRecordMapper.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/settingsbypass/EvilSettings.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/settingsbypass/EvilSettings.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/settingsbypass/EvilSettings.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/adapter/outbound/settingsbypass/EvilSettings.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/ApplicationContextDependentFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/ApplicationContextDependentFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/ApplicationContextDependentFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/ApplicationContextDependentFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/BadlyNamedHandler.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/BadlyNamedHandler.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/BadlyNamedHandler.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/BadlyNamedHandler.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/BulkWriteWithoutWriteAccessUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/BulkWriteWithoutWriteAccessUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/BulkWriteWithoutWriteAccessUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/BulkWriteWithoutWriteAccessUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/FixtureRepository.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/FixtureRepository.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/FixtureRepository.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/FixtureRepository.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/GenericLeakQueryPort.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/GenericLeakQueryPort.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/GenericLeakQueryPort.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/GenericLeakQueryPort.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/JakartaValidationApplicationFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/JakartaValidationApplicationFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/JakartaValidationApplicationFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/JakartaValidationApplicationFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingCapabilityUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingCapabilityUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingCapabilityUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingCapabilityUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingTransactionBoundaryUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingTransactionBoundaryUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingTransactionBoundaryUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/MissingTransactionBoundaryUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/OutboundWithoutPermissionUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/OutboundWithoutPermissionUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/OutboundWithoutPermissionUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/OutboundWithoutPermissionUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakPortFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakPortFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakPortFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakPortFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakQueryPort.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakQueryPort.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakQueryPort.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/RawLeakQueryPort.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/ReadOnlyRepositoryWriteUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/ReadOnlyRepositoryWriteUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/ReadOnlyRepositoryWriteUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/ReadOnlyRepositoryWriteUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/TransactionalAnnotatedFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/TransactionalAnnotatedFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/TransactionalAnnotatedFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/TransactionalAnnotatedFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/WebDtoAcceptingApplicationFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/WebDtoAcceptingApplicationFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/WebDtoAcceptingApplicationFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/WebDtoAcceptingApplicationFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/orchestration/UnmarkedOrchestrationUseCase.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/orchestration/UnmarkedOrchestrationUseCase.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/orchestration/UnmarkedOrchestrationUseCase.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/orchestration/UnmarkedOrchestrationUseCase.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/port/in/MultiOperationPort.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/port/in/MultiOperationPort.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/application/port/in/MultiOperationPort.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/application/port/in/MultiOperationPort.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/boundary/DefaultTypingFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/boundary/DefaultTypingFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/boundary/DefaultTypingFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/boundary/DefaultTypingFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/boundary/InheritableThreadLocalFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/boundary/InheritableThreadLocalFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/boundary/InheritableThreadLocalFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/boundary/InheritableThreadLocalFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/boundary/ProblemDetailUsingFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/boundary/ProblemDetailUsingFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/boundary/ProblemDetailUsingFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/boundary/ProblemDetailUsingFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/NonSampleFeatureCoupledContractFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/NonSampleFeatureCoupledContractFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/NonSampleFeatureCoupledContractFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/NonSampleFeatureCoupledContractFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/contract/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/BillingFeatureFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/BillingFeatureFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/BillingFeatureFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/BillingFeatureFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/contractisolation/features/billing/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/AnnotatedPublicNoArgValueObjectFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/AnnotatedPublicNoArgValueObjectFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/AnnotatedPublicNoArgValueObjectFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/AnnotatedPublicNoArgValueObjectFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/JakartaValidationDomainFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/JakartaValidationDomainFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/JakartaValidationDomainFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/JakartaValidationDomainFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/LoggerUsingDomainFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/LoggerUsingDomainFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/LoggerUsingDomainFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/LoggerUsingDomainFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/PublicSetterAggregateFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/PublicSetterAggregateFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/PublicSetterAggregateFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/PublicSetterAggregateFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/SpringDependentDomainFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/SpringDependentDomainFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/SpringDependentDomainFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/SpringDependentDomainFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/entity/FakeDomainEntity.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/entity/FakeDomainEntity.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/entity/FakeDomainEntity.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/entity/FakeDomainEntity.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/jaxrs/JaxRsDomainEventFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/jaxrs/JaxRsDomainEventFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/jaxrs/JaxRsDomainEventFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/jaxrs/JaxRsDomainEventFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/kafka/KafkaDomainEventFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/kafka/KafkaDomainEventFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/kafka/KafkaDomainEventFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/kafka/KafkaDomainEventFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/nonrecord/NonRecordDomainEventFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/nonrecord/NonRecordDomainEventFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/nonrecord/NonRecordDomainEventFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/nonrecord/NonRecordDomainEventFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/pii/PiiViolationEventFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/pii/PiiViolationEventFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/pii/PiiViolationEventFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/pii/PiiViolationEventFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/springhttp/SpringHttpDomainEventFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/springhttp/SpringHttpDomainEventFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/springhttp/SpringHttpDomainEventFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/event/springhttp/SpringHttpDomainEventFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/vo/PackagePublicNoArgValueObjectFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/vo/PackagePublicNoArgValueObjectFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/domain/vo/PackagePublicNoArgValueObjectFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/domain/vo/PackagePublicNoArgValueObjectFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/LeakyProductionConsumerFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/LeakyProductionConsumerFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/LeakyProductionConsumerFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/LeakyProductionConsumerFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/LeakedTestFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/LeakedTestFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/LeakedTestFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/LeakedTestFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/fixtures/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/fixtureleak/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/secrets/RefreshScopeUsingFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/secrets/RefreshScopeUsingFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/secrets/RefreshScopeUsingFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/secrets/RefreshScopeUsingFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/semantic/callback/MisfiledControllerFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/semantic/callback/MisfiledControllerFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/semantic/callback/MisfiledControllerFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/semantic/callback/MisfiledControllerFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/semantic/records/MisfiledEntityFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/semantic/records/MisfiledEntityFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/semantic/records/MisfiledEntityFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/semantic/records/MisfiledEntityFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/serialization/BigDecimalDoubleConstructorFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/serialization/BigDecimalDoubleConstructorFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/serialization/BigDecimalDoubleConstructorFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/serialization/BigDecimalDoubleConstructorFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/shared/worklog/WorkLogStatus.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/shared/worklog/WorkLogStatus.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/shared/worklog/WorkLogStatus.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/shared/worklog/WorkLogStatus.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/slice/MixedSliceAnnotationsFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/slice/MixedSliceAnnotationsFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/slice/MixedSliceAnnotationsFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/slice/MixedSliceAnnotationsFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/slice/package-info.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/slice/package-info.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/slice/package-info.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/slice/package-info.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/JakartaWebSocketEndpointFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/JakartaWebSocketEndpointFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/JakartaWebSocketEndpointFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/JakartaWebSocketEndpointFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/ResponseBodyEmitterUsingFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/ResponseBodyEmitterUsingFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/ResponseBodyEmitterUsingFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/ResponseBodyEmitterUsingFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SpringWebSocketHandlerFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SpringWebSocketHandlerFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SpringWebSocketHandlerFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SpringWebSocketHandlerFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SseEmitterUsingFixture.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SseEmitterUsingFixture.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SseEmitterUsingFixture.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/architecture/violations/streaming/SseEmitterUsingFixture.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/ScheduledJobOverlapPolicyTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/async/ScheduledJobOverlapPolicyTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/ScheduledJobOverlapPolicyTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/async/ScheduledJobOverlapPolicyTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/TaskExecutorDecoratorPolicyTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/async/TaskExecutorDecoratorPolicyTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/TaskExecutorDecoratorPolicyTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/async/TaskExecutorDecoratorPolicyTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/authz/RequiresPermissionProxyabilityTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/authz/RequiresPermissionProxyabilityTest.java similarity index 96% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/authz/RequiresPermissionProxyabilityTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/authz/RequiresPermissionProxyabilityTest.java index 76a5a9a6..160c93f2 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/authz/RequiresPermissionProxyabilityTest.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/authz/RequiresPermissionProxyabilityTest.java @@ -5,9 +5,9 @@ import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; -import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.lang.ArchRule; import dev.caskeleton.application.security.RequiresPermission; +import dev.caskeleton.bootstrap.architecture.ProductionClassImportOption; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -41,7 +41,7 @@ class RequiresPermissionProxyabilityTest { static void importProductionClasses() { productionClasses = new ClassFileImporter() - .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .withImportOption(new ProductionClassImportOption()) .importPackages("dev.caskeleton"); } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/outbox/EventPayloadPiiContractTest.java b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/contract/outbox/EventPayloadPiiContractTest.java similarity index 89% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/outbox/EventPayloadPiiContractTest.java rename to src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/contract/outbox/EventPayloadPiiContractTest.java index a460e9cb..b5458671 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/outbox/EventPayloadPiiContractTest.java +++ b/src/app-bootstrap/src/architectureTest/java/dev/caskeleton/bootstrap/contract/outbox/EventPayloadPiiContractTest.java @@ -13,6 +13,7 @@ import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.EvaluationResult; import com.tngtech.archunit.lang.SimpleConditionEvent; +import dev.caskeleton.bootstrap.architecture.ProductionClassImportOption; import dev.caskeleton.domain.stereotype.DomainEvent; import java.util.regex.Pattern; import org.junit.jupiter.api.Test; @@ -31,9 +32,9 @@ import org.junit.jupiter.api.Test; *

  • Red test: loads only the violation fixture ({@code * PiiViolationEventFixture} — a {@code @DomainEvent} with an {@code email} field) and asserts * the rule fires. - *
  • Green test: loads all production + sample classes (excluding the - * violations package) and asserts the rule does NOT fire — i.e., no real event class carries - * a PII field name. + *
  • Green test: loads all production classes (excluding the violations + * package) and asserts the rule does NOT fire — i.e., no real event class carries a PII field + * name. * */ class EventPayloadPiiContractTest { @@ -99,14 +100,14 @@ class EventPayloadPiiContractTest { // ---- Green test: production classes must pass the rule ---- /** - * Production + sample-portfolio classes excluding the violations fixture package. + * Production classes excluding the violations fixture package. * - *

    Scans {@code dev.caskeleton} broadly (covers domain-core, application-core, - * adapter-outbound, sample-portfolio all on the test classpath) but excludes the {@code - * violations} package which intentionally contains broken fixtures. + *

    Scans {@code dev.caskeleton} broadly across the production test classpath but excludes the + * {@code violations} package which intentionally contains broken fixtures. */ private static final JavaClasses PRODUCTION_CLASSES = new ClassFileImporter() + .withImportOption(new ProductionClassImportOption()) .importPackages("dev.caskeleton") .that( new DescribedPredicate("not in violations package") { diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/DefaultProfileBootCharacterizationTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/DefaultProfileBootCharacterizationTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/DefaultProfileBootCharacterizationTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/DefaultProfileBootCharacterizationTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/DependencyErrorStartupContractTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/DependencyErrorStartupContractTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/DependencyErrorStartupContractTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/DependencyErrorStartupContractTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/FiveAdapterOffInventoryTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/FiveAdapterOffInventoryTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/FiveAdapterOffInventoryTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/FiveAdapterOffInventoryTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/GraphQlShippedAndGatedTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/GraphQlShippedAndGatedTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/GraphQlShippedAndGatedTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/GraphQlShippedAndGatedTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/MasterSwitchEnvironmentPostProcessorTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/MasterSwitchEnvironmentPostProcessorTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/MasterSwitchEnvironmentPostProcessorTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/MasterSwitchEnvironmentPostProcessorTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/ShippedCompositionHarness.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/ShippedCompositionHarness.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/ShippedCompositionHarness.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/ShippedCompositionHarness.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/StartupWarningZeroTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/StartupWarningZeroTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/StartupWarningZeroTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/activation/StartupWarningZeroTest.java diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/logging/LogProfileDriftCharacterizationTest.java b/src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/logging/LogProfileDriftCharacterizationTest.java similarity index 100% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/logging/LogProfileDriftCharacterizationTest.java rename to src/app-bootstrap/src/bootCompositionTest/java/dev/caskeleton/bootstrap/logging/LogProfileDriftCharacterizationTest.java diff --git a/src/app-bootstrap/src/conditionalTransportTest/java/dev/caskeleton/bootstrap/transport/ConditionalTransportCompositionContractTest.java b/src/app-bootstrap/src/conditionalTransportTest/java/dev/caskeleton/bootstrap/transport/ConditionalTransportCompositionContractTest.java index ebd5f68c..1708c8b8 100644 --- a/src/app-bootstrap/src/conditionalTransportTest/java/dev/caskeleton/bootstrap/transport/ConditionalTransportCompositionContractTest.java +++ b/src/app-bootstrap/src/conditionalTransportTest/java/dev/caskeleton/bootstrap/transport/ConditionalTransportCompositionContractTest.java @@ -3,42 +3,17 @@ package dev.caskeleton.bootstrap.transport; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.yaml.snakeyaml.LoaderOptions; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; /** - * What each opt-in transport's registry entry claims, and what actually backs the claim. - * - *

    This test asserted that all three transports had {@code runtime_memberships: []} and that - * their classes load. Both halves aged badly. - * - *

    The membership half became false: the five-adapter contract put GraphQL on the single - * {@code app-bootstrap} bootJar behind {@code APP_GRAPHQL_ENABLED}, so its membership is {@code - * [app-bootstrap]} by design. The test went red the moment that landed and nobody saw it, because - * this suite runs in {@code conditionalTransportQualification} rather than in {@code test} — and - * {@code conditionalTransportQualification} is one of the two commands CI runs. A gate that is red - * in a lane nobody runs locally is a gate that reports whatever the last person to run it saw. - * - *

    The class-loading half was never evidence of anything. A type resolving proves the jar is on a - * classpath; it says nothing about whether a composition assembles the transport, or whether the - * switch that is supposed to gate it does. The spec's own words: class existence is not composition - * evidence. - * - *

    So the two groups are now asserted for what they actually are. gRPC and WebSocket are - * build-only — no membership, and nothing may put them on a runtime. GraphQL is shipped and - * switch-gated, which is a stronger claim and carries a stronger obligation: the switch has to be - * the thing that decides. That obligation is discharged by evidence this test can name but not - * produce — the {@code off-local}, {@code off-dev} and {@code off-prod} Compose lanes prove zero - * beans, sockets and routes with the switch off, and {@code local-graphql} proves a real {@code - * /graphql} against a real identity provider with it on. + * Conditional transports are verified against the real composition root, not a duplicate registry + * membership list. Build-only transports must be absent from app-bootstrap dependencies; GraphQL is + * shipped deliberately and is controlled by its master switch. Runtime behavior is proven by the + * named compose qualification lanes. */ class ConditionalTransportCompositionContractTest { @@ -57,49 +32,35 @@ class ConditionalTransportCompositionContractTest { "dev.caskeleton.adapter.inbound.graphql.HealthGraphqlController"); @Test - @DisplayName("a build-only transport has no runtime membership") - void aBuildOnlyTransportHasNoRuntimeMembership() throws IOException { - List> modules = modules(); - + @DisplayName("build-only transports are absent from the production composition") + void buildOnlyTransportsAreAbsentFromProductionComposition() throws IOException { + String bootstrapBuild = + Files.readString(repositoryRoot().resolve("src/app-bootstrap/build.gradle")); BUILD_ONLY_TRANSPORTS.forEach( (moduleId, typeName) -> { - assertThat(modules) - .filteredOn(module -> moduleId.equals(module.get("id"))) - .singleElement() - .extracting(module -> module.get("runtime_memberships")) - .as( - "%s is qualified on its own classpath and reaches no deployment; membership is " - + "what would put it on one", - moduleId) - .isEqualTo(List.of()); + String gradlePath = + switch (moduleId) { + case "adapter-inbound-grpc" -> ":adapter:inbound:grpc"; + case "adapter-inbound-websocket" -> ":adapter:inbound:websocket"; + default -> throw new IllegalArgumentException(moduleId); + }; + assertThat(bootstrapBuild) + .as("%s is qualification-only and must not ship in app-bootstrap", moduleId) + .doesNotContain("project('" + gradlePath + "')"); assertThatCodeLoads(typeName); }); } @Test - @DisplayName("a switch-gated transport has membership, and a master switch that decides it") - void aSwitchGatedTransportHasMembershipAndASwitch() throws IOException { - List> modules = modules(); - - SWITCH_GATED_TRANSPORTS.forEach( - (moduleId, typeName) -> { - assertThat(modules) - .filteredOn(module -> moduleId.equals(module.get("id"))) - .singleElement() - .extracting(module -> module.get("runtime_memberships")) - .as( - "%s ships on the one bootJar by design; asserting it has no membership is what " - + "made this suite red in a lane nobody runs", - moduleId) - .isEqualTo(List.of("app-bootstrap")); - assertThatCodeLoads(typeName); - }); - - assertThat(shippedApplicationYaml()) - .as( - "membership without a switch is a transport that publishes a route because a jar is " - + "present, which is the state the five-adapter contract exists to remove") - .contains("${APP_GRAPHQL_ENABLED:false}"); + @DisplayName("GraphQL is a real composition dependency and its master switch decides activation") + void switchGatedTransportIsARealDependencyWithASwitch() throws IOException { + String bootstrapBuild = + Files.readString(repositoryRoot().resolve("src/app-bootstrap/build.gradle")); + assertThat(bootstrapBuild).contains("project(':adapter:inbound:graphql')"); + SWITCH_GATED_TRANSPORTS + .values() + .forEach(ConditionalTransportCompositionContractTest::assertThatCodeLoads); + assertThat(shippedApplicationYaml()).contains("${APP_GRAPHQL_ENABLED:false}"); } @Test @@ -120,13 +81,6 @@ class ConditionalTransportCompositionContractTest { .contains("\"local-graphql\""); } - private static List> modules() throws IOException { - Map registry = readRegistry(); - @SuppressWarnings("unchecked") - List> modules = (List>) registry.get("modules"); - return modules; - } - private static String shippedApplicationYaml() throws IOException { return Files.readString( repositoryRoot().resolve("src/app-bootstrap/src/main/resources/application.yml")); @@ -140,13 +94,6 @@ class ConditionalTransportCompositionContractTest { } } - private static Map readRegistry() throws IOException { - Path registry = repositoryRoot().resolve("src/config/architecture/modules.json"); - try (InputStream stream = Files.newInputStream(registry)) { - return new Yaml(new SafeConstructor(new LoaderOptions())).load(stream); - } - } - private static Path repositoryRoot() { Path candidate = Path.of("").toAbsolutePath(); while (candidate != null diff --git a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/BuildVerificationPurityContractTest.java b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/BuildVerificationPurityContractTest.java index d104ae29..11244f74 100644 --- a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/BuildVerificationPurityContractTest.java +++ b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/BuildVerificationPurityContractTest.java @@ -14,9 +14,10 @@ import org.junit.jupiter.api.io.TempDir; final class BuildVerificationPurityContractTest { private static final Path SOURCE_ROOT = sourceRoot(); - private static final Path ARCHIVE_SCRIPT = SOURCE_ROOT.resolve("gradle/archive-hygiene.gradle"); + private static final Path ARCHIVE_SCRIPT = + SOURCE_ROOT.resolve("build-logic/src/main/groovy/ca.archive-hygiene.gradle"); private static final Path PUBLIC_PATH_SCRIPT = - SOURCE_ROOT.resolve("gradle/public-path-snapshot.gradle"); + SOURCE_ROOT.resolve("build-logic/src/main/groovy/ca.public-path-snapshot.gradle"); /** * What the renderer produces for the fixture's {@code security.yml}. @@ -256,7 +257,7 @@ final class BuildVerificationPurityContractTest { System.arraycopy(arguments, 0, fullArguments, 2, arguments.length); return GradleRunner.create() .withProjectDir(projectDirectory.toFile()) - .withTestKitDir(projectDirectory.resolve(".test-kit").toFile()) + .withTestKitDir(sourceRoot().resolve("app-bootstrap/build/test-kit-cache").toFile()) .withArguments(fullArguments); } diff --git a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ConditionalTransportEvidenceFunctionalTest.java b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ConditionalTransportEvidenceFunctionalTest.java index cb0442cd..0888f531 100644 --- a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ConditionalTransportEvidenceFunctionalTest.java +++ b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ConditionalTransportEvidenceFunctionalTest.java @@ -110,7 +110,7 @@ final class ConditionalTransportEvidenceFunctionalTest { System.arraycopy(arguments, 0, fullArguments, 2, arguments.length); return GradleRunner.create() .withProjectDir(projectDirectory.toFile()) - .withTestKitDir(projectDirectory.resolve(".test-kit").toFile()) + .withTestKitDir(sourceRoot().resolve("app-bootstrap/build/test-kit-cache").toFile()) .withArguments(fullArguments); } diff --git a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ReleaseProvenanceFunctionalTest.java b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ReleaseProvenanceFunctionalTest.java new file mode 100644 index 00000000..44af2a5e --- /dev/null +++ b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/ReleaseProvenanceFunctionalTest.java @@ -0,0 +1,148 @@ +package dev.caskeleton.bootstrap.contract; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.stream.Stream; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Verifies that release provenance is a release concern rather than a configuration-time build + * prerequisite. + */ +class ReleaseProvenanceFunctionalTest { + + @Test + void aBuildWithNoGitMetadataConfiguresAndIsNotCalledARelease(@TempDir Path fixtureRoot) + throws Exception { + Path fixtureSrc = fixtureRoot.resolve("src"); + copyBuildFixture(fixtureSrc); + assertThat(fixtureSrc.resolve(".git")).doesNotExist(); + + BuildResult configured = run(fixtureSrc, "help"); + assertThat(configured.getOutput()).contains("BUILD SUCCESSFUL"); + + BuildResult provenance = runAndFail(fixtureSrc, "verifyReleaseProvenance"); + assertThat(provenance.getOutput()).contains("no source revision"); + } + + @Test + void anAttestedRevisionSatisfiesReleaseProvenanceWithoutGit(@TempDir Path fixtureRoot) + throws Exception { + Path fixtureSrc = fixtureRoot.resolve("src"); + copyBuildFixture(fixtureSrc); + + BuildResult result = + run( + fixtureSrc, + "verifyReleaseProvenance", + "-PgitRevision=0123456789abcdef0123456789abcdef01234567"); + + assertThat(result.getOutput()).contains("verifyReleaseProvenance: OK"); + } + + private static BuildResult run(Path projectDirectory, String... arguments) { + return runner(projectDirectory, arguments).build(); + } + + private static BuildResult runAndFail(Path projectDirectory, String... arguments) { + return runner(projectDirectory, arguments).buildAndFail(); + } + + private static GradleRunner runner(Path projectDirectory, String... arguments) { + String[] fullArguments = new String[arguments.length + 2]; + fullArguments[0] = "--console=plain"; + fullArguments[1] = "--stacktrace"; + System.arraycopy(arguments, 0, fullArguments, 2, arguments.length); + return GradleRunner.create() + .withProjectDir(projectDirectory.toFile()) + // Fixtures stay isolated; Gradle's immutable dependency/plugin caches and daemon do not. + .withTestKitDir(sourceRoot().resolve("app-bootstrap/build/test-kit-cache").toFile()) + .withArguments(fullArguments); + } + + private static void copyBuildFixture(Path fixtureSrc) throws IOException { + Path sourceRoot = sourceRoot(); + copyTree(sourceRoot.resolve("gradle"), fixtureSrc.resolve("gradle")); + copyTree(sourceRoot.resolve("config"), fixtureSrc.resolve("config")); + Path gradleProperties = sourceRoot.resolve("gradle.properties"); + if (Files.isRegularFile(gradleProperties)) { + copyFile(gradleProperties, fixtureSrc.resolve("gradle.properties")); + } + copyIncludedBuildSources(sourceRoot.resolve("build-logic"), fixtureSrc.resolve("build-logic")); + copyIncludedBuildSources( + sourceRoot.resolve("build-qualification"), fixtureSrc.resolve("build-qualification")); + + try (Stream paths = Files.walk(sourceRoot)) { + paths + .filter(Files::isRegularFile) + .filter( + path -> + path.getFileName().toString().equals("build.gradle") + || path.getFileName().toString().equals("gradle.lockfile") + || path.equals(sourceRoot.resolve("settings.gradle"))) + .filter(path -> !path.startsWith(sourceRoot.resolve("build"))) + .forEach( + source -> { + try { + copyFile(source, fixtureSrc.resolve(sourceRoot.relativize(source))); + } catch (IOException exception) { + throw new IllegalStateException( + "failed to copy Gradle fixture input " + source, exception); + } + }); + } + } + + private static void copyIncludedBuildSources(Path sourceRoot, Path targetRoot) + throws IOException { + copyFile(sourceRoot.resolve("settings.gradle"), targetRoot.resolve("settings.gradle")); + copyFile(sourceRoot.resolve("build.gradle"), targetRoot.resolve("build.gradle")); + Path sourceDirectory = sourceRoot.resolve("src"); + if (Files.isDirectory(sourceDirectory)) { + copyTree(sourceDirectory, targetRoot.resolve("src")); + } + Path lockfile = sourceRoot.resolve("gradle.lockfile"); + if (Files.isRegularFile(lockfile)) { + copyFile(lockfile, targetRoot.resolve("gradle.lockfile")); + } + } + + private static void copyTree(Path sourceRoot, Path targetRoot) throws IOException { + try (Stream paths = Files.walk(sourceRoot)) { + paths + .filter(Files::isRegularFile) + .forEach( + source -> { + try { + copyFile(source, targetRoot.resolve(sourceRoot.relativize(source))); + } catch (IOException exception) { + throw new IllegalStateException("failed to copy tree input " + source, exception); + } + }); + } + } + + private static void copyFile(Path source, Path target) throws IOException { + Files.createDirectories(target.getParent()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } + + private static Path sourceRoot() { + for (Path candidate = Path.of("").toAbsolutePath(); + candidate != null; + candidate = candidate.getParent()) { + if (Files.isRegularFile(candidate.resolve("gradlew")) + && Files.isDirectory(candidate.resolve("app-bootstrap"))) { + return candidate; + } + } + throw new IllegalStateException("repository src root not found"); + } +} diff --git a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/RuntimeMembershipFunctionalTest.java b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/RuntimeMembershipFunctionalTest.java index e4a6ac6a..f23a1d8f 100644 --- a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/RuntimeMembershipFunctionalTest.java +++ b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/RuntimeMembershipFunctionalTest.java @@ -11,106 +11,55 @@ import org.gradle.testkit.runner.GradleRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +/** The runtime registry verifies invariants from Gradle's resolved graph; it does not mirror it. */ final class RuntimeMembershipFunctionalTest { - /** The included build the membership convention lives in. */ private static final Path BUILD_LOGIC = sourceRoot().resolve("build-logic"); @Test - void exactRegistryMembershipMatchesBothRuntimeGraphs(@TempDir Path temporaryDirectory) - throws IOException { - RuntimeFixture fixture = fixture(temporaryDirectory, "", ""); + void registeredRuntimeGraphsPass(@TempDir Path temporaryDirectory) throws IOException { + RuntimeFixture fixture = fixture(temporaryDirectory, "", false); - BuildResult result = run(fixture.projectDirectory(), "verifyRuntimeModuleMembership"); + BuildResult result = run(fixture.projectDirectory(), "verifyRuntimeModuleRegistry"); assertThat(result.getOutput()) - .contains("verifyRuntimeModuleMembership: 2 runtime composition(s) match the registry"); + .contains( + "verifyRuntimeModuleRegistry: 1 composition root(s) resolve only registered application projects"); } @Test - void dependencyAbsentFromMembershipFailsClosed(@TempDir Path temporaryDirectory) + void anUnregisteredProjectOnTheRuntimeClasspathFailsClosed(@TempDir Path temporaryDirectory) throws IOException { RuntimeFixture fixture = - fixture(temporaryDirectory, "implementation project(':adapter:inbound:grpc')", ""); + fixture(temporaryDirectory, "implementation project(':unregistered')", false); - BuildResult result = runAndFail(fixture.projectDirectory(), "verifyRuntimeModuleMembership"); + BuildResult result = runAndFail(fixture.projectDirectory(), "verifyRuntimeModuleRegistry"); assertThat(result.getOutput()) .contains("app-bootstrap") - .contains("unregistered runtime dependencies") - .contains("adapter-inbound-grpc"); - } - - @Test - void registeredMemberWithoutDependencyFailsClosed(@TempDir Path temporaryDirectory) - throws IOException { - RuntimeFixture fixture = fixture(temporaryDirectory, "", "\"app-bootstrap\""); - - BuildResult result = runAndFail(fixture.projectDirectory(), "verifyRuntimeModuleMembership"); - - assertThat(result.getOutput()) - .contains("app-bootstrap") - .contains("missing registered runtime dependencies") - .contains("adapter-inbound-grpc"); - } - - @Test - void unknownCompositionMembershipFailsClosed(@TempDir Path temporaryDirectory) - throws IOException { - RuntimeFixture fixture = fixture(temporaryDirectory, "", "\"unknown-runtime\""); - - BuildResult result = runAndFail(fixture.projectDirectory(), "verifyRuntimeModuleMembership"); - - assertThat(result.getOutput()) - .contains("adapter-inbound-grpc") - .contains("unknown runtime membership") - .contains("unknown-runtime"); - } - - @Test - void missingMembershipFieldFailsClosed(@TempDir Path temporaryDirectory) throws IOException { - RuntimeFixture fixture = fixture(temporaryDirectory, "", ""); - String registry = Files.readString(fixture.registry(), UTF_8); - Files.writeString( - fixture.registry(), - registry.replace(" \"runtime_memberships\": []", " \"drop\": true"), - UTF_8); - - BuildResult result = runAndFail(fixture.projectDirectory(), "verifyRuntimeModuleMembership"); - - // Names the module and the field it lost. The wording changed when the check moved into the - // shared registry parser: it now enforces the exact field set rather than looking for one field - // at a time, so a dropped `runtime_memberships` is reported as a missing field instead of as a - // value that is not a list. The stricter check also catches an *added* field, which the - // previous - // one ignored — so the assertion follows the property rather than the old sentence. - assertThat(result.getOutput()) - .contains("adapter-inbound-grpc") - .contains("runtime_memberships") - .contains("missing"); + .contains("unregistered application projects") + .contains(":unregistered"); } private static RuntimeFixture fixture( - Path temporaryDirectory, String extraAppDependency, String grpcMembership) + Path temporaryDirectory, String extraAppDependency, boolean omitDomainFromRegistry) throws IOException { - Path projectDirectory = temporaryDirectory.resolve("runtime-membership-fixture"); + Path repositoryRoot = temporaryDirectory.resolve("repository"); + Path projectDirectory = repositoryRoot.resolve("src"); Files.createDirectories(projectDirectory.resolve("config/architecture")); - for (String module : - new String[] {"domain-core", "adapter/inbound/grpc", "app-bootstrap", "sample-portfolio"}) { + for (String module : new String[] {"domain-core", "app-bootstrap", "unregistered"}) { Files.createDirectories(projectDirectory.resolve(module)); } + Files.writeString( projectDirectory.resolve("settings.gradle"), - // includeBuild so the fixture can apply the convention rather than a copy of it: the - // membership check moved into build-logic when its registry parsing was shared, and a - // fixture that applied the old script by path would be testing something the build no - // longer runs. """ pluginManagement { includeBuild('%s') } - include 'domain-core', 'adapter:inbound:grpc', 'app-bootstrap', 'sample-portfolio' + include 'domain-core', 'app-bootstrap', 'unregistered' """ .formatted(BUILD_LOGIC.toAbsolutePath().toString().replace("\\", "/")), UTF_8); + Files.writeString( projectDirectory.resolve("build.gradle"), """ @@ -118,9 +67,6 @@ final class RuntimeMembershipFunctionalTest { id 'base' id 'ca.runtime-membership' } - // The fixture's projects sit beside its registry rather than under a src/ directory, so it - // states its own root instead of inheriting this repository's layout. - moduleRegistryRepositoryRoot = projectDir subprojects { apply plugin: 'java' } project(':app-bootstrap') { dependencies { @@ -128,52 +74,29 @@ final class RuntimeMembershipFunctionalTest { %s } } - project(':sample-portfolio') { - dependencies { implementation project(':domain-core') } - } """ .formatted(extraAppDependency), UTF_8); - String memberships = grpcMembership.isBlank() ? "" : grpcMembership; + + String domainEntry = + omitDomainFromRegistry + ? "" + : """ + {"id":"domain-core","gradle_path":":domain-core","source_path":"src/domain-core","allowed_dependencies":[]}, + """; Path registry = projectDirectory.resolve("config/architecture/modules.json"); Files.writeString( registry, """ { - "runtime_compositions": ["app-bootstrap", "sample-portfolio"], + "composition_roots": ["app-bootstrap"], "modules": [ - { - "id": "domain-core", - "gradle_path": ":domain-core", - "source_path": "domain-core", - "allowed_dependencies": [], - "runtime_memberships": ["app-bootstrap", "sample-portfolio"] - }, - { - "id": "adapter-inbound-grpc", - "gradle_path": ":adapter:inbound:grpc", - "source_path": "adapter/inbound/grpc", - "allowed_dependencies": [], - "runtime_memberships": [%s] - }, - { - "id": "app-bootstrap", - "gradle_path": ":app-bootstrap", - "source_path": "app-bootstrap", - "allowed_dependencies": ["domain-core"], - "runtime_memberships": ["app-bootstrap"] - }, - { - "id": "sample-portfolio", - "gradle_path": ":sample-portfolio", - "source_path": "sample-portfolio", - "allowed_dependencies": ["domain-core"], - "runtime_memberships": ["sample-portfolio"] - } + %s + {"id":"app-bootstrap","gradle_path":":app-bootstrap","source_path":"src/app-bootstrap","allowed_dependencies":["domain-core"]} ] } """ - .formatted(memberships), + .formatted(domainEntry), UTF_8); return new RuntimeFixture(projectDirectory, registry); } @@ -193,7 +116,7 @@ final class RuntimeMembershipFunctionalTest { System.arraycopy(arguments, 0, fullArguments, 2, arguments.length); return GradleRunner.create() .withProjectDir(projectDirectory.toFile()) - .withTestKitDir(projectDirectory.resolve(".test-kit").toFile()) + .withTestKitDir(sourceRoot().resolve("app-bootstrap/build/test-kit-cache").toFile()) .withArguments(fullArguments); } diff --git a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/StrictQualificationTestConventionFunctionalTest.java b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/StrictQualificationTestConventionFunctionalTest.java index 45f617e4..fde14ed0 100644 --- a/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/StrictQualificationTestConventionFunctionalTest.java +++ b/src/app-bootstrap/src/functionalTest/java/dev/caskeleton/bootstrap/contract/StrictQualificationTestConventionFunctionalTest.java @@ -205,7 +205,7 @@ final class StrictQualificationTestConventionFunctionalTest { System.arraycopy(arguments, 0, fullArguments, 2, arguments.length); return GradleRunner.create() .withProjectDir(projectDirectory.toFile()) - .withTestKitDir(projectDirectory.resolve(".test-kit").toFile()) + .withTestKitDir(sourceRoot().resolve("app-bootstrap/build/test-kit-cache").toFile()) .withArguments(fullArguments); } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/compose/ComposeMergeCharacterizationTest.java b/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/compose/ComposeMergeCharacterizationTest.java similarity index 96% rename from src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/compose/ComposeMergeCharacterizationTest.java rename to src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/compose/ComposeMergeCharacterizationTest.java index 4b6db1f0..59751f90 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/compose/ComposeMergeCharacterizationTest.java +++ b/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/compose/ComposeMergeCharacterizationTest.java @@ -49,8 +49,9 @@ class ComposeMergeCharacterizationTest { * *

    Delegating rather than reimplementing: the script is the entry point CI calls, and a test * that re-derived the same checks in Java would be a second opinion that can agree with the - * contract while the script disagrees. What this adds is that the check runs in the ordinary - * suite, so a lane cannot drift until somebody remembers to run a shell script. + * contract while the script disagrees. What this adds is that the check runs in the explicit + * integration lane, where Docker Compose and companion CLI tools are environment prerequisites + * rather than hidden unit-test dependencies. */ @Test @DisplayName("every lane renders exactly the services its contract names") diff --git a/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/DistributedLockProviderContractTest.java b/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/DistributedLockProviderContractTest.java index ba72e28f..d6e5a6bd 100644 --- a/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/DistributedLockProviderContractTest.java +++ b/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/DistributedLockProviderContractTest.java @@ -88,7 +88,7 @@ class DistributedLockProviderContractTest { sharedDataSource = hikari(pg); - // Flyway migrates V1 (idempotency), V2 (work_log from sample-portfolio on + // Flyway migrates V1 (idempotency), V2 (legacy work_log migration on // app-bootstrap test classpath), V3 (outbox), V4 (INT_LOCK). Flyway.configure() .dataSource(sharedDataSource) diff --git a/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/outbox/OutboxContainerTestSupport.java b/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/outbox/OutboxContainerTestSupport.java index 8ab1a6e2..4555fd7a 100644 --- a/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/outbox/OutboxContainerTestSupport.java +++ b/src/app-bootstrap/src/integrationTest/java/dev/caskeleton/bootstrap/integration/outbox/OutboxContainerTestSupport.java @@ -56,7 +56,7 @@ final class OutboxContainerTestSupport { /** * Runs Flyway migrations against the given DataSource. All three migrations (V1 idempotency, V2 - * work_log from sample-portfolio on app-bootstrap test classpath, V3 outbox_event) are applied. + * legacy work_log migration, V3 outbox_event) are applied. */ static void migrate(DataSource dataSource) { Flyway.configure() diff --git a/src/app-bootstrap/src/sampleOffTest/java/dev/caskeleton/bootstrap/contract/SampleOffClasspathContractTest.java b/src/app-bootstrap/src/sampleOffTest/java/dev/caskeleton/bootstrap/contract/SampleOffClasspathContractTest.java deleted file mode 100644 index 1055a007..00000000 --- a/src/app-bootstrap/src/sampleOffTest/java/dev/caskeleton/bootstrap/contract/SampleOffClasspathContractTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.caskeleton.bootstrap.contract; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import dev.caskeleton.bootstrap.CaSkeletonApplication; -import org.junit.jupiter.api.Test; - -final class SampleOffClasspathContractTest { - - @Test - void sampleProductionFixtureIsAbsentFromTheMinimalBootstrapClasspath() { - assertThat(System.getProperty("ca.sample.mode")).isEqualTo("off"); - assertThat(CaSkeletonApplication.class.getName()) - .isEqualTo("dev.caskeleton.bootstrap.CaSkeletonApplication"); - assertThatThrownBy( - () -> - Class.forName( - "dev.caskeleton.sample.portfolio.SamplePortfolioApplication", - false, - CaSkeletonApplication.class.getClassLoader())) - .isInstanceOf(ClassNotFoundException.class); - } -} diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/PortResolutionContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/PortResolutionContractTest.java index eda5e083..9e49f856 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/PortResolutionContractTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/activation/PortResolutionContractTest.java @@ -46,13 +46,15 @@ class PortResolutionContractTest { "@Bean[^\\n]*\\n(?:\\s*@[\\w.]+(?:\\([^)]*\\))?\\s*\\n)*?\\s*([\\w<>,\\[\\] ]+?)\\s+(\\w+)\\s*\\(", Pattern.MULTILINE); + private static final List PRODUCTION_SOURCES = loadProductionSources(); + @Test @DisplayName("no port implementation wins by @Primary alone") void noPortIsResolvedByPreference() { List unconditional = new ArrayList<>(); - for (Path source : productionSources()) { - String text = read(source); + for (ProductionSource source : PRODUCTION_SOURCES) { + String text = source.text(); if (!text.contains("@Primary")) { continue; } @@ -69,7 +71,8 @@ class PortResolutionContractTest { continue; } if (!declaration.contains("@ConditionalOnProperty")) { - unconditional.add(source.getFileName() + "#" + matcher.group(2) + " -> " + returnType); + unconditional.add( + source.path().getFileName() + "#" + matcher.group(2) + " -> " + returnType); } } } @@ -87,8 +90,8 @@ class PortResolutionContractTest { void competingPrimariesAreMutuallyExclusive() { Map> havingValuesByPort = new LinkedHashMap<>(); - for (Path source : productionSources()) { - String text = read(source); + for (ProductionSource source : PRODUCTION_SOURCES) { + String text = source.text(); if (!text.contains("@Primary")) { continue; } @@ -125,13 +128,14 @@ class PortResolutionContractTest { .containsKey("DistributedLockPort"); } - private static List productionSources() { + private static List loadProductionSources() { Path root = repositoryRoot().resolve("src"); try (Stream files = Files.walk(root)) { return files .filter(path -> path.toString().endsWith(".java")) .filter(path -> path.toString().contains("/src/main/java/")) .filter(path -> !path.toString().contains("/build/")) + .map(path -> new ProductionSource(path, read(path))) .toList(); } catch (IOException unreadable) { throw new UncheckedIOException(unreadable); @@ -157,4 +161,6 @@ class PortResolutionContractTest { } return candidate; } + + private record ProductionSource(Path path, String text) {} } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncExceptionPropagationTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncExceptionPropagationTest.java index 970a95f1..bf2f4b8b 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncExceptionPropagationTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncExceptionPropagationTest.java @@ -11,9 +11,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; /** @@ -30,26 +28,33 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; */ class AsyncExceptionPropagationTest { - private final ApplicationContextRunner runner = - new ApplicationContextRunner() - .withUserConfiguration(PropagatorConfig.class, AsyncExecutorConfig.class); - @Test void submitPathSurfacesTaskExceptionViaFuture() { - runner.run( - context -> { - ThreadPoolTaskExecutor executor = - context.getBean(AsyncExecutorConfig.EXECUTOR_BEAN_NAME, ThreadPoolTaskExecutor.class); - Future future = - executor.submit( - () -> { - throw new IllegalStateException("boom in async task"); - }); - assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)) - .isInstanceOf(ExecutionException.class) - .hasRootCauseInstanceOf(IllegalStateException.class) - .hasMessageContaining("boom in async task"); - }); + AsyncExecutorConfig config = new AsyncExecutorConfig(); + DomainContextPropagator propagator = + DomainContextPropagatorFactory.create(DomainContextStrategy.THREAD_LOCAL); + BackgroundJobMetrics metrics = + new BackgroundJobMetrics( + new StaticListableBeanFactory() + .getBeanProvider(io.micrometer.core.instrument.MeterRegistry.class)); + ThreadPoolTaskExecutor executor = + config.applicationTaskExecutor( + new AsyncExecutorSettings(1, 1, 8), + config.asyncContextTaskDecorator(propagator), + metrics); + try { + Future future = + executor.submit( + () -> { + throw new IllegalStateException("boom in async task"); + }); + assertThatThrownBy(() -> future.get(5, TimeUnit.SECONDS)) + .isInstanceOf(ExecutionException.class) + .hasRootCauseInstanceOf(IllegalStateException.class) + .hasMessageContaining("boom in async task"); + } finally { + executor.shutdown(); + } } @Test @@ -66,12 +71,4 @@ class AsyncExceptionPropagationTest { .isInstanceOf(IllegalStateException.class) .hasMessage("boom on execute path"); } - - @Configuration - static class PropagatorConfig { - @Bean - DomainContextPropagator domainContextPropagator() { - return DomainContextPropagatorFactory.create(DomainContextStrategy.THREAD_LOCAL); - } - } } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncGracefulShutdownBehaviorTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncGracefulShutdownBehaviorTest.java index 8748e495..5a83a033 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncGracefulShutdownBehaviorTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/async/AsyncGracefulShutdownBehaviorTest.java @@ -10,6 +10,7 @@ import dev.caskeleton.shared.concurrency.DomainContextPropagator; import dev.caskeleton.shared.concurrency.DomainContextPropagatorFactory; import dev.caskeleton.shared.concurrency.DomainContextStrategy; import java.util.Arrays; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -86,12 +87,12 @@ class AsyncGracefulShutdownBehaviorTest { AtomicBoolean completed = new AtomicBoolean(false); CountDownLatch started = new CountDownLatch(1); - // A job that is still in-flight when shutdown begins, but well under the 19s budget. + CountDownLatch allowCompletion = new CountDownLatch(1); Future inFlightJob = executor.submit( () -> { started.countDown(); - sleepQuietly(300); + awaitQuietly(allowCompletion); completed.set(true); }); assertThat(started.await(5, TimeUnit.SECONDS)) @@ -99,9 +100,12 @@ class AsyncGracefulShutdownBehaviorTest { .isTrue(); long startNanos = System.nanoTime(); - // Graceful shutdown: setWaitForTasksToCompleteOnShutdown(true) + - // setAwaitTerminationSeconds(19). - context.close(); + // Close on another thread so this test can observe that shutdown has actually started before + // releasing the in-flight job. This proves the drain contract without an arbitrary sleep. + CompletableFuture closeFuture = CompletableFuture.runAsync(context::close); + awaitExecutorShutdown(executor); + allowCompletion.countDown(); + closeFuture.get(5, TimeUnit.SECONDS); inFlightJob.get(5, TimeUnit.SECONDS); long elapsedMs = (System.nanoTime() - startNanos) / 1_000_000L; @@ -131,14 +135,26 @@ class AsyncGracefulShutdownBehaviorTest { } } - private static void sleepQuietly(long millis) { + private static void awaitQuietly(CountDownLatch latch) { try { - Thread.sleep(millis); - } catch (InterruptedException e) { + latch.await(); + } catch (InterruptedException interrupted) { Thread.currentThread().interrupt(); + throw new IllegalStateException( + "interrupted while waiting for test coordination", interrupted); } } + private static void awaitExecutorShutdown(ThreadPoolTaskExecutor executor) throws Exception { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2); + while (!executor.getThreadPoolExecutor().isShutdown() && System.nanoTime() < deadline) { + Thread.sleep(1L); + } + assertThat(executor.getThreadPoolExecutor().isShutdown()) + .as("context.close must initiate executor shutdown before the in-flight job is released") + .isTrue(); + } + private static String renderedArguments(ILoggingEvent event) { return Arrays.stream(event.getArgumentArray()) .map(String::valueOf) diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverPlatformAutoConfigurationTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverPlatformAutoConfigurationTest.java index 99baba11..951f26a8 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverPlatformAutoConfigurationTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverPlatformAutoConfigurationTest.java @@ -28,42 +28,12 @@ class FileserverPlatformAutoConfigurationTest { .withUserConfiguration(SupportingBeansConfiguration.class); @Test - void theCapabilityIsAbsentUntilItIsExplicitlyEnabled() { - runner.run( - context -> { - assertThat(context).doesNotHaveBean(FileserverWebProperties.class); - assertThat(context).doesNotHaveBean(BlockingTransferExecutor.class); - assertThat(context).doesNotHaveBean(FileserverMetricsPort.class); - assertThat(context).doesNotHaveBean(FileserverPlatformSettings.class); - }); - } - - /** - * A malformed detail setting must not fail a deployment that never wanted the capability. - * - *

    This is the whole reason the settings are bound inside the auto-configuration rather than by - * the global properties scan: a scanned properties class binds — and rejects — regardless of the - * master switch, which turns an optional capability into a mandatory one. - */ - @Test - void detailSettingsAreNotBoundWhileTheCapabilityIsOff() { + void disabledCapabilityIgnoresDetailAndSubFeatureSettings() { runner .withPropertyValues( "app.fileserver-platform.upload.ttl=not-a-duration", "app.fileserver-platform.storage.buffer-size=not-a-size", - "app.fileserver-platform.storage.root=relative/path") - .run( - context -> { - assertThat(context).hasNotFailed(); - assertThat(context).doesNotHaveBean(FileserverPlatformSettings.class); - }); - } - - /** Sub-feature switches are inert until the master switch is on. */ - @Test - void subFeatureSwitchesDoNothingWhileTheMasterSwitchIsOff() { - runner - .withPropertyValues( + "app.fileserver-platform.storage.root=relative/path", "app.fileserver-platform.admin.enabled=true", "app.fileserver-platform.tus.enabled=true", "app.fileserver-platform.httpbis-draft12.enabled=true", @@ -72,6 +42,9 @@ class FileserverPlatformAutoConfigurationTest { context -> { assertThat(context).hasNotFailed(); assertThat(context).doesNotHaveBean(FileserverWebProperties.class); + assertThat(context).doesNotHaveBean(BlockingTransferExecutor.class); + assertThat(context).doesNotHaveBean(FileserverMetricsPort.class); + assertThat(context).doesNotHaveBean(FileserverPlatformSettings.class); assertThat(context).doesNotHaveBean(FileserverCleanupWorker.class); }); } @@ -156,21 +129,6 @@ class FileserverPlatformAutoConfigurationTest { * switched on the R1 publication and its directory initialisation. Each namespace must now move * exactly one capability. */ - @Test - void theHttpPlatformSwitchDoesNotActivateTheOtherFileCapabilities() { - enabled() - .withPropertyValues("app.fileserver.enabled=false", "app.file-export.enabled=false") - .run( - context -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(FileserverPlatformSettings.class); - assertThat(context.getEnvironment().getProperty("app.fileserver.enabled")) - .isEqualTo("false"); - assertThat(context.getEnvironment().getProperty("app.file-export.enabled")) - .isEqualTo("false"); - }); - } - /** * A delegation whose internal mapping does not round-trip must not start. * @@ -187,49 +145,43 @@ class FileserverPlatformAutoConfigurationTest { } @Test - void enablingItAssemblesThePolicyAndTransportBeans() { + void enabledDefaultsAssembleTheExpectedRuntime() { enabled() + .withPropertyValues("app.fileserver.enabled=false", "app.file-export.enabled=false") .run( context -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(FileserverPlatformSettings.class); assertThat(context).hasSingleBean(FileserverWebProperties.class); assertThat(context).hasSingleBean(UploadPolicy.class); assertThat(context).hasSingleBean(DownloadPolicy.class); assertThat(context).hasSingleBean(BlockingTransferExecutor.class); assertThat(context).hasSingleBean(SafeFileFingerprint.class); - }); - } - @Test - void theDesignDefaultsAreTheConservativeOnes() { - enabled() - .run( - context -> { DownloadPolicy download = context.getBean(DownloadPolicy.class); assertThat(download.cacheControl()).isEqualTo("private, no-store"); assertThat(download.inlineAllowed()).isFalse(); assertThat(download.rangeBudget().allowsMultipleRanges()).isFalse(); - assertThat(context.getBean(NginxDelegationProperties.class).enabled()).isFalse(); assertThat(context.getBean(FileserverWebProperties.class).maxBatchParts()) .isEqualTo(16); - }); - } - @Test - void theTransferPoolUsesTheDesignBounds() { - enabled() - .run( - context -> - assertThat( - context.getBean( - dev.caskeleton.adapter.inbound.web.fileserver.config - .TransferExecutorProperties.class)) - .satisfies( - properties -> { - assertThat(properties.coreSize()).isEqualTo(8); - assertThat(properties.maxSize()).isEqualTo(32); - assertThat(properties.queueCapacity()).isEqualTo(64); - })); + assertThat( + context.getBean( + dev.caskeleton.adapter.inbound.web.fileserver.config + .TransferExecutorProperties.class)) + .satisfies( + properties -> { + assertThat(properties.coreSize()).isEqualTo(8); + assertThat(properties.maxSize()).isEqualTo(32); + assertThat(properties.queueCapacity()).isEqualTo(64); + }); + + assertThat(context.getEnvironment().getProperty("app.fileserver.enabled")) + .isEqualTo("false"); + assertThat(context.getEnvironment().getProperty("app.file-export.enabled")) + .isEqualTo("false"); + }); } @Test diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverRuntimeAssemblyTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverRuntimeAssemblyTest.java index 5258fa06..4b55a335 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverRuntimeAssemblyTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/fileserver/FileserverRuntimeAssemblyTest.java @@ -59,15 +59,12 @@ class FileserverRuntimeAssemblyTest { .withUserConfiguration(AssemblySupport.class); @Test - void enablingTheCapabilityStartsTheContext() { - enabled().run(context -> assertThat(context).hasNotFailed()); - } - - @Test - void everyStoragePortHasAProductionImplementation() { + void enabledBaselineAssemblesTheCompleteRuntime() { enabled() .run( context -> { + assertThat(context).hasNotFailed(); + List.of( UploadStorageGateway.class, UploadContentGateway.class, @@ -81,14 +78,7 @@ class FileserverRuntimeAssemblyTest { OrphanScanPort.class, FileVerificationService.class) .forEach(port -> assertThat(context).hasSingleBean(port)); - }); - } - @Test - void everyApplicationServiceIsWired() { - enabled() - .run( - context -> { List.of( UploadApplicationService.class, FinalizeUploadService.class, @@ -101,17 +91,14 @@ class FileserverRuntimeAssemblyTest { TransferAdmissionController.class, UploadIdentifierFactory.class) .forEach(service -> assertThat(context).hasSingleBean(service)); - }); - } - @Test - void authorizationAndAuditAreNeverLeftUnbound() { - enabled() - .run( - context -> { assertThat(context).hasSingleBean(FileAccessPolicy.class); assertThat(context).hasSingleBean(AdminAuditPort.class); assertThat(context).hasSingleBean(FileserverAuditPort.class); + assertThat(context.getBean(FileAccessPolicy.class)) + .isInstanceOf(RoleBasedFileAccessPolicy.class); + assertThat(context).doesNotHaveBean(FileserverAdminService.class); + assertThat(context).doesNotHaveBean(FileserverCleanupWorker.class); }); } @@ -154,20 +141,6 @@ class FileserverRuntimeAssemblyTest { .hasMessageContaining("must not be active under the 'prod' profile")); } - @Test - void theRoleBasedPolicyIsSelectedByConfigurationAlone() { - enabled() - .run( - context -> - assertThat(context.getBean(FileAccessPolicy.class)) - .isInstanceOf(RoleBasedFileAccessPolicy.class)); - } - - @Test - void theAdminPlaneStaysAbsentUntilItsOwnSwitchIsSet() { - enabled().run(context -> assertThat(context).doesNotHaveBean(FileserverAdminService.class)); - } - @Test void theAdminPlaneAppearsOnItsOwnSwitch() { enabled() @@ -175,11 +148,6 @@ class FileserverRuntimeAssemblyTest { .run(context -> assertThat(context).hasSingleBean(FileserverAdminService.class)); } - @Test - void theCleanupWorkerStaysAbsentUntilItsOwnSwitchIsSet() { - enabled().run(context -> assertThat(context).doesNotHaveBean(FileserverCleanupWorker.class)); - } - @Test void theCleanupWorkerAppearsOnItsOwnSwitch() { enabled() diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientAutoConfigurationTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientAutoConfigurationTest.java index 305f6013..7d9feb50 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientAutoConfigurationTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/httpclient/HttpClientAutoConfigurationTest.java @@ -36,7 +36,7 @@ class HttpClientAutoConfigurationTest { .withPropertyValues("app.httpclient.enabled=true"); @Test - void publishesEveryCallerFacingEntryPoint() { + void paymentProfilePublishesCallerEntryPointsWithoutLeakingActuatorDetails() { runner .withPropertyValues(paymentClient()) .run( @@ -47,6 +47,15 @@ class HttpClientAutoConfigurationTest { assertThat(context).hasSingleBean(GenericHttpGateway.class); assertThat(context).hasSingleBean(ReactiveSseGateway.class); assertThat(context).hasSingleBean(DynamicTargetGateway.class); + + Map report = + new HttpClientActuatorEndpoint(context.getBean(ClientRuntimeRegistry.class)) + .clients(); + assertThat(report).containsKey("payment"); + assertThat(report.toString()) + .doesNotContain("https://payment.test") + .doesNotContain("secret") + .doesNotContain("Bearer"); }); } @@ -95,23 +104,6 @@ class HttpClientAutoConfigurationTest { }); } - @Test - void actuatorReportExposesNoTargetOrCredentialDetail() { - runner - .withPropertyValues(paymentClient()) - .run( - context -> { - Map report = - new HttpClientActuatorEndpoint(context.getBean(ClientRuntimeRegistry.class)) - .clients(); - assertThat(report).containsKey("payment"); - assertThat(report.toString()) - .doesNotContain("https://payment.test") - .doesNotContain("secret") - .doesNotContain("Bearer"); - }); - } - private static String[] paymentClient() { return new String[] { "app.httpclient.clients[0].name=payment", diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaResolvedDataSourceValidationTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaResolvedDataSourceValidationTest.java index 9cd96853..5fb8f027 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaResolvedDataSourceValidationTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaResolvedDataSourceValidationTest.java @@ -4,15 +4,19 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import com.zaxxer.hikari.HikariDataSource; import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.reflect.Proxy; import java.nio.file.Files; import java.nio.file.Path; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.jdbc.datasource.AbstractDataSource; /** * The validator reads the pool that was built, not a namespace nobody supplies (JPA-INT-002). @@ -69,42 +73,93 @@ class JpaResolvedDataSourceValidationTest { @Test @DisplayName("an unreachable database fails at startup, not at the first query") void anUnreachableDatabaseFailsAtStartup() { - HikariDataSource pool = new HikariDataSource(); - pool.setJdbcUrl("jdbc:postgresql://127.0.0.1:1/nothing-listens-here"); - pool.setDriverClassName("org.postgresql.Driver"); - pool.setMaximumPoolSize(2); - pool.setConnectionTimeout(250L); - pool.setInitializationFailTimeout(-1); + AbstractDataSource unreachable = + new AbstractDataSource() { + @Override + public Connection getConnection() throws SQLException { + throw new SQLException("fixture connection refused"); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + return getConnection(); + } + }; assertThatThrownBy( () -> new JpaDataSourceProfileValidator(new PostgreSqlVersionPolicy()) - .validateResolved(pool, true)) + .validateResolved(unreachable, true)) .as( - "the alternative is a context that starts, reports healthy, and fails on whoever sends " - + "the first request") - .hasMessageContaining("could not open a connection"); - pool.close(); + "the validator owns the DataSource failure mapping; TCP timeout behaviour belongs to " + + "the driver/integration lane") + .hasMessageContaining("could not open a connection") + .hasRootCauseMessage("fixture connection refused"); } @Test @DisplayName("the product check applies only where PostgreSQL is the selected vendor") void theProductCheckFollowsTheVendorSelector() { - HikariDataSource h2 = new HikariDataSource(); - h2.setJdbcUrl("jdbc:h2:mem:validation-vendor"); - h2.setMaximumPoolSize(10); - h2.setConnectionTimeout(5_000L); - JpaDataSourceProfileValidator validator = new JpaDataSourceProfileValidator(new PostgreSqlVersionPolicy()); - assertThatCode(() -> validator.validateResolved(h2, false)) + AbstractDataSource mustNotConnect = + new AbstractDataSource() { + @Override + public Connection getConnection() { + throw new AssertionError( + "non-PostgreSQL profiles must not open a validation connection"); + } + + @Override + public Connection getConnection(String username, String password) { + return getConnection(); + } + }; + assertThatCode(() -> validator.validateResolved(mustNotConnect, false)) .as("local development runs H2 by design; demanding PostgreSQL there refuses every laptop") .doesNotThrowAnyException(); - assertThatThrownBy(() -> validator.validateResolved(h2, true)) + + AbstractDataSource h2Metadata = dataSourceReporting("H2", 2); + assertThatThrownBy(() -> validator.validateResolved(h2Metadata, true)) .as("and where PostgreSQL is selected, H2 is still refused") .hasMessageContaining("PostgreSQL"); - h2.close(); + } + + private static AbstractDataSource dataSourceReporting(String product, int majorVersion) { + DatabaseMetaData metadata = + (DatabaseMetaData) + Proxy.newProxyInstance( + DatabaseMetaData.class.getClassLoader(), + new Class[] {DatabaseMetaData.class}, + (proxy, method, args) -> + switch (method.getName()) { + case "getDatabaseProductName" -> product; + case "getDatabaseMajorVersion" -> majorVersion; + default -> throw new UnsupportedOperationException(method.getName()); + }); + Connection connection = + (Connection) + Proxy.newProxyInstance( + Connection.class.getClassLoader(), + new Class[] {Connection.class}, + (proxy, method, args) -> + switch (method.getName()) { + case "getMetaData" -> metadata; + case "close" -> null; + default -> throw new UnsupportedOperationException(method.getName()); + }); + return new AbstractDataSource() { + @Override + public Connection getConnection() { + return connection; + } + + @Override + public Connection getConnection(String username, String password) { + return connection; + } + }; } /** Strips {@code //} and block comments, leaving the code a binder would act on. */ diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/notification/NotificationPayloadAtRestContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/notification/NotificationPayloadAtRestContractTest.java index 84aa5d55..2fc2e905 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/notification/NotificationPayloadAtRestContractTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/notification/NotificationPayloadAtRestContractTest.java @@ -27,6 +27,8 @@ import org.junit.jupiter.api.Test; */ class NotificationPayloadAtRestContractTest { + private static final List PRODUCTION_SOURCES = loadProductionSources(); + @Test @DisplayName("a template variable can carry arbitrary caller text, so it cannot be restricted") void aTemplateVariableCanCarryArbitraryCallerText() { @@ -48,9 +50,9 @@ class NotificationPayloadAtRestContractTest { @DisplayName("PAYLOAD_ENCRYPTION protects callbacks and nothing on the accept path") void payloadEncryptionProtectsCallbacksOnly() { List consumers = - productionSources() - .filter(path -> read(path).contains("SecretPurpose.PAYLOAD_ENCRYPTION")) - .map(path -> path.getFileName().toString()) + PRODUCTION_SOURCES.stream() + .filter(source -> source.text().contains("SecretPurpose.PAYLOAD_ENCRYPTION")) + .map(source -> source.path().getFileName().toString()) .sorted() .toList(); @@ -73,12 +75,16 @@ class NotificationPayloadAtRestContractTest { @DisplayName("the write path is reachable, and cannot be assembled without the protection") void theWritePathIsProtectedWhereverItIsReachable() { List importers = - productionSources() + PRODUCTION_SOURCES.stream() .filter( - path -> - !path.getFileName().toString().equals("NotificationJpaPersistenceFacade.java")) - .filter(path -> read(path).contains("NotificationJpaPersistenceFacade")) - .map(path -> path.getFileName().toString()) + source -> + !source + .path() + .getFileName() + .toString() + .equals("NotificationJpaPersistenceFacade.java")) + .filter(source -> source.text().contains("NotificationJpaPersistenceFacade")) + .map(source -> source.path().getFileName().toString()) .toList(); assertThat(importers) @@ -162,7 +168,7 @@ class NotificationPayloadAtRestContractTest { .isTrue(); } - private static Stream productionSources() { + private static List loadProductionSources() { Path root = repositoryRoot().resolve("src"); return Stream.of( "application-core", @@ -178,12 +184,14 @@ class NotificationPayloadAtRestContractTest { .filter(path -> path.toString().endsWith(".java")) .filter(path -> path.toString().contains("/src/main/java/")) .filter(path -> !path.toString().contains("/build/")) + .map(path -> new ProductionSource(path, read(path))) .toList() .stream(); } catch (IOException unreadable) { throw new UncheckedIOException(unreadable); } - }); + }) + .toList(); } private static String read(Path path) { @@ -205,4 +213,6 @@ class NotificationPayloadAtRestContractTest { } return candidate; } + + private record ProductionSource(Path path, String text) {} } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisCapabilityCompositionTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisCapabilityCompositionTest.java index d1e8c906..7a555ef2 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisCapabilityCompositionTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisCapabilityCompositionTest.java @@ -74,22 +74,11 @@ class RedisCapabilityCompositionTest { assertThat(context).hasNotFailed(); assertThat(context).hasSingleBean(CacheRegionPort.class); assertThat(context.getBeansOfType(EdgeRateLimitPort.class)).isEmpty(); + assertThat(context.getBeansOfType(IdempotencyStorePortV2.class)).isEmpty(); assertThat(context.getBeansOfType(DistributedLeasePort.class)).isEmpty(); }); } - @Test - @DisplayName("the rate-limit provider composes the port the web bridge requires") - void rateLimitProviderComposesThePort() { - runner - .withPropertyValues("ca-skeleton.capabilities.rate-limit.provider=redis") - .run( - context -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(EdgeRateLimitPort.class); - }); - } - @Test @DisplayName("a rate limiter with no configured policy is refused at startup") void aRateLimiterWithoutPoliciesIsRefused() { @@ -111,30 +100,6 @@ class RedisCapabilityCompositionTest { }); } - @Test - @DisplayName("the lease provider composes the lease port") - void leaseProviderComposesThePort() { - runner - .withPropertyValues("ca-skeleton.capabilities.lease.provider=redis") - .run( - context -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(DistributedLeasePort.class); - }); - } - - @Test - @DisplayName("the idempotency provider composes the owner-safe V2 store") - void idempotencyProviderComposesTheStore() { - runner - .withPropertyValues("ca-skeleton.capabilities.idempotency.provider=redis") - .run( - context -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(IdempotencyStorePortV2.class); - }); - } - @Test @DisplayName("every selected role composes at once, under one namespace") void allRolesComposeTogether() { diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisReadinessGroupPostProcessorTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisReadinessGroupPostProcessorTest.java index 352441f7..706b4b22 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisReadinessGroupPostProcessorTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/autoconfigure/redis/RedisReadinessGroupPostProcessorTest.java @@ -2,13 +2,16 @@ package dev.caskeleton.bootstrap.autoconfigure.redis; import static org.assertj.core.api.Assertions.assertThat; +import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.RedisCorrectnessRoles; import dev.caskeleton.bootstrap.contract.support.ShippedConfiguration; import dev.caskeleton.bootstrap.runtime.ReadinessGroup; +import dev.caskeleton.bootstrap.runtime.SecretSourceValidator; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; @@ -26,6 +29,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySourcesPropertyResolver; +import org.springframework.core.env.StandardEnvironment; /** * Starts a real Actuator health context in each of the three deployment shapes. @@ -145,26 +149,29 @@ class RedisReadinessGroupPostProcessorTest { } @Test - @DisplayName("every correctness role gates readiness, not only the one spot-checked above") - void eachCorrectnessRoleGatesReadiness() throws IOException { - Map selectors = - Map.of( - "ca-skeleton.security.auth-mode", "redis-session", - "ca-skeleton.capabilities.idempotency.provider", "redis", - "ca-skeleton.capabilities.rate-limit.provider", "redis", - "ca-skeleton.capabilities.lease.provider", "redis"); - for (Map.Entry selector : selectors.entrySet()) { - Map properties = redisOn(); - properties.put(selector.getKey(), selector.getValue()); - try (ConfigurableApplicationContext context = boot(properties)) { - assertThat( - context - .getBean(HealthEndpointGroups.class) - .get("readiness") - .isMember("redisRequired")) - .as("%s=%s", selector.getKey(), selector.getValue()) - .isTrue(); - } + @DisplayName("every correctness selector makes the post-processor append Redis readiness") + void eachCorrectnessSelectorAppendsRedisReadiness() throws IOException { + for (Map.Entry selector : RedisCorrectnessRoles.selectors().entrySet()) { + StandardEnvironment environment = new StandardEnvironment(); + environment + .getPropertySources() + .addFirst( + new org.springframework.core.env.MapPropertySource( + "test", + Map.of( + ReadinessGroup.INCLUDE_PROPERTY, + shippedReadinessInclude(), + SecretSourceValidator.REDIS_ENABLED_PROPERTY, + "true", + selector.getKey(), + selector.getValue()))); + + new RedisReadinessGroupPostProcessor() + .postProcessEnvironment(environment, new SpringApplication()); + + assertThat(environment.getProperty(ReadinessGroup.INCLUDE_PROPERTY)) + .as("%s=%s", selector.getKey(), selector.getValue()) + .contains("readinessState", RedisCorrectnessRoles.REQUIRED_HEALTH_CONTRIBUTOR); } } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ConditionalTransportQualificationContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ConditionalTransportQualificationContractTest.java index 5220d49e..93b87c7f 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ConditionalTransportQualificationContractTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ConditionalTransportQualificationContractTest.java @@ -76,7 +76,7 @@ class ConditionalTransportQualificationContractTest { Files.readString(repositoryRoot().resolve(".github/workflows/ci-quality-gates.yml")); assertThat(workflow) - .contains("./gradlew conditionalTransportQualification") + .contains("./gradlew :conditionalTransportQualification") // `--continue` would let a failing transport lane be reported alongside a green overall // step, which is the one thing a no-skip qualification may not do. .doesNotContain("conditionalTransportQualification --continue"); diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ContractSuiteCompletenessTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ContractSuiteCompletenessTest.java index 33d6dc0a..fe47fc6f 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ContractSuiteCompletenessTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/ContractSuiteCompletenessTest.java @@ -16,17 +16,8 @@ import org.junit.jupiter.api.Test; * deleted or renamed) causes a {@link ClassNotFoundException} which is collected and reported as a * single assertion failure listing all missing FQCNs. * - *

    Gate numbering note

    - * - *
      - *
    • Gates 1–9 are the base contracts verified by this test. - *
    • Gate #10 (OpenAPI drift) lives in {@code sample-portfolio} and is verified by its own - * module test. It is not loadable from {@code app-bootstrap}'s classpath and is therefore - * intentionally excluded here. - *
    • Gate #11 (sample removal smoke) is {@code - * dev.caskeleton.bootstrap.contract.SampleRemovalSmokeContractTest}, which is included in the - * 9-base enumeration below as the envelope/schema gate covers it (see concern mapping). - *
    + *

    The enumeration intentionally covers production contract tests only. Preview/reference modules + * are outside the release-blocking application contract surface. */ class ContractSuiteCompletenessTest { diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/DeveloperExperienceContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/DeveloperExperienceContractTest.java index 2b902f0a..0b501c61 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/DeveloperExperienceContractTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/DeveloperExperienceContractTest.java @@ -7,16 +7,11 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -38,21 +33,19 @@ class DeveloperExperienceContractTest { } @Test - void bootstrapIsOneOrderedFiveStageGradleEntrypoint() throws IOException { + void bootstrapIsOneOrderedFourStageGradleEntrypoint() throws IOException { String build = read("src/build.gradle"); assertThat(build) .contains("tasks.register('bootstrapCompile')") .contains("tasks.register('bootstrapDependencies'") .contains("tasks.register('bootstrapMigrateAndStart'") - .contains("tasks.register('bootstrapSampleContract'") .contains("tasks.register('bootstrapSmoke'") .contains("tasks.register('bootstrap')"); assertThat(build) .contains("bootstrapDependencies.configure { dependsOn bootstrapCompile }") .contains("bootstrapMigrateAndStart.configure { dependsOn bootstrapDependencies }") - .contains("bootstrapSampleContract.configure { dependsOn bootstrapMigrateAndStart }") - .contains("bootstrapSmoke.configure { dependsOn bootstrapSampleContract }"); + .contains("bootstrapSmoke.configure { dependsOn bootstrapMigrateAndStart }"); } @Test @@ -225,33 +218,21 @@ class DeveloperExperienceContractTest { @Test void dockerDependencyCacheStagesDeclareTheCompleteConfigurationRegistry() throws IOException { - for (String dockerfile : new String[] {"src/Dockerfile", "src/Dockerfile.sample"}) { - assertDockerDependencyCacheContract(dockerfile, read(dockerfile)); - } + assertDockerDependencyCacheContract("src/Dockerfile", read("src/Dockerfile")); } @Test - void dockerBuildsConsumeOnlyGradleStagedExecutableJars() throws IOException { - Map executableModules = - Map.of( - "src/Dockerfile", "app-bootstrap", - "src/Dockerfile.sample", "sample-portfolio"); - - executableModules.forEach( - (dockerfile, module) -> { - try { - String content = read(dockerfile); - assertDockerJarSelectionContract(dockerfile, module, content); - assertThat(read("src/" + module + "/build.gradle")) - .contains("tasks.register('stageDockerJar', Sync)") - .contains("dependsOn tasks.named('bootJar')") - .contains("from(tasks.named('bootJar').flatMap { it.archiveFile })") - .contains("into(layout.buildDirectory.dir('docker'))") - .contains("rename { 'application.jar' }"); - } catch (IOException exception) { - throw new IllegalStateException("failed to inspect " + dockerfile, exception); - } - }); + void dockerBuildConsumesOnlyTheGradleStagedExecutableJar() throws IOException { + String dockerfile = "src/Dockerfile"; + String module = "app-bootstrap"; + String content = read(dockerfile); + assertDockerJarSelectionContract(dockerfile, module, content); + assertThat(read("src/" + module + "/build.gradle")) + .contains("tasks.register('stageDockerJar', Sync)") + .contains("dependsOn tasks.named('bootJar')") + .contains("from(tasks.named('bootJar').flatMap { it.archiveFile })") + .contains("into(layout.buildDirectory.dir('docker'))") + .contains("rename { 'application.jar' }"); } @Test @@ -334,135 +315,10 @@ class DeveloperExperienceContractTest { } } - @Test - void aBuildWithNoGitMetadataConfiguresAndIsNotCalledARelease(@TempDir Path fixtureRoot) - throws Exception { - // A source archive with no `.git` and no `-PgitRevision`. The root build used to throw during - // configuration here — "A 7-40 character hexadecimal source revision is required" — so - // `./gradlew test` on an unpacked tarball failed before it compiled anything. Release - // traceability was being enforced on every task in the build. - Path fixtureSrc = fixtureRoot.resolve("src"); - copyDependencyCacheStageInputs(fixtureSrc); - assertThat(fixtureSrc.resolve(".git")).doesNotExist(); - - ProcessResult configured = runGitlessGradle(fixtureSrc, "help"); - - assertThat(configured.exitCode()).as(configured.output()).isZero(); - assertThat(configured.output()).contains("BUILD SUCCESSFUL"); - - // And the other half: such a build may not call itself a release. verifyReleaseProvenance is - // where the revision is required, and releaseCheck is what depends on it. - ProcessResult provenance = runGitlessGradle(fixtureSrc, "verifyReleaseProvenance"); - - assertThat(provenance.exitCode()).as(provenance.output()).isNotZero(); - assertThat(provenance.output()).contains("no source revision"); - } - - @Test - void anAttestedRevisionSatisfiesReleaseProvenanceWithoutGit(@TempDir Path fixtureRoot) - throws Exception { - Path fixtureSrc = fixtureRoot.resolve("src"); - copyDependencyCacheStageInputs(fixtureSrc); - - ProcessResult result = - runGitlessGradle( - fixtureSrc, - "verifyReleaseProvenance", - "-PgitRevision=0123456789abcdef0123456789abcdef01234567"); - - assertThat(result.exitCode()).as(result.output()).isZero(); - assertThat(result.output()).contains("verifyReleaseProvenance: OK"); - } - - private static void copyDependencyCacheStageInputs(Path fixtureSrc) throws IOException { - Path sourceRoot = REPOSITORY_ROOT.resolve("src"); - copyFile(sourceRoot.resolve("gradlew"), fixtureSrc.resolve("gradlew")); - fixtureSrc.resolve("gradlew").toFile().setExecutable(true); - copyTree(sourceRoot.resolve("gradle"), fixtureSrc.resolve("gradle")); - copyTree(sourceRoot.resolve("config"), fixtureSrc.resolve("config")); - // The convention plugins, whole. The walk below copies files named exactly `build.gradle`, - // which - // picks up build-logic's own build script and misses the precompiled script plugins beside it — - // so the fixture resolved `pluginManagement { includeBuild('build-logic') }` against a build - // that declared no plugins, and every leaf failed on an unknown plugin id. - copyTree(sourceRoot.resolve("build-logic"), fixtureSrc.resolve("build-logic")); - try (Stream paths = Files.walk(sourceRoot)) { - paths - .filter(Files::isRegularFile) - .filter( - path -> - path.getFileName().toString().equals("build.gradle") - || path.getFileName().toString().equals("gradle.lockfile") - || path.equals(sourceRoot.resolve("settings.gradle"))) - .filter(path -> !path.startsWith(sourceRoot.resolve("build"))) - .forEach( - source -> { - try { - copyFile(source, fixtureSrc.resolve(sourceRoot.relativize(source))); - } catch (IOException exception) { - throw new IllegalStateException( - "failed to copy dependency-cache input " + source, exception); - } - }); - } - } - - private static ProcessResult runGitlessGradle(Path fixtureSrc, String... arguments) - throws Exception { - Path outputFile = fixtureSrc.resolve("gitless-run.log"); - List command = new ArrayList<>(List.of("./gradlew")); - command.addAll(List.of(arguments)); - command.addAll(List.of("--no-daemon", "--console=plain")); - Process process = - new ProcessBuilder(command) - .directory(fixtureSrc.toFile()) - .redirectErrorStream(true) - .redirectOutput(outputFile.toFile()) - .start(); - boolean completed = process.waitFor(2, TimeUnit.MINUTES); - if (!completed) { - process.destroyForcibly(); - process.waitFor(); - } - String output = Files.readString(outputFile); - return new ProcessResult(completed ? process.exitValue() : -1, output); - } - - private static void copyTree(Path sourceRoot, Path targetRoot) throws IOException { - try (Stream paths = Files.walk(sourceRoot)) { - paths - .filter(Files::isRegularFile) - .forEach( - source -> { - try { - copyFile(source, targetRoot.resolve(sourceRoot.relativize(source))); - } catch (IOException exception) { - throw new IllegalStateException("failed to copy tree input " + source, exception); - } - }); - } - } - - private static void copyFile(Path source, Path target) throws IOException { - Files.createDirectories(target.getParent()); - Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); - } - private static String read(String relative) throws IOException { return Files.readString(REPOSITORY_ROOT.resolve(relative)); } - /** - * Reads {@code src/.env.local.example}, the tracked file a developer copies to start the local - * stack — not {@code src/.env}, which is operator input and absent from every clean checkout. - * - *

    Pointing this at {@code src/.env} meant the port contract below was only ever checked on a - * machine that already had a working setup. It was checking nothing on the one machine where a - * new developer's first run happens. It caught a real drift the moment it was pointed here: the - * example named port 5432 while Compose publishes 5433. - * - *

    Not {@code .env.example} either — that one deliberately leaves connection values blank. - */ private static String envValue(String key) throws IOException { String prefix = key + "="; return read("src/.env.local.example") @@ -471,7 +327,7 @@ class DeveloperExperienceContractTest { .filter(line -> line.startsWith(prefix)) .map(line -> line.substring(prefix.length()).trim()) .findFirst() - .orElseThrow(() -> new AssertionError("src/.env does not declare " + key)); + .orElseThrow(() -> new AssertionError("src/.env.local.example does not declare " + key)); } private static Map parseYamlMap(String source) { @@ -524,6 +380,4 @@ class DeveloperExperienceContractTest { throw new IllegalStateException( "repository root not found from " + Paths.get("").toAbsolutePath()); } - - private record ProcessResult(int exitCode, String output) {} } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/SampleRemovalSmokeContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/SampleRemovalSmokeContractTest.java deleted file mode 100644 index 02c578c3..00000000 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/SampleRemovalSmokeContractTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package dev.caskeleton.bootstrap.contract; - -import static org.assertj.core.api.Assertions.assertThat; - -import dev.caskeleton.bootstrap.contract.support.RepositoryContractResources; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.yaml.snakeyaml.Yaml; - -/** - * Gate #11 (sample-removal smoke) of the contract-verification suite. The skeleton ships a - * reference domain ({@code sample-portfolio}) that remains in the template as a fixture. Production - * code and the sample-off contract build must not depend on it. A downstream adopter may delete the - * module only after the sample-off build is green. - * - *

    The contract is a build/test classpath matrix, not a Spring runtime profile: - * - *

      - *
    1. production modules reference the sample only through the app-bootstrap sample fixture - * configuration; - *
    2. app-bootstrap core test sources compile without importing sample types; - *
    3. the sample-off source set removes the fixture from its compile/runtime classpaths; - *
    4. sample-on and sample-off are both release-blocking CI jobs. - *
    - * - *

    The build-graph scan is the no-datasource proxy for "core app/context boots green without the - * sample": in this template the only application content is the sample, so the meaningful, - * infrastructure-free guarantee is that production never depends on it - * (feature-contract-verification-test-suite §구현 가이드 §6, 판정 기준 Required tests). - */ -class SampleRemovalSmokeContractTest { - - /** A Gradle dependency line: {@code project(':sample-portfolio')}. */ - private static final Pattern SAMPLE_DEP = - Pattern.compile("^\\s*([A-Za-z]+)\\s+project\\(\\s*['\"]:sample-portfolio['\"]\\s*\\)"); - - @Test - void productionModulesReferenceSampleOnlyAsTestFixtureDependency() throws IOException { - RepositoryContractResources resources = RepositoryContractResources.fromSystemProperty(); - - List violations = new ArrayList<>(); - for (String sourcePath : registeredProductionSourcePaths(resources)) { - Path buildFile = resources.requireTrackedFile(sourcePath + "/build.gradle"); - for (String line : Files.readAllLines(buildFile)) { - Matcher m = SAMPLE_DEP.matcher(line); - if (m.find() && !m.group(1).startsWith("test") && !m.group(1).equals("sampleFixture")) { - violations.add( - sourcePath - + ": '" - + line.trim() - + "' (configuration '" - + m.group(1) - + "' is not test-scoped)"); - } - } - } - - assertThat(violations) - .as( - "sample-portfolio must be a test-only dependency of every production module so " - + "the sample can be removed without breaking the production build/runtime") - .isEmpty(); - } - - private static List registeredProductionSourcePaths(RepositoryContractResources resources) - throws IOException { - Path registry = resources.requireTrackedFile("src/config/architecture/modules.json"); - final Map root; - try (InputStream input = Files.newInputStream(registry)) { - root = new Yaml().load(input); - } - @SuppressWarnings("unchecked") - List> modules = (List>) root.get("modules"); - assertThat(modules).as("architecture registry must declare its leaf modules").isNotEmpty(); - assertThat(modules.stream().filter(row -> "sample-portfolio".equals(row.get("id"))).count()) - .as("architecture registry must declare exactly one sample fixture leaf") - .isEqualTo(1); - - return modules.stream() - .filter(row -> !"sample-portfolio".equals(row.get("id"))) - .map(row -> (String) row.get("source_path")) - .toList(); - } - - @Test - void appBootstrapCoreTestsDoNotImportSamplePortfolio() throws IOException { - Path testJava = - RepositoryContractResources.fromSystemProperty() - .requireTrackedDirectory("src/app-bootstrap/src/test/java"); - List imports; - try (Stream files = Files.walk(testJava)) { - imports = - files - .filter(path -> path.toString().endsWith(".java")) - .flatMap( - path -> { - try { - return Files.readAllLines(path).stream() - .filter( - line -> line.startsWith("import dev.caskeleton.sample.portfolio.")) - .map(line -> testJava.relativize(path) + ": " + line.trim()); - } catch (IOException exception) { - throw new IllegalStateException("Cannot inspect " + path, exception); - } - }) - .sorted() - .toList(); - } - - assertThat(imports) - .as( - "app-bootstrap core tests must compile without sample-portfolio; sample-specific " - + "contract checks belong in the sample module") - .isEmpty(); - } - - @Test - void sampleOffCompilationAndStrictRuntimeProofAreDeclared() throws IOException { - RepositoryContractResources resources = RepositoryContractResources.fromSystemProperty(); - Path buildFile = resources.requireTrackedFile("src/app-bootstrap/build.gradle"); - String build = Files.readString(buildFile); - - assertThat(build).contains("sampleFixture project(':sample-portfolio')"); - assertThat(build) - .contains("java.srcDirs = sourceSets.test.java.srcDirs") - .contains("java.srcDir 'src/sampleOffTest/java'") - .contains("tasks.register('sampleOffCompile')") - .contains("dependsOn tasks.named(sourceSets.sampleOffTest.classesTaskName)") - .contains("def sampleOffQualification = registerStrictQualificationTest(") - .contains("name: 'sampleOffTest'") - .contains("sourceSet: sourceSets.sampleOffTest") - .contains("dev.caskeleton.bootstrap.contract.SampleOffClasspathContractTest") - .contains("systemProperty 'ca.sample.mode', 'off'") - .doesNotContain("tasks.register('sampleOffTest', Test)"); - resources.requireTrackedFile( - "src/app-bootstrap/src/sampleOffTest/java/dev/caskeleton/bootstrap/contract/" - + "SampleOffClasspathContractTest.java"); - } - - @Test - void runtimeSampleToggleIsNotRegistered() throws IOException { - Path registry = - RepositoryContractResources.fromSystemProperty() - .requireTrackedFile("docs/registries/env-keys.yaml"); - - assertThat(Files.readString(registry)) - .as("sample-off is a build/test mode, so no APP_SAMPLE_ENABLED runtime toggle may remain") - .doesNotContain("- name: APP_SAMPLE_ENABLED"); - } - - @Test - void sampleOffCiJobIsReleaseBlocking() throws IOException { - // The gate matrix half of this assertion is gone with the matrix. It read .github/ - // ci-gate-matrix.yml and required a row declaring that the sample-off job exists and is - // release-blocking — a third copy of what the workflow itself says in `jobs:` and in - // `release-gate.needs`. Checking a register against the thing it registers is work that only - // ever finds a disagreement between two descriptions of one fact. - RepositoryContractResources resources = RepositoryContractResources.fromSystemProperty(); - String workflow = - Files.readString(resources.requireTrackedFile(".github/workflows/ci-quality-gates.yml")); - - assertThat(workflow).contains("\n sample-off:\n"); - assertThat(workflow).contains(":app-bootstrap:sampleOffTest"); - assertThat(workflow).contains("\n - sample-off\n"); - } -} diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingAuthorityContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingAuthorityContractTest.java index 7a76ce8f..fe41f9f2 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingAuthorityContractTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingAuthorityContractTest.java @@ -43,17 +43,21 @@ class MessagingAuthorityContractTest { private static final Pattern MESSAGING_PREFIX_CONSTANT = Pattern.compile("@ConfigurationProperties\\(prefix = MessagingSettings\\.PREFIX\\)"); + private static final List MESSAGING_SOURCES = + loadSources("adapter/outbound/messaging", "messaging", "app-bootstrap"); + private static final List CORE_SOURCES = + loadSources("application-core", "domain-core"); + @Test @DisplayName("the owners of app.messaging are the two this wave knows about, and no more") void theSettingsOwnersAreTheKnownTwo() { List owners = - productionSources() + MESSAGING_SOURCES.stream() .filter( - path -> { - String source = read(path); - return MESSAGING_PREFIX.matcher(source).find() - || MESSAGING_PREFIX_CONSTANT.matcher(source).find(); - }) + source -> + MESSAGING_PREFIX.matcher(source.text()).find() + || MESSAGING_PREFIX_CONSTANT.matcher(source.text()).find()) + .map(ProductionSource::path) .toList(); assertThat(owners.stream().map(path -> path.getFileName().toString()).sorted()) @@ -67,9 +71,9 @@ class MessagingAuthorityContractTest { @DisplayName("exactly one production type implements the application's outbox publish port") void exactlyOnePublicationPath() { List implementations = - productionSources() - .filter(path -> read(path).contains("implements OutboxMessagePublishPort")) - .map(path -> path.getFileName().toString().replace(".java", "")) + MESSAGING_SOURCES.stream() + .filter(source -> source.text().contains("implements OutboxMessagePublishPort")) + .map(source -> source.path().getFileName().toString().replace(".java", "")) .sorted() .toList(); @@ -84,14 +88,17 @@ class MessagingAuthorityContractTest { @Test @DisplayName("the adapter's generic publisher stays out of the application's core") void theLegacyGenericPublisherDoesNotLeakIntoCore() { - List core = sourcesUnder("application-core", "domain-core").toList(); + List core = CORE_SOURCES.stream().map(ProductionSource::path).toList(); assertThat(core) .as("a scan that reached no source would report every leak as absent") .isNotEmpty(); List leaks = - core.stream().filter(path -> read(path).contains("adapter.outbound.messaging")).toList(); + CORE_SOURCES.stream() + .filter(source -> source.text().contains("adapter.outbound.messaging")) + .map(ProductionSource::path) + .toList(); assertThat(leaks) .as( @@ -100,11 +107,7 @@ class MessagingAuthorityContractTest { .isEmpty(); } - private static Stream productionSources() { - return sourcesUnder("adapter/outbound/messaging", "messaging", "app-bootstrap"); - } - - private static Stream sourcesUnder(String... moduleRoots) { + private static List loadSources(String... moduleRoots) { Path root = repositoryRoot().resolve("src"); return Stream.of(moduleRoots) .map(root::resolve) @@ -116,12 +119,14 @@ class MessagingAuthorityContractTest { .filter(path -> path.toString().endsWith(".java")) .filter(path -> path.toString().contains("/src/main/java/")) .filter(path -> !path.toString().contains("/build/")) + .map(path -> new ProductionSource(path, read(path))) .toList() .stream(); } catch (IOException unreadable) { throw new UncheckedIOException(unreadable); } - }); + }) + .toList(); } private static String read(Path path) { @@ -143,4 +148,6 @@ class MessagingAuthorityContractTest { } return candidate; } + + private record ProductionSource(Path path, String text) {} } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingCapabilityRegistryContractTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingCapabilityRegistryContractTest.java index 18e495ef..a1c2edc3 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingCapabilityRegistryContractTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/messaging/MessagingCapabilityRegistryContractTest.java @@ -377,13 +377,15 @@ class MessagingCapabilityRegistryContractTest { @Test void messagingQualificationSchemaValidatesTheEvidenceItWrites() throws Exception { - // Qualification lives in gradle/qualification/messaging-qualification.gradle now, not in the + // Qualification lives in build-qualification/src/main/groovy/ca.messaging-qualification.gradle + // now, not in the // root build file. The property held here is the one that makes the manifest evidence rather // than a file: each producer is finalized by a JSON Schema validation of the exact bytes it // wrote, and the combined producer runs after the JSON-schema one. String qualification = Files.readString( - repositorySrcRoot().resolve("gradle/qualification/messaging-qualification.gradle")); + repositorySrcRoot() + .resolve("build-qualification/src/main/groovy/ca.messaging-qualification.gradle")); assertThat(qualification) .contains( @@ -397,7 +399,7 @@ class MessagingCapabilityRegistryContractTest { Path src = repositorySrcRoot(); return List.of( src.resolve("build.gradle"), - src.resolve("gradle/qualification/messaging-qualification.gradle")); + src.resolve("build-qualification/src/main/groovy/ca.messaging-qualification.gradle")); } private static Path requiredConfig(String relativePath) { diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/MongoRegistryPermissionParityTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/MongoRegistryPermissionParityTest.java index 14dd093d..b5fa61cf 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/MongoRegistryPermissionParityTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/MongoRegistryPermissionParityTest.java @@ -58,20 +58,12 @@ class MongoRegistryPermissionParityTest { } @Test - @DisplayName("the leaf ships in the composition root the registry names") - void theLeafShipsInTheCompositionRootTheRegistryNames() throws IOException { - List memberships = new ArrayList<>(); - module().get("runtime_memberships").forEach(membership -> memberships.add(membership.asText())); - - assertThat(memberships) - .as("property-only activation switches on a module that is already on the classpath") - .contains("app-bootstrap"); + @DisplayName("the composition root actually declares the Mongo leaf") + void theCompositionRootDeclaresTheMongoLeaf() throws IOException { assertThat( Files.readString( repositoryRoot().resolve(Path.of("src", "app-bootstrap", "build.gradle")))) - .as( - "the membership is what the composition root's build says, not a plan: a leaf recorded " - + "as shipping and absent from the jar is a switch an operator cannot turn on") + .as("property-only activation requires the Mongo module to be on the application classpath") .contains("project(':adapter:outbound:persistence-mongo')"); } diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/RuntimeMembershipClasspathAgreementTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/RuntimeMembershipClasspathAgreementTest.java deleted file mode 100644 index 12a801b1..00000000 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/registry/RuntimeMembershipClasspathAgreementTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package dev.caskeleton.bootstrap.registry; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -/** - * What the registry says ships, and what the runtime classpath actually resolves, must be the same - * set. - * - *

    The existing gate compares direct project dependencies. That comparison cannot see a leaf a - * starter pulls in transitively, so the leaf reaches the bootJar while the registry records it as - * belonging to no runtime at all — the membership list stays clean precisely because it is not - * looking at what ships. - * - *

    Green at Wave 0, and that is the point: it is the gate that must stay green while Waves 1 and - * 2 add the Mongo, GraphQL, and messaging edges. - */ -class RuntimeMembershipClasspathAgreementTest { - - private static final String COMPOSITION_ROOT = "app-bootstrap"; - - @Test - @DisplayName("every project on the runtime classpath records app-bootstrap membership") - void classpathAndRegistryAgree() throws IOException { - Path manifest = - repositoryRoot() - .resolve("src/app-bootstrap/build/architecture/runtime-project-closure.txt"); - Assumptions.assumeTrue( - Files.isRegularFile(manifest), - "run :app-bootstrap:runtimeClasspathManifest first; this test reads its output"); - - List onClasspath = - Files.readAllLines(manifest).stream().filter(line -> !line.isBlank()).toList(); - List declared = declaredMembers(); - - assertThat(onClasspath) - .as( - "these projects resolve onto the composition root's runtime classpath but do not " - + "declare app-bootstrap in runtime_memberships, so the registry describes a jar " - + "that is not the one being built") - .allSatisfy(id -> assertThat(declared).contains(id)); - - assertThat(declared) - .as( - "these leaves declare app-bootstrap membership but do not resolve onto its runtime " - + "classpath, so the registry promises something the jar does not carry") - .allSatisfy(id -> assertThat(onClasspath).contains(id)); - } - - private static List declaredMembers() throws IOException { - JsonNode registry = - new ObjectMapper() - .readTree(repositoryRoot().resolve("src/config/architecture/modules.json").toFile()); - List declared = new ArrayList<>(); - for (JsonNode module : registry.get("modules")) { - for (JsonNode membership : module.get("runtime_memberships")) { - if (COMPOSITION_ROOT.equals(membership.asText())) { - declared.add(module.get("id").asText()); - } - } - } - return declared; - } - - private static Path repositoryRoot() { - Path candidate = Path.of("").toAbsolutePath(); - while (candidate != null - && !Files.isRegularFile(candidate.resolve("src/config/architecture/modules.json"))) { - candidate = candidate.getParent(); - } - if (candidate == null) { - throw new IllegalStateException("could not locate the repository root from the test cwd"); - } - return candidate; - } -} diff --git a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/OperationalContractRuntimeTest.java b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/OperationalContractRuntimeTest.java index be1d7ea8..27f2b1b0 100644 --- a/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/OperationalContractRuntimeTest.java +++ b/src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/OperationalContractRuntimeTest.java @@ -28,20 +28,10 @@ import org.springframework.web.bind.annotation.RestController; /** * Regression guard for the skeleton's operational envelope/error contract. * - *

    The audit motivation: this contract used to live only in the disposable {@code - * sample-portfolio} module, which {@code app-bootstrap} does NOT depend on at runtime ({@code - * bootJar} mainClass {@code CaSkeletonApplication}). The contract beans now live in {@code - * adapter-web} (a production dependency of {@code app-bootstrap}). This test pins that {@link - * CaSkeletonApplication}'s component scan ({@code scanBasePackages = "dev.caskeleton"}) actually - * wires them into the running application, so a future change cannot silently drop the contract - * from the real app again. - * - *

    Why a {@code @WebMvcTest} slice rather than a full {@code @SpringBootTest}: a full boot of - * {@code CaSkeletonApplication} requires the ~50 {@code ${...}} env vars wired in {@code - * application.yml} (datasource, OIDC issuer, etc.) from {@code src/.env}, and {@code - * sample-portfolio} on the test classpath drags in Spring Data JPA / Security / OAuth2 - * autoconfiguration plus JPA entities and repositories that need a live datasource and JWT issuer. - * The MVC slice gives a real, {@code CaSkeletonApplication}-anchored web context without that cost. + *

    This test pins that {@link CaSkeletonApplication}'s component scan ({@code scanBasePackages = + * "dev.caskeleton"}) wires the production envelope/error contract into the running application. A + * {@code @WebMvcTest} slice is used instead of a full {@code @SpringBootTest} because the latter + * requires the complete external runtime configuration and infrastructure. * *

    The slice scans {@code CaSkeletonApplication}'s package for {@code @ControllerAdvice} beans, * so it picks up the advices via the same component-scan path the running app uses. The raw-{@code diff --git a/src/application-core/build.gradle b/src/application-core/build.gradle index c8836ab6..1866233d 100644 --- a/src/application-core/build.gradle +++ b/src/application-core/build.gradle @@ -1,8 +1,10 @@ +plugins { + id 'ca.java-library' +} + // Framework-free application use-case contract. Runtime dependencies are project-only; // composition and diagnostic rendering belong to adapters/bootstrap. -apply plugin: 'ca.java-library' - dependencies { implementation project(':shared-contract') diff --git a/src/build-logic/src/main/groovy/ca.architecture-registry.settings.gradle b/src/build-logic/src/main/groovy/ca.architecture-registry.settings.gradle index ef51aceb..8ebe68fb 100644 --- a/src/build-logic/src/main/groovy/ca.architecture-registry.settings.gradle +++ b/src/build-logic/src/main/groovy/ca.architecture-registry.settings.gradle @@ -17,9 +17,7 @@ import dev.caskeleton.buildlogic.ModuleRegistry // // What the count was supposed to protect is protected better elsewhere and without the copy: a // registry entry must name an existing directory, `verifyCleanArchitectureDependencies` fails when a -// declared project has no entry or an entry no project, `verifyRuntimeModuleMembership` compares the -// resolved runtime closure against the declared memberships, and `verifyDocumentedLeafCount` fails -// any document that states a count the registry disagrees with. The registry is the SSOT for the +// declared project has no entry or an entry no project, `verifyRuntimeModuleRegistry` validates resolved project dependencies against the registry. The registry is the SSOT for the // leaf list, so it is the SSOT for its length. File repositoryRoot = settings.settingsDir.parentFile.canonicalFile @@ -38,6 +36,3 @@ registry.modules.each { module -> include module.gradlePath project(module.gradlePath).projectDir = module.sourceDirectory } - -// Handed to the root build so it reads the same parsed registry rather than parsing it again. -gradle.ext.moduleRegistry = registry diff --git a/src/build-logic/src/main/groovy/ca.architecture.gradle b/src/build-logic/src/main/groovy/ca.architecture.gradle index 4d7a82bf..81456e90 100644 --- a/src/build-logic/src/main/groovy/ca.architecture.gradle +++ b/src/build-logic/src/main/groovy/ca.architecture.gradle @@ -1,3 +1,4 @@ +import dev.caskeleton.buildlogic.ModuleRegistry import org.gradle.api.artifacts.component.ModuleComponentIdentifier // The architecture rules. Applied to the root project, because their subject is the repository. @@ -16,9 +17,10 @@ tasks.register('verifyCleanArchitectureDependencies') { File moduleRegistryFile = rootProject.file('config/architecture/modules.json') inputs.file(moduleRegistryFile) - // The registry the settings plugin already parsed. Reading it again here would be a second - // definition of a valid registry. - def registry = gradle.moduleRegistry + // Reuse the same parser/validation implementation as the settings plugin. Parsing the small + // registry twice is intentional: it avoids hidden Settings -> Gradle global state while keeping + // exactly one definition of a valid registry. Source paths are repository-root-relative. + def registry = ModuleRegistry.read(moduleRegistryFile, rootProject.projectDir.parentFile.canonicalFile) // Registry-shape rules that used to run in settings, moved here. // @@ -26,25 +28,23 @@ tasks.register('verifyCleanArchitectureDependencies') { // it in settings meant failing before any project existed — no task could run, `--dry-run` // could not run, and a derived project that mistyped an id had no way to reach a diagnostic // other than editing the registry blind. Here the same mistake is a named task failure. + def productionModules = registry.modules.findAll { it.id != 'sample-portfolio' } List registryViolations = [] - registry.modules.each { module -> + productionModules.each { module -> module.allowedDependencies.each { String dependencyId -> if (dependencyId == module.id) { registryViolations << "'${module.id}' declares itself as an allowed dependency" } else if (registry.byId(dependencyId) == null) { registryViolations << "'${module.id}' allows unknown dependency id '${dependencyId}'" - } else if (module.id != 'sample-portfolio' && dependencyId == 'sample-portfolio') { - registryViolations << - "'${module.id}' allows a production dependency on the removable sample fixture" } } } - Map> allowedProjectDependencies = registry.modules.collectEntries { module -> + Map> allowedProjectDependencies = productionModules.collectEntries { module -> String moduleName = module.gradlePath.replaceFirst('^:', '') Set allowed = module.allowedDependencies .collect { registry.byId(it) } - .findAll { it != null } + .findAll { it != null && it.id != 'sample-portfolio' } .collect { it.gradlePath.replaceFirst('^:', '') } .toSet() [(moduleName): allowed] @@ -57,8 +57,9 @@ tasks.register('verifyCleanArchitectureDependencies') { registryViolations.join('\n ')) } - Set declaredModules = rootProject.subprojects.findAll { it.childProjects.isEmpty() } - .collect { it.path.replaceFirst('^:', '') }.toSet() + Set declaredModules = rootProject.subprojects.findAll { + it.childProjects.isEmpty() && it.path != ':sample-portfolio' + }.collect { it.path.replaceFirst('^:', '') }.toSet() Set governedModules = allowedProjectDependencies.keySet() Set missingFromBuild = governedModules - declaredModules Set missingFromPolicy = declaredModules - governedModules @@ -89,21 +90,12 @@ tasks.register('verifyCleanArchitectureDependencies') { } .toSet() - if (moduleName != 'sample-portfolio' && actual.contains('sample-portfolio')) { - throw new GradleException( - "Module ':${moduleName}' has a forbidden production dependency on " + - "':sample-portfolio'. The sample module may only be consumed through " + - "non-production fixture configurations." - ) - } - Set forbidden = actual - allowed if (!forbidden.isEmpty()) { throw new GradleException( "Module ':${moduleName}' has forbidden project dependencies ${forbidden}. " + - "Allowed dependencies are ${allowed}. " + - "Production modules must not depend on ':sample-portfolio'; " + - "all project edges must be explicitly registered." + "Allowed dependencies are ${allowed}; all production project edges " + + "must be explicitly registered." ) } } @@ -183,7 +175,7 @@ tasks.register('verifyNoIgnoredSourcePackages') { List violations = [] List sourceFiles = [] - rootProject.subprojects.each { sub -> + rootProject.subprojects.findAll { it.path != ':sample-portfolio' }.each { sub -> ['src/main/java', 'src/test/java'].each { String sourceRootPath -> File sourceRoot = sub.file(sourceRootPath) if (!sourceRoot.isDirectory()) { diff --git a/src/gradle/archive-hygiene.gradle b/src/build-logic/src/main/groovy/ca.archive-hygiene.gradle similarity index 100% rename from src/gradle/archive-hygiene.gradle rename to src/build-logic/src/main/groovy/ca.archive-hygiene.gradle diff --git a/src/gradle/config-contract.gradle b/src/build-logic/src/main/groovy/ca.config-contract.gradle similarity index 94% rename from src/gradle/config-contract.gradle rename to src/build-logic/src/main/groovy/ca.config-contract.gradle index c365a05a..88836736 100644 --- a/src/gradle/config-contract.gradle +++ b/src/build-logic/src/main/groovy/ca.config-contract.gradle @@ -224,21 +224,15 @@ tasks.register('verifyEnvKeys') { Set consumed = new TreeSet<>() consumed.addAll(applicationAppReferences) consumed.addAll(envAppKeys) - // A key can be read in ways checks A-D never look at: a module's own application.yml (the - // sample's, for one) and Java that names a secret directly, as SecretSourceValidator does. - // Counting only the composition root's yaml would report those as orphans, which is the - // opposite failure — a check that cries wolf gets an exclusion list and then gets ignored. - // - // Source only. `/main/` also matches build outputs — src/app-bootstrap/build/resources/ - // main/application.yml and src/sample-portfolio/build/resources/main/application.yml both - // exist after any build — so a key deleted from source still counted as "consumed" from a - // stale processResources copy, and the orphan failure below was skipped. A clean CI - // checkout and an incremental local build then disagreed about the same registry. - // verifyRunbookReferences already excludes /build/ for exactly this reason (see its - // traversal above); this traversal now uses the same rule. + // A key can be read in ways checks A-D never look at: another production module's + // application.yml or Java that names a secret directly, as SecretSourceValidator does. + // Count those sources, but keep the removable preview sample outside the production config + // contract entirely. Source only: build outputs are excluded so stale processResources + // copies cannot make a deleted key look consumed. def appKeyPattern = ~/APP_[A-Z][A-Z0-9_]*/ rootProject.projectDir.eachFileRecurse { File candidate -> - if (!candidate.isFile() || candidate.path.contains('/build/')) { + if (!candidate.isFile() || candidate.path.contains('/build/') || + candidate.path.contains('/sample-portfolio/')) { return } boolean interesting = diff --git a/src/build-logic/src/main/groovy/ca.evidence.gradle b/src/build-logic/src/main/groovy/ca.evidence.gradle index 5f45ab4f..34ff3bd3 100644 --- a/src/build-logic/src/main/groovy/ca.evidence.gradle +++ b/src/build-logic/src/main/groovy/ca.evidence.gradle @@ -23,7 +23,8 @@ Closure> readJUnitEvidence = { String evidenceName, File res skipped : results.skipped, failures : results.failures, errors : results.errors, - executedClasses: results.executedClasses + executedClasses: results.executedClasses, + executedSelectors: results.executedSelectors ] as Map } diff --git a/src/gradle/graphql-platform-conventions.gradle b/src/build-logic/src/main/groovy/ca.graphql-platform.gradle similarity index 100% rename from src/gradle/graphql-platform-conventions.gradle rename to src/build-logic/src/main/groovy/ca.graphql-platform.gradle diff --git a/src/build-logic/src/main/groovy/ca.grpc-platform-module.gradle b/src/build-logic/src/main/groovy/ca.grpc-platform-module.gradle index 59d58149..7045410d 100644 --- a/src/build-logic/src/main/groovy/ca.grpc-platform-module.gradle +++ b/src/build-logic/src/main/groovy/ca.grpc-platform-module.gradle @@ -1,3 +1,5 @@ +import org.gradle.api.artifacts.VersionCatalogsExtension + // A vendored platform leaf that compiles against io.grpc: `ca.platform-module` plus the grpc BOM. // // io.grpc is not managed by the Spring Boot BOM, so four `grpc:*` leaves each imported grpc-bom at @@ -22,27 +24,13 @@ plugins { id 'ca.platform-module' } -// Read from the root's `ext.grpcVersion` SSOT, which is where the four leaves read it from. Resolved -// at apply time, which is the same moment their inline blocks resolved it: the root sets the property -// while evaluating its own build file, long before any leaf is evaluated. -Object declaredGrpcVersion = project.rootProject.findProperty('grpcVersion') -if (declaredGrpcVersion == null || declaredGrpcVersion.toString().isBlank()) { - throw new GradleException( - "${project.path} applies ca.grpc-platform-module, which imports io.grpc:grpc-bom, but " + - 'the root project declares no `ext.grpcVersion`. Importing an unversioned BOM ' + - 'would leave every io.grpc coordinate in this leaf unmanaged.') +// The gRPC version is owned by the shared version catalog. Optional builds import the same +// catalog file, so the platform no longer reaches into a parent/root ext property. +def versionCatalog = project.extensions.getByType(VersionCatalogsExtension).named('libs') +def grpcVersionConstraint = versionCatalog.findVersion('grpc').orElseThrow { + new GradleException("Version catalog 'libs' must define version 'grpc' for ca.grpc-platform-module") } -String grpcVersion = declaredGrpcVersion.toString() - -// No runtime guard for dependency-management any more, because there is nothing left to guard. -// -// This used to throw when `io.spring.dependency-management` was absent, since without it there is no -// `dependencyManagement` block to import the BOM into, and a BOM that was never imported does not -// announce itself: it surfaces later as an io.grpc coordinate with no version, in whichever leaf -// asks first. That check answered a question a leaf could get wrong while the root applied the -// plugin from `configure(subprojects)`. `ca.platform-module` -> `ca.java-library` -> -// `ca.java-conventions` applies it now, so the plugin graph makes the precondition true instead of -// checking it afterwards. +String grpcVersion = grpcVersionConstraint.requiredVersion dependencyManagement { imports { diff --git a/src/build-logic/src/main/groovy/ca.java-conventions.gradle b/src/build-logic/src/main/groovy/ca.java-conventions.gradle index 33dc9c70..cd98e5a1 100644 --- a/src/build-logic/src/main/groovy/ca.java-conventions.gradle +++ b/src/build-logic/src/main/groovy/ca.java-conventions.gradle @@ -24,6 +24,7 @@ plugins { id 'ca.api-surface' id 'ca.dependency-policy' id 'ca.strict-qualification' + id 'ca.test-jvm-agents' } // The main build's catalog, read through the Gradle API rather than the `libs` accessor, which is @@ -32,6 +33,9 @@ plugins { def versionCatalog = project.extensions.getByType(VersionCatalogsExtension).named('libs') String springBootVersion = versionCatalog.findVersion('springBoot').get().requiredVersion +group = 'dev.caskeleton' +version = rootProject.ext.has('traceableVersion') ? rootProject.ext.traceableVersion : '0.0.1-SNAPSHOT' + java { toolchain { languageVersion = JavaLanguageVersion.of(21) diff --git a/src/gradle/notification-api-surface.gradle b/src/build-logic/src/main/groovy/ca.notification-api-surface.gradle similarity index 100% rename from src/gradle/notification-api-surface.gradle rename to src/build-logic/src/main/groovy/ca.notification-api-surface.gradle diff --git a/src/gradle/notification-configuration.gradle b/src/build-logic/src/main/groovy/ca.notification-configuration.gradle similarity index 100% rename from src/gradle/notification-configuration.gradle rename to src/build-logic/src/main/groovy/ca.notification-configuration.gradle diff --git a/src/gradle/notification-evidence.gradle b/src/build-logic/src/main/groovy/ca.notification-evidence.gradle similarity index 100% rename from src/gradle/notification-evidence.gradle rename to src/build-logic/src/main/groovy/ca.notification-evidence.gradle diff --git a/src/gradle/public-path-snapshot.gradle b/src/build-logic/src/main/groovy/ca.public-path-snapshot.gradle similarity index 100% rename from src/gradle/public-path-snapshot.gradle rename to src/build-logic/src/main/groovy/ca.public-path-snapshot.gradle diff --git a/src/build-logic/src/main/groovy/ca.quality-conventions.gradle b/src/build-logic/src/main/groovy/ca.quality-conventions.gradle index a1108999..c2a096d5 100644 --- a/src/build-logic/src/main/groovy/ca.quality-conventions.gradle +++ b/src/build-logic/src/main/groovy/ca.quality-conventions.gradle @@ -1,6 +1,7 @@ import com.github.spotbugs.snom.Confidence import groovy.xml.XmlSlurper import com.github.spotbugs.snom.SpotBugsTask +import org.gradle.api.plugins.quality.Checkstyle import org.gradle.api.artifacts.VersionCatalogsExtension // feature-static-analysis-quality-contract — the static analysis baseline. @@ -8,10 +9,10 @@ import org.gradle.api.artifacts.VersionCatalogsExtension // Tiered, which is the change. Every tool used to hang off every leaf's `check`, so // `./gradlew :domain-core:check` ran a bytecode bug finder and a security scanner before it would // tell a developer whether their unit test passed. The two fast, deterministic tools stay on -// `check`; the two slow, worker-forking ones move to `qualityCheck`, which `ci` runs. +// `check`; the two slow, worker-forking production analysis moves to `qualityCheck`, which `ci` runs. // // check Spotless (formatting), Checkstyle (style), Error Prone (compile-time) -// qualityCheck SpotBugs + FindSecBugs (bytecode analysis, forks an analysis worker per source set) +// qualityCheck SpotBugs + FindSecBugs (production `main` bytecode only) // // Nothing is disabled and no finding is downgraded: `./gradlew qualityCheck` runs the same tasks // with the same configuration, and CI runs it on every pull request. @@ -27,6 +28,14 @@ plugins { def versionCatalog = project.extensions.getByType(VersionCatalogsExtension).named('libs') Closure versionOf = { String alias -> versionCatalog.findVersion(alias).get().requiredVersion } +// Main and optional-platform builds share one repository-level quality configuration. The optional +// build lives one directory below the main Gradle root, so resolve the shared config without +// assuming that every build root is the repository's src directory. +File repositoryConfigDirectory = rootProject.file('config') +if (!repositoryConfigDirectory.isDirectory()) { + repositoryConfigDirectory = new File(rootProject.projectDir.parentFile, 'config') +} + // D1 — google-java-format owns formatting + import order; spotlessApply auto-fixes, spotlessCheck // (wired into check) verifies. CI must NEVER run spotlessApply. spotless { @@ -42,12 +51,16 @@ spotless { // OuterTypeFilename, which is why no hand-written Java scanner enforces it any more. checkstyle { toolVersion = versionOf('checkstyle') - configFile = rootProject.file('config/checkstyle/checkstyle.xml') - configDirectory = rootProject.file('config/checkstyle') + configFile = new File(repositoryConfigDirectory, 'checkstyle/checkstyle.xml') + configDirectory = new File(repositoryConfigDirectory, 'checkstyle') ignoreFailures = false // No warning-tier checks in the default build. Javadoc coverage is a documentation backlog, not // a signal to print on every migration/build run. maxWarnings = Integer.MAX_VALUE + + // Keep the ordinary check lane limited to main and test. Auxiliary source sets belong to + // explicit verification lanes and are linted by auxiliaryStyleCheck instead. + sourceSets = [sourceSets.main, sourceSets.test] } // D3/D4 — bytecode bug finder; FindSecBugs plugin loaded via spotbugsPlugins below. @@ -57,7 +70,7 @@ checkstyle { spotbugs { toolVersion = versionOf('spotbugs') reportLevel = Confidence.valueOf('HIGH') - excludeFilter = rootProject.file('config/spotbugs/exclude.xml') + excludeFilter = new File(repositoryConfigDirectory, 'spotbugs/exclude.xml') } // An incomplete SpotBugs run is a failure, not a clean report. @@ -106,18 +119,20 @@ Closure> spotBugsAnalysisFailures = { File reportFile -> failures } -sourceSets.configureEach { sourceSet -> - tasks.named("spotbugs${sourceSet.name.capitalize()}", SpotBugsTask) { - auxClassPaths.from(sourceSet.runtimeClasspath - sourceSet.output) - def xmlAnalysisReport = reports.maybeCreate('xml') - xmlAnalysisReport.required.set(true) - doLast { - List analysisFailures = - spotBugsAnalysisFailures(xmlAnalysisReport.outputLocation.get().asFile) - if (!analysisFailures.isEmpty()) { - throw new GradleException( - "${path}: SpotBugs analysis incomplete:\n " + analysisFailures.join('\n ')) - } +// Production bytecode is the blocking static-analysis surface. Auxiliary test/benchmark/ +// qualification source sets are already compiled and executed by their lanes; running SpotBugs for +// every one of them multiplied analysis workers as source sets grew without protecting shipped code. +tasks.named('spotbugsMain', SpotBugsTask) { + def mainSourceSet = sourceSets.main + auxClassPaths.from(mainSourceSet.runtimeClasspath - mainSourceSet.output) + def xmlAnalysisReport = reports.maybeCreate('xml') + xmlAnalysisReport.required.set(true) + doLast { + List analysisFailures = + spotBugsAnalysisFailures(xmlAnalysisReport.outputLocation.get().asFile) + if (!analysisFailures.isEmpty()) { + throw new GradleException( + "${path}: SpotBugs analysis incomplete:\n " + analysisFailures.join('\n ')) } } } @@ -155,8 +170,16 @@ tasks.named('check') { setDependsOn(dependsOn.findAll { !isSpotBugsDependency(it) }) } +tasks.register('auxiliaryStyleCheck') { + group = 'verification' + description = 'Runs Checkstyle for non-default source sets outside the fast local check lane.' + dependsOn tasks.withType(Checkstyle).matching { + it.name != 'checkstyleMain' && it.name != 'checkstyleTest' + } +} + tasks.register('qualityCheck') { group = 'verification' description = 'Runs this leaf\'s SpotBugs and FindSecBugs bytecode analysis.' - dependsOn tasks.withType(SpotBugsTask) + dependsOn tasks.named('spotbugsMain') } diff --git a/src/build-logic/src/main/groovy/ca.runtime-membership.gradle b/src/build-logic/src/main/groovy/ca.runtime-membership.gradle index 0bde440f..62823045 100644 --- a/src/build-logic/src/main/groovy/ca.runtime-membership.gradle +++ b/src/build-logic/src/main/groovy/ca.runtime-membership.gradle @@ -1,104 +1,64 @@ -// Where the registry's repository-root-relative source paths are resolved from. -// -// Defaults to the parent of the Gradle root, which is this repository's layout: the build lives in -// src/ and source_path values read `src/...`. A consumer with a different layout — the functional -// fixture, whose projects sit beside its registry — says so rather than having the plugin guess. +import org.gradle.api.artifacts.component.ProjectComponentIdentifier + +// Runtime topology is derived from Gradle, not copied into modules.json. The registry only names +// composition roots and the application projects that are allowed to exist in this build. ext.moduleRegistryRepositoryRoot = rootProject.projectDir.parentFile -tasks.register('verifyRuntimeModuleMembership') { +File registryFile = rootProject.file('config/architecture/modules.json') +def registry +try { + registry = dev.caskeleton.buildlogic.ModuleRegistry.read( + registryFile, project.moduleRegistryRepositoryRoot as File) +} catch (IllegalStateException invalid) { + throw new GradleException(invalid.message, invalid) +} +def productionModules = registry.modules.findAll { it.id != 'sample-portfolio' } +Map moduleIdByGradlePath = + productionModules.collectEntries { [(it.gradlePath): it.id] } +List compositionIds = registry.compositionRoots.findAll { it != 'sample-portfolio' } + +tasks.register('verifyRuntimeModuleRegistry') { group = 'verification' - description = 'Verifies registry runtime membership against both shipped composition roots.' - - File registryFile = rootProject.file('config/architecture/modules.json') + description = 'Verifies resolved composition runtime project dependencies are registered application modules.' inputs.file(registryFile) - // Parsed through the shared reader, not re-implemented here. - // - // This task carried ~85 lines re-checking what settings already checked — duplicate ids, - // duplicate Gradle paths, membership validity, a composition including itself. Three - // implementations of one rule are three definitions of valid; what this task uniquely owns is - // the comparison below, between declared membership and the runtime closure Gradle resolves. - // - // It reads the file rather than taking the settings plugin's parsed result, because this plugin - // is also applied by a standalone fixture whose settings never ran that plugin. Same parser, - // same rules, one implementation. - def registry - try { - registry = dev.caskeleton.buildlogic.ModuleRegistry.read( - registryFile, project.moduleRegistryRepositoryRoot as File) - } catch (IllegalStateException invalid) { - throw new GradleException(invalid.message, invalid) - } - // From the registry file, not from a constant in the reader: the registry owns which runtime - // compositions exist, so a derived project adds or drops one by editing JSON. - List compositionIds = registry.runtimeCompositions - Map moduleIdByGradlePath = - registry.modules.collectEntries { [(it.gradlePath): it.id] } - // Resolved here rather than through `rootProject` inside the action: that is `Task.project` at - // execution time, which Gradle 9 deprecates and this repository's `--warning-mode=fail` gates - // reject outright. - def owningRootProject = project.rootProject - doLast { compositionIds.each { String compositionId -> def composition = registry.byId(compositionId) - String compositionGradlePath = composition.gradlePath - Project compositionProject = owningRootProject.findProject(compositionGradlePath) + Project compositionProject = rootProject.findProject(composition.gradlePath) if (compositionProject == null) { throw new GradleException( - "Runtime composition '${compositionId}' references missing Gradle project " + - "'${compositionGradlePath}'.") + "Runtime composition '${compositionId}' references missing Gradle project '${composition.gradlePath}'.") } - - Set expected = registry.membersOf(compositionId) - .findAll { it.id != compositionId } - .collect { it.id } - .toSet() - - // The resolved runtime closure, not the declared dependency list. - // - // A declared-dependency comparison cannot see a leaf that arrives transitively — a - // starter pulling in six internal modules puts all six in the bootJar while the registry - // records them as belonging to no runtime at all. The membership list then stays clean - // precisely because it is not looking at what ships. Resolving runtimeClasspath asks the - // question the jar answers. def runtimeClasspath = compositionProject.configurations.findByName('runtimeClasspath') if (runtimeClasspath == null) { throw new GradleException( "Runtime composition '${compositionId}' has no runtimeClasspath configuration.") } - Set actual = runtimeClasspath.incoming.resolutionResult.allComponents - .findAll { it.id instanceof org.gradle.api.artifacts.component.ProjectComponentIdentifier } - .collect { (it.id as org.gradle.api.artifacts.component.ProjectComponentIdentifier).projectPath } + + Set actualProjectPaths = runtimeClasspath.incoming.resolutionResult.allComponents + .findAll { it.id instanceof ProjectComponentIdentifier } + .collect { (it.id as ProjectComponentIdentifier).projectPath } .findAll { String projectPath -> projectPath != compositionProject.path } - .collect { String projectPath -> - String dependencyId = moduleIdByGradlePath[projectPath] - if (dependencyId == null) { - throw new GradleException( - "Runtime composition '${compositionId}' resolves unregistered " + - "Gradle project '${projectPath}' onto its runtime classpath.") - } - dependencyId - } .toSet() - Set unregistered = actual - expected - Set missing = expected - actual - if (!unregistered.isEmpty() || !missing.isEmpty()) { - List violations = [] - if (!unregistered.isEmpty()) { - violations << "unregistered runtime dependencies ${unregistered.toSorted()}" - } - if (!missing.isEmpty()) { - violations << "missing registered runtime dependencies ${missing.toSorted()}" - } + Set unregistered = actualProjectPaths.findAll { !moduleIdByGradlePath.containsKey(it) }.toSet() + if (!unregistered.isEmpty()) { throw new GradleException( - "Runtime composition '${compositionId}' membership drift: " + - violations.join('; ') + '.') + "Runtime composition '${compositionId}' resolves unregistered application projects " + + "${unregistered.toSorted()}.") } + } logger.lifecycle( - "verifyRuntimeModuleMembership: ${compositionIds.size()} runtime composition(s) " + - 'match the registry') + "verifyRuntimeModuleRegistry: ${compositionIds.size()} composition root(s) resolve only registered application projects") } } + +// Compatibility alias for scripts/docs that still use the old task name. It no longer compares an +// exact membership snapshot; it delegates to the invariant-based runtime registry check above. +tasks.register('verifyRuntimeModuleMembership') { + group = 'verification' + description = 'Compatibility alias for verifyRuntimeModuleRegistry.' + dependsOn tasks.named('verifyRuntimeModuleRegistry') +} diff --git a/src/build-logic/src/main/groovy/ca.test-jvm-agents.gradle b/src/build-logic/src/main/groovy/ca.test-jvm-agents.gradle new file mode 100644 index 00000000..0fa24bf8 --- /dev/null +++ b/src/build-logic/src/main/groovy/ca.test-jvm-agents.gradle @@ -0,0 +1,47 @@ +import org.gradle.api.GradleException +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.testing.Test +import org.gradle.process.CommandLineArgumentProvider + +abstract class MockitoAgentArgumentProvider implements CommandLineArgumentProvider { + @Classpath + abstract ConfigurableFileCollection getMockitoCoreClasspath() + + @Input + abstract Property getOwner() + + @Override + Iterable asArguments() { + List candidates = mockitoCoreClasspath.files.findAll { File file -> + file.isFile() && file.name ==~ 'mockito-core-[^/]+\\.jar' + }.sort { File left, File right -> left.absolutePath <=> right.absolutePath } + if (candidates.size() != 1) { + throw new GradleException( + "${owner.get()}: expected exactly one mockito-core JAR for the test JVM, " + + "found ${candidates.size()}: " + candidates.collect { it.absolutePath }) + } + ["-javaagent:${candidates[0].absolutePath}", '-Xshare:off'] + } +} + +// Leaf-owned configuration: no rootProject.subprojects traversal and no cross-project mutation. +pluginManager.withPlugin('java') { + pluginManager.withPlugin('io.spring.dependency-management') { + def mockitoAgentDependencies = configurations.dependencyScope('mockitoAgentDependencies') + def mockitoAgent = configurations.resolvable('mockitoAgent') { + description = 'Mockito core JAR used only as a Test JVM startup agent.' + extendsFrom(mockitoAgentDependencies.get()) + transitive = false + } + dependencies.add(mockitoAgentDependencies.get().name, 'org.mockito:mockito-core') + tasks.withType(Test).configureEach { + def provider = objects.newInstance(MockitoAgentArgumentProvider) + provider.mockitoCoreClasspath.from(mockitoAgent) + provider.owner.set("${project.path}:${name}") + jvmArgumentProviders.add(provider) + } + } +} diff --git a/src/build-logic/src/main/groovy/dev/caskeleton/buildlogic/ModuleRegistry.groovy b/src/build-logic/src/main/groovy/dev/caskeleton/buildlogic/ModuleRegistry.groovy index a3ec00cc..8c3d2951 100644 --- a/src/build-logic/src/main/groovy/dev/caskeleton/buildlogic/ModuleRegistry.groovy +++ b/src/build-logic/src/main/groovy/dev/caskeleton/buildlogic/ModuleRegistry.groovy @@ -3,89 +3,46 @@ package dev.caskeleton.buildlogic import groovy.json.JsonSlurper /** - * The module registry, parsed and validated once. + * Parses the application build's module registry. * - *

    Seven build files parsed {@code config/architecture/modules.json} independently — settings, the - * root build in nine places, the runtime-membership script and three leaves — each with its own - * assumptions about the shape. Two of those readers were the authority on the same rule: settings - * validated the field set and the edges before including projects, and the root re-implemented the - * edge check for {@code verifyCleanArchitectureDependencies}. A registry with two validators has two - * definitions of valid, and the disagreement is only visible when one of them is wrong. - * - *

    Deliberately a plain class rather than a script plugin. Settings and projects both need it, and - * they load plugins through different mechanisms; a class in the included build's jar is reachable - * from either. - * - *

    Validation is here, not at the call sites. A caller that only wants the leaf list still gets the - * duplicate-id check, because a registry that is malformed for one reader is malformed for all. + * The registry owns project identity/path plus architecture-edge policy. It intentionally does not + * mirror Gradle's runtime graph: runtime membership is derived from the resolved runtimeClasspath, + * so adding a dependency never requires a second edit to this JSON just to restate what Gradle + * already knows. */ final class ModuleRegistry { - /** - * The fields a module entry must carry. A missing one is a failure; an extra one is not. - * - *

    This used to be an exact-set comparison in both directions, and the second direction was a - * current-state check rather than an invariant: adding a {@code description} or a {@code type} - * to an entry — a normal thing to want from a registry — failed the build in settings, - * before any project existed. Nothing reads a field this class does not know about, so an extra - * one cannot change what the build does; refusing it only stopped the registry being extended. - */ private static final Set REQUIRED_MODULE_FIELDS = - ['id', 'gradle_path', 'source_path', 'allowed_dependencies', 'runtime_memberships'] as Set + ['id', 'gradle_path', 'source_path', 'allowed_dependencies'] as Set + private static final Set REQUIRED_ROOT_FIELDS = ['composition_roots', 'modules'] as Set - /** Same rule at the root: these two must be present, and others are allowed. */ - private static final Set REQUIRED_ROOT_FIELDS = ['runtime_compositions', 'modules'] as Set - - /** Every registered module, in registry order. */ final List modules - - /** - * The runtime compositions this registry declares, in registry order. - * - *

    Read from {@code runtime_compositions}, not from a constant. The list used to exist twice — - * once here as {@code RUNTIME_COMPOSITIONS} and once in the JSON — and {@code read} only checked - * that the two copies agreed, so the JSON field looked like configuration while deciding nothing. - * The cost of that was not cosmetic: a derived project that drops or renames a composition root - * fails in settings, before any project exists, with no way to recover short of editing - * this file. The JSON is the registry, so the JSON is where the list lives. - */ - final List runtimeCompositions - - /** The file this was read from, for failure messages that name it. */ + final List compositionRoots final File source - private ModuleRegistry(List modules, List runtimeCompositions, File source) { + private ModuleRegistry(List modules, List compositionRoots, File source) { this.modules = Collections.unmodifiableList(modules) - this.runtimeCompositions = Collections.unmodifiableList(runtimeCompositions) + this.compositionRoots = Collections.unmodifiableList(compositionRoots) this.source = source } - /** One registered leaf. */ static final class Module { final String id final String gradlePath final String sourcePath final File sourceDirectory final List allowedDependencies - final List runtimeMemberships private Module(String id, String gradlePath, String sourcePath, File sourceDirectory, - List allowedDependencies, List runtimeMemberships) { + List allowedDependencies) { this.id = id this.gradlePath = gradlePath this.sourcePath = sourcePath this.sourceDirectory = sourceDirectory this.allowedDependencies = Collections.unmodifiableList(allowedDependencies) - this.runtimeMemberships = Collections.unmodifiableList(runtimeMemberships) } } - /** - * Reads and validates the registry. - * - * @param registryFile the registry JSON - * @param repositoryRoot the root every source path is resolved against and must stay inside - */ static ModuleRegistry read(File registryFile, File repositoryRoot) { if (!registryFile.isFile()) { throw new IllegalStateException("Missing module registry: ${registryFile}") @@ -103,24 +60,18 @@ final class ModuleRegistry { if (!(parsed.modules instanceof List) || parsed.modules.isEmpty()) { throw new IllegalStateException("Module registry has no modules: ${registryFile}") } - if (!(parsed.runtime_compositions instanceof List) || parsed.runtime_compositions.isEmpty()) { + + List compositionRoots = requireStringList( + parsed.composition_roots, '', 'composition_roots') + if (compositionRoots.isEmpty()) { throw new IllegalStateException( - "Module registry needs a nonempty 'runtime_compositions' list: ${registryFile}") + "Module registry needs a nonempty 'composition_roots' list: ${registryFile}") } - List runtimeCompositions = parsed.runtime_compositions.withIndex().collect { - value, index -> - if (!(value instanceof String) || (value as String).isBlank()) { - throw new IllegalStateException( - "Module registry has a non-string or blank runtime_compositions entry at " + - "index ${index}: ${registryFile}") - } - value as String - } - if (runtimeCompositions.toSet().size() != runtimeCompositions.size()) { + if (compositionRoots.toSet().size() != compositionRoots.size()) { throw new IllegalStateException( - "Module registry contains duplicate runtime_compositions: ${registryFile}") + "Module registry contains duplicate composition_roots: ${registryFile}") } - Set declaredCompositions = runtimeCompositions.toSet() + File canonicalRoot = repositoryRoot.canonicalFile String rootPrefix = canonicalRoot.path + File.separator Set ids = new LinkedHashSet<>() @@ -135,9 +86,6 @@ final class ModuleRegistry { Set missingFields = REQUIRED_MODULE_FIELDS - module.keySet().collect { it as String }.toSet() if (!missingFields.isEmpty()) { - // Named by id when the entry still carries one. "entry 2 has the wrong fields" sends - // a reader counting array elements; naming the module and the fields that differ - // says which entry and what about it. Object rawId = module['id'] String named = (rawId instanceof String && !(rawId as String).isBlank()) ? "'${rawId}'" @@ -151,20 +99,13 @@ final class ModuleRegistry { "Module registry entry ${index} needs a nonblank string '${field}'.") } } + String id = module.id as String List allowedDependencies = requireStringList(module.allowed_dependencies, id, 'allowed_dependencies') - List runtimeMemberships = - requireStringList(module.runtime_memberships, id, 'runtime_memberships') - - if (runtimeMemberships.toSet().size() != runtimeMemberships.size()) { + if (allowedDependencies.toSet().size() != allowedDependencies.size()) { throw new IllegalStateException( - "Module registry entry '${id}' contains duplicate runtime memberships.") - } - Set unknown = runtimeMemberships.toSet() - declaredCompositions - if (!unknown.isEmpty()) { - throw new IllegalStateException( - "Module registry entry '${id}' references unknown runtime memberships ${unknown.toSorted()}.") + "Module registry entry '${id}' contains duplicate allowed dependencies.") } if (!ids.add(id)) { throw new IllegalStateException("Module registry contains duplicate module id '${id}'.") @@ -194,57 +135,34 @@ final class ModuleRegistry { throw new IllegalStateException( "Module registry entry '${id}' source path is not an existing directory: ${sourceDirectory}") } - // Canonicalised first, so two entries that differ only by a symlink or a `..` segment are - // caught rather than silently mapped onto one project directory. if (!sourceDirectories.add(sourceDirectory.path)) { throw new IllegalStateException( "Module registry entry '${id}' resolves to duplicate or aliased canonical source " + "directory: ${sourceDirectory}") } - return new Module(id, gradlePath, sourcePath, sourceDirectory, - allowedDependencies, runtimeMemberships) + new Module(id, gradlePath, sourcePath, sourceDirectory, allowedDependencies) } - runtimeCompositions.each { compositionId -> - Module composition = modules.find { it.id == compositionId } - if (composition == null || !composition.runtimeMemberships.contains(compositionId)) { - throw new IllegalStateException( - "Runtime composition '${compositionId}' must be registered and include itself in " + - 'runtime_memberships.') - } + Set moduleIds = modules.collect { it.id }.toSet() + Set unknownRoots = compositionRoots.toSet() - moduleIds + if (!unknownRoots.isEmpty()) { + throw new IllegalStateException( + "Module registry composition_roots reference unknown module ids ${unknownRoots.toSorted()}.") } - // Edge rules — self-dependency, an unknown id, a production edge onto the removable sample - // fixture — are NOT checked here any more. They are real defects, and - // `verifyCleanArchitectureDependencies` fails on every one of them by name. - // - // What moved is where they fail. Settings runs before any project exists, so a mistyped - // dependency id took the whole build down: no task could be listed, no `--dry-run` could - // run, and the only diagnostic was this exception. That is the right severity for "this - // registry cannot be turned into a project list" — a duplicate id, a path outside the - // repository, a directory that is not there — and the wrong severity for "this edge is not - // allowed", which is a question about the architecture and belongs to the task that answers - // the rest of them. - - return new ModuleRegistry(modules, runtimeCompositions, registryFile) + new ModuleRegistry(modules, compositionRoots, registryFile) } - /** Modules whose runtime_memberships name the given composition. */ - List membersOf(String composition) { - return modules.findAll { it.runtimeMemberships.contains(composition) } - } - - /** A module by id, or null. */ Module byId(String id) { - return modules.find { it.id == id } + modules.find { it.id == id } } private static List requireStringList(Object raw, String id, String field) { if (!(raw instanceof List)) { throw new IllegalStateException("Module registry entry '${id}' needs a '${field}' list.") } - return raw.withIndex().collect { value, index -> + raw.withIndex().collect { value, index -> if (!(value instanceof String) || (value as String).isBlank()) { throw new IllegalStateException( "Module registry entry '${id}' has a non-string or blank ${field} entry at index ${index}.") diff --git a/src/build-logic/src/test/groovy/ModuleRegistryTest.groovy b/src/build-logic/src/test/groovy/ModuleRegistryTest.groovy index f6c59a6e..777bb35a 100644 --- a/src/build-logic/src/test/groovy/ModuleRegistryTest.groovy +++ b/src/build-logic/src/test/groovy/ModuleRegistryTest.groovy @@ -9,13 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals import static org.junit.jupiter.api.Assertions.assertThrows import static org.junit.jupiter.api.Assertions.assertTrue -/** - * The registry rules still fail closed after being moved out of settings.gradle. - * - *

    Moving validation is the risk the design names: build logic that relocates can lose a rule and - * still look green, because the build that no longer checks a thing does not report that it stopped. - * Each case below is one rule settings.gradle enforced before the move. - */ +/** Contract tests for the small amount of state modules.json still owns. */ class ModuleRegistryTest { Path root @@ -30,192 +24,129 @@ class ModuleRegistryTest { private File write(String json) { Path file = root.resolve('modules.json') Files.writeString(file, json) - return file.toFile() + file.toFile() } - private static String entry(String id, String path, String source, String deps = '[]', - String memberships = '[]') { - return """{"id":"${id}","gradle_path":"${path}","source_path":"${source}", - "allowed_dependencies":${deps},"runtime_memberships":${memberships}}""" + private static String entry(String id, String path, String source, String deps = '[]') { + """{"id":"${id}","gradle_path":"${path}","source_path":"${source}","allowed_dependencies":${deps}}""" } - private String registry(String... entries) { - return """{"runtime_compositions":["app-bootstrap","sample-portfolio"], - "modules":[${entries.join(',')}]}""" + private String registry(String roots = '["app-bootstrap","sample-portfolio"]', String... entries) { + """{"composition_roots":${roots},"modules":[${entries.join(',')}]}""" } private ModuleRegistry read(String json) { - return ModuleRegistry.read(write(json), root.toFile()) + ModuleRegistry.read(write(json), root.toFile()) } private String valid() { - return registry( - entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', '["app-bootstrap"]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]')) + registry('["app-bootstrap","sample-portfolio"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'), + entry('sample-portfolio', ':sample-portfolio', 'src/beta')) } @Test - @DisplayName("a well-formed registry parses into its modules") + @DisplayName('a well-formed registry parses project identity and composition roots') void wellFormedRegistryParses() { def parsed = read(valid()) - assertEquals(2, parsed.modules.size()) - assertEquals(['app-bootstrap'], parsed.membersOf('app-bootstrap').collect { it.id }) + assertEquals(['app-bootstrap', 'sample-portfolio'], parsed.compositionRoots) assertTrue(parsed.byId('app-bootstrap').sourceDirectory.isDirectory()) } @Test - @DisplayName("a duplicate module id is refused") void duplicateIdIsRefused() { - String json = registry( - entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', '["app-bootstrap"]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]'), - entry('app-bootstrap', ':other', 'src/alpha')) - def failure = assertThrows(IllegalStateException) { read(json) } + def failure = assertThrows(IllegalStateException) { + read(registry('["app-bootstrap","sample-portfolio"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'), + entry('sample-portfolio', ':sample-portfolio', 'src/beta'), + entry('app-bootstrap', ':other', 'src/alpha'))) + } assertTrue(failure.message.contains('duplicate module id'), failure.message) } @Test - @DisplayName("two entries resolving to one canonical directory are refused") void aliasedSourceDirectoryIsRefused() { - // The `..` segment makes two different source_path strings name one directory. Without - // canonicalisation both would be included and the second would silently take the first's - // project directory. - String json = registry( - entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', '["app-bootstrap"]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]'), - entry('aliased', ':aliased', 'src/beta/../alpha')) - def failure = assertThrows(IllegalStateException) { read(json) } + def failure = assertThrows(IllegalStateException) { + read(registry('["app-bootstrap","sample-portfolio"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'), + entry('sample-portfolio', ':sample-portfolio', 'src/beta'), + entry('aliased', ':aliased', 'src/beta/../alpha'))) + } assertTrue(failure.message.contains('duplicate or aliased'), failure.message) } @Test - @DisplayName("a source path escaping the repository root is refused") void escapingSourcePathIsRefused() { - String json = registry( - entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', '["app-bootstrap"]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]'), - entry('escaping', ':escaping', '../outside')) - def failure = assertThrows(IllegalStateException) { read(json) } - assertTrue(failure.message.contains('escapes the repository root') - || failure.message.contains('not an existing directory'), failure.message) + def failure = assertThrows(IllegalStateException) { + read(registry('["app-bootstrap","sample-portfolio"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'), + entry('sample-portfolio', ':sample-portfolio', 'src/beta'), + entry('escaping', ':escaping', '../outside'))) + } + assertTrue(failure.message.contains('escapes the repository root') || + failure.message.contains('not an existing directory'), failure.message) } @Test - @DisplayName("edge rules are not settings-time failures; the registry still parses") - void edgeRulesDoNotFailTheProjectList() { - // A self-edge, an unknown id and a production edge onto the sample fixture are all real - // defects, and verifyCleanArchitectureDependencies fails on each by name. None of them - // stops this registry describing a project list, so none of them fails here: settings runs - // before any project exists, and a failure here leaves no task able to report anything. - String json = registry( + @DisplayName('architecture edges are data here; architectureCheck decides whether they are legal') + void edgeRulesDoNotFailProjectDiscovery() { + def parsed = read(registry('["app-bootstrap","sample-portfolio"]', entry('app-bootstrap', ':app-bootstrap', 'src/alpha', - '["sample-portfolio","nope","app-bootstrap"]', '["app-bootstrap"]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]')) - def registry = read(json) - assertEquals(2, registry.modules.size()) - assertEquals(['sample-portfolio', 'nope', 'app-bootstrap'], - registry.byId('app-bootstrap').allowedDependencies) + '["sample-portfolio","unknown","app-bootstrap"]'), + entry('sample-portfolio', ':sample-portfolio', 'src/beta'))) + assertEquals(['sample-portfolio', 'unknown', 'app-bootstrap'], + parsed.byId('app-bootstrap').allowedDependencies) } @Test - @DisplayName("an unknown runtime membership is refused") - void unknownMembershipIsRefused() { - String json = registry( - entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', - '["app-bootstrap","not-a-composition"]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]')) - def failure = assertThrows(IllegalStateException) { read(json) } - assertTrue(failure.message.contains('unknown runtime memberships'), failure.message) - } - - @Test - @DisplayName("an extra field on a module entry is carried, not refused") void extraFieldIsAccepted() { - // The registry is meant to be extended — a `description`, a `type`, an owner. Nothing reads - // a field this class does not know about, so an extra one cannot change what the build does, - // and refusing it only stopped derived projects adding one. - String json = """{"runtime_compositions":["app-bootstrap","sample-portfolio"],"modules":[ + String json = """{"composition_roots":["app-bootstrap","sample-portfolio"],"modules":[ {"id":"app-bootstrap","gradle_path":":app-bootstrap","source_path":"src/alpha", - "allowed_dependencies":[],"runtime_memberships":["app-bootstrap"],"extra":true}, - ${entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]')}]}""" - def registry = read(json) - assertEquals(2, registry.modules.size()) - assertEquals(':app-bootstrap', registry.byId('app-bootstrap').gradlePath) + "allowed_dependencies":[],"owner":"platform"}, + ${entry('sample-portfolio', ':sample-portfolio', 'src/beta')}]}""" + assertEquals(2, read(json).modules.size()) } @Test - @DisplayName("a module entry missing a required field is still refused") void missingRequiredFieldIsRefused() { - String json = """{"runtime_compositions":["app-bootstrap","sample-portfolio"],"modules":[ - {"id":"app-bootstrap","gradle_path":":app-bootstrap","source_path":"src/alpha", - "allowed_dependencies":[]}, - ${entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]')}]}""" + String json = """{"composition_roots":["app-bootstrap","sample-portfolio"],"modules":[ + {"id":"app-bootstrap","gradle_path":":app-bootstrap","source_path":"src/alpha"}, + ${entry('sample-portfolio', ':sample-portfolio', 'src/beta')}]}""" def failure = assertThrows(IllegalStateException) { read(json) } - assertTrue(failure.message.contains('is missing [runtime_memberships]'), failure.message) + assertTrue(failure.message.contains('allowed_dependencies'), failure.message) } @Test - @DisplayName("the registry decides which runtime compositions exist, so a derived project may drop one") - void theRegistryOwnsItsCompositionList() { - // The list used to be a constant here as well as a field in the JSON, and read() only checked - // that the two agreed. A derived project that drops the sample fixture then failed in - // *settings* — before any project exists — with no recovery short of editing this class. - String json = """{"runtime_compositions":["app-bootstrap"], - "modules":[${entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', - '["app-bootstrap"]')}]}""" - - def parsed = read(json) - - assertEquals(['app-bootstrap'], parsed.runtimeCompositions) - assertEquals(['app-bootstrap'], parsed.membersOf('app-bootstrap').collect { it.id }) + void aDerivedBuildMayOwnOneCompositionRoot() { + def parsed = read(registry('["app-bootstrap"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'))) + assertEquals(['app-bootstrap'], parsed.compositionRoots) } @Test - @DisplayName("a composition the registry names is still checked, whatever it is called") - void aRenamedCompositionIsStillChecked() { - // Dropping the constant must not drop the rule. A membership naming something the registry - // does not declare is still refused, against the declared list rather than a fixed one. - String json = """{"runtime_compositions":["service-bootstrap"], - "modules":[${entry('service-bootstrap', ':service-bootstrap', 'src/alpha', '[]', - '["service-bootstrap"]')}, - ${entry('beta', ':beta', 'src/beta', '[]', '["app-bootstrap"]')}]}""" - - def failure = assertThrows(IllegalStateException) { read(json) } - - assertTrue(failure.message.contains('unknown runtime memberships'), failure.message) + void unknownCompositionRootIsRefused() { + def failure = assertThrows(IllegalStateException) { + read(registry('["service-bootstrap"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'))) + } + assertTrue(failure.message.contains('unknown module ids'), failure.message) } @Test - @DisplayName("an empty runtime_compositions list is refused") - void anEmptyCompositionListIsRefused() { - String json = """{"runtime_compositions":[], - "modules":[${entry('alpha', ':alpha', 'src/alpha')}]}""" - - def failure = assertThrows(IllegalStateException) { read(json) } - - assertTrue(failure.message.contains("nonempty 'runtime_compositions'"), failure.message) + void emptyCompositionRootsAreRefused() { + def failure = assertThrows(IllegalStateException) { + read(registry('[]', entry('app-bootstrap', ':app-bootstrap', 'src/alpha'))) + } + assertTrue(failure.message.contains("nonempty 'composition_roots'"), failure.message) } @Test - @DisplayName("a duplicated runtime composition is refused") - void aDuplicatedCompositionIsRefused() { - String json = """{"runtime_compositions":["app-bootstrap","app-bootstrap"], - "modules":[${entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', - '["app-bootstrap"]')}]}""" - - def failure = assertThrows(IllegalStateException) { read(json) } - - assertTrue(failure.message.contains('duplicate runtime_compositions'), failure.message) - } - - @Test - @DisplayName("a runtime composition that does not include itself is refused") - void compositionMustIncludeItself() { - String json = registry( - entry('app-bootstrap', ':app-bootstrap', 'src/alpha', '[]', '[]'), - entry('sample-portfolio', ':sample-portfolio', 'src/beta', '[]', '["sample-portfolio"]')) - def failure = assertThrows(IllegalStateException) { read(json) } - assertTrue(failure.message.contains('must be registered and include itself'), failure.message) + void duplicateCompositionRootsAreRefused() { + def failure = assertThrows(IllegalStateException) { + read(registry('["app-bootstrap","app-bootstrap"]', + entry('app-bootstrap', ':app-bootstrap', 'src/alpha'))) + } + assertTrue(failure.message.contains('duplicate composition_roots'), failure.message) } } diff --git a/src/build-logic/src/test/groovy/PlatformModuleConventionTest.groovy b/src/build-logic/src/test/groovy/PlatformModuleConventionTest.groovy index a07d2426..6b1ff7ac 100644 --- a/src/build-logic/src/test/groovy/PlatformModuleConventionTest.groovy +++ b/src/build-logic/src/test/groovy/PlatformModuleConventionTest.groovy @@ -11,12 +11,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue * The vendored-platform conventions give a leaf what its forty-three build files each wrote by hand, * and refuse to give it half of that silently. * - *

    The grpc BOM is the part worth testing rather than reading. Its two preconditions — a root that - * declares {@code ext.grpcVersion}, and Spring's dependency-management plugin to import into — are - * both satisfied today by the order in which the root build applies things, and both are invisible - * at the call site. A convention that skipped the import when either was missing would not fail - * here; it would surface much later as an io.grpc coordinate with no version, in whichever leaf - * asked for one first. + *

    The grpc BOM is the part worth testing rather than reading. Its two preconditions are a shared + * {@code libs.versions.grpc} entry and Spring's dependency-management plugin to import into. The + * convention must fail at configuration time when the shared catalog contract is incomplete. */ class PlatformModuleConventionTest { @@ -37,6 +34,7 @@ class PlatformModuleConventionTest { spotbugs = "4.10.2" findsecbugs = "1.14.0" errorprone = "2.49.0" + grpc = "1.68.1" '''.stripIndent()) // No explicit `versionCatalogs` block: Gradle imports gradle/libs.versions.toml as `libs` // by convention, and declaring it again is rejected as a second `from` call. @@ -96,8 +94,6 @@ class PlatformModuleConventionTest { doLast { logger.lifecycle('managed-grpc-api=' + managed) } } ''') - Files.writeString(projectDir.resolve('gradle.properties'), "grpcVersion=1.68.1\n") - def result = runner('reportManagedVersion').build() assertTrue(result.output.contains('managed-grpc-api=1.68.1'), @@ -105,8 +101,17 @@ class PlatformModuleConventionTest { } @Test - @DisplayName("the grpc convention refuses a root that declares no grpcVersion") + @DisplayName("the grpc convention refuses a catalog that declares no grpc version") void grpcConventionRefusesAMissingVersion() { + Files.writeString(projectDir.resolve('gradle/libs.versions.toml'), ''' + [versions] + springBoot = "4.0.8" + googleJavaFormat = "1.35.0" + checkstyle = "13.5.0" + spotbugs = "4.10.2" + findsecbugs = "1.14.0" + errorprone = "2.49.0" + '''.stripIndent()) buildFile(''' plugins { id 'ca.grpc-platform-module' @@ -115,8 +120,8 @@ class PlatformModuleConventionTest { def result = runner('tasks').buildAndFail() - assertTrue(result.output.contains('ext.grpcVersion'), - "the refusal should name the property that is missing:\n${result.output}") + assertTrue(result.output.contains("Version catalog 'libs' must define version 'grpc'"), + "the refusal should name the missing catalog entry:\n${result.output}") } @Test @@ -135,8 +140,6 @@ class PlatformModuleConventionTest { doLast { logger.lifecycle('dependency-management-present=' + present) } } ''') - Files.writeString(projectDir.resolve('gradle.properties'), "grpcVersion=1.68.1\n") - def result = runner('reportDependencyManagement').build() assertTrue(result.output.contains('dependency-management-present=true'), diff --git a/src/build-qualification/build.gradle b/src/build-qualification/build.gradle new file mode 100644 index 00000000..b89ad1f6 --- /dev/null +++ b/src/build-qualification/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'groovy-gradle-plugin' +} + +// Release/certification logic lives here, physically separate from compilation conventions. +// These plugins only register qualification/evidence tasks on the application build that applies them. diff --git a/src/build-qualification/settings.gradle b/src/build-qualification/settings.gradle new file mode 100644 index 00000000..f4c9c86e --- /dev/null +++ b/src/build-qualification/settings.gradle @@ -0,0 +1,8 @@ +dependencyResolutionManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +rootProject.name = 'build-qualification' diff --git a/src/gradle/jpa-evidence.gradle b/src/build-qualification/src/main/groovy/ca.jpa-evidence.gradle similarity index 98% rename from src/gradle/jpa-evidence.gradle rename to src/build-qualification/src/main/groovy/ca.jpa-evidence.gradle index d487ce7e..acf44d0c 100644 --- a/src/gradle/jpa-evidence.gradle +++ b/src/build-qualification/src/main/groovy/ca.jpa-evidence.gradle @@ -1,6 +1,5 @@ import groovy.json.JsonOutput import groovy.json.JsonSlurper -import dev.caskeleton.buildlogic.JUnitEvidence import java.nio.charset.StandardCharsets import java.security.MessageDigest import java.time.Instant @@ -19,7 +18,7 @@ import org.gradle.api.tasks.testing.Test File jpaEvidenceRegistryFile = rootProject.file('config/jpa/readiness-cards.yaml') def jpaEvidenceOutputDirectory = layout.buildDirectory.dir('jpa-evidence/manifests') -String jpaEvidenceImage = project.ext.jpaPostgreSqlEvidenceImage as String +String jpaEvidenceImage = providers.gradleProperty('jpaPostgreSqlEvidenceImage').getOrElse('postgres:16-alpine') Closure canonicalizeJpaEvidence canonicalizeJpaEvidence = { Object value -> @@ -100,17 +99,15 @@ Closure> readJpaJUnitResult = { Test testTask -> // Read through the shared reader rather than a second XmlSlurper. This one did not disable // DOCTYPE processing and counted a skipped case as an executed class; junit-evidence.gradle did // neither. Same format, two readers, two answers — the shared one keeps the stricter answer. - def results - try { - results = JUnitEvidence.read(testTask.path, resultDirectory) - } catch (IllegalStateException unreadable) { - throw new GradleException(unreadable.message, unreadable) + if (!rootProject.ext.has('readJUnitEvidence')) { + throw new GradleException('ca.jpa-evidence requires ca.evidence on the root project.') } - int executed = results.tests - int skipped = results.skipped - int failures = results.failures - int errors = results.errors - Set selectors = new TreeSet<>(results.executedSelectors) + Map results = rootProject.ext.readJUnitEvidence(testTask.path, resultDirectory) + int executed = results.tests as int + int skipped = results.skipped as int + int failures = results.failures as int + int errors = results.errors as int + Set selectors = new TreeSet<>(results.executedSelectors as Set) [ tasks: [testTask.path], diff --git a/src/gradle/qualification/jpa-qualification.gradle b/src/build-qualification/src/main/groovy/ca.jpa-qualification.gradle similarity index 99% rename from src/gradle/qualification/jpa-qualification.gradle rename to src/build-qualification/src/main/groovy/ca.jpa-qualification.gradle index ca55b16c..055234a4 100644 --- a/src/gradle/qualification/jpa-qualification.gradle +++ b/src/build-qualification/src/main/groovy/ca.jpa-qualification.gradle @@ -84,7 +84,7 @@ tasks.register('jpaReleaseGate') { // `dependsOn every leaf's checkstyleMain`. Naming the real task removes the indirection and the // misleading name — Checkstyle's OneTopLevelClass is one rule in the D2 ruleset this runs. dependsOn subprojects.findAll { it.childProjects.isEmpty() } - .collect { it.tasks.named('checkstyleMain') } + .collect { "${it.path}:checkstyleMain" } dependsOn 'verifyJpaReleaseGateTasks' dependsOn ':app-bootstrap:test' } diff --git a/src/gradle/qualification/messaging-qualification.gradle b/src/build-qualification/src/main/groovy/ca.messaging-qualification.gradle similarity index 93% rename from src/gradle/qualification/messaging-qualification.gradle rename to src/build-qualification/src/main/groovy/ca.messaging-qualification.gradle index c6ec2182..fef8f4b1 100644 --- a/src/gradle/qualification/messaging-qualification.gradle +++ b/src/build-qualification/src/main/groovy/ca.messaging-qualification.gradle @@ -75,7 +75,7 @@ def prepareMessagingContractEvidence = tasks.register('prepareMessagingContractE // JUnit XML through the shared reader, not a second XmlSlurper. // // This closure used to parse TEST-*.xml itself with `new XmlSlurper(false, false)`. That is the -// same construction src/gradle/jpa-evidence.gradle removed, and it left the reason in a comment: +// same construction the historical JPA evidence script removed, and it left the reason in a comment: // the shared reader additionally sets `disallow-doctype-decl`, so two readers of the same files did // not agree on how to read them, and only one of them could be what the author meant. It is also // where the counts come from — dev.caskeleton.buildlogic.JUnitEvidence takes them from the suite @@ -91,17 +91,15 @@ def messagingEvidenceFromXml = { List resultDirectories -> Set selectors = new TreeSet<>() resultDirectories.each { String directory -> File resultDirectory = messagingEvidenceResultRoot.get().dir(directory).asFile - def results - try { - results = dev.caskeleton.buildlogic.JUnitEvidence.read( - "messaging-evidence/${directory}", resultDirectory) - } catch (IllegalStateException unreadable) { - throw new GradleException(unreadable.message, unreadable) + if (!rootProject.ext.has('readJUnitEvidence')) { + throw new GradleException('ca.messaging-qualification requires ca.evidence on the root project.') } - executed += results.tests - failed += results.failures + results.errors - skipped += results.skipped - selectors.addAll(results.executedSelectors) + Map results = rootProject.ext.readJUnitEvidence( + "messaging-evidence/${directory}", resultDirectory) + executed += results.tests as int + failed += (results.failures as int) + (results.errors as int) + skipped += results.skipped as int + selectors.addAll(results.executedSelectors as Set) } if (executed <= 0) { throw new GradleException('Messaging qualification XML contains no discovered test cases.') @@ -194,8 +192,7 @@ def writeMessagingEvidence = { schema: messagingSha256FileSet( 'ca-skeleton.messaging.evidence.schema-set.v1', [ - file('shared-contract/src/main/resources/contracts/messaging/envelope/v1.schema.json'), - file('sample-portfolio/src/main/resources/contracts/messaging/portfolio.worklog.reserved/v1.schema.json') + file('shared-contract/src/main/resources/contracts/messaging/envelope/v1.schema.json') ] + fileTree( 'adapter/outbound/messaging/src/main/resources/contracts/messaging/meta/draft-2020-12' ).files.toList()), @@ -272,7 +269,6 @@ def verifyMessagingContracts = tasks.register('verifyMessagingContracts') { dependsOn validateMessagingJsonSchemaV1EvidenceManifestSchema dependsOn ':application-core:messagingApplicationContractQualificationTest' dependsOn ':shared-contract:messagingSharedSchemaQualificationTest' - dependsOn ':sample-portfolio:messagingSampleContractQualificationTest' dependsOn ':adapter:outbound:messaging:messagingCompiledContractsQualificationTest' dependsOn ':adapter:outbound:messaging:messagingJsonSchemaV1QualificationTest' dependsOn ':adapter:outbound:messaging:verifyJsonSchemaRuntimeGraph' @@ -281,11 +277,10 @@ def verifyMessagingContracts = tasks.register('verifyMessagingContracts') { doLast { writeMessagingEvidence( 'verifyMessagingContracts', - ['application', 'shared', 'sample', 'compiled', 'json-schema'], + ['application', 'shared', 'compiled', 'json-schema'], [ ':application-core:messagingApplicationContractQualificationTest', ':shared-contract:messagingSharedSchemaQualificationTest', - ':sample-portfolio:messagingSampleContractQualificationTest', ':adapter:outbound:messaging:messagingCompiledContractsQualificationTest', ':adapter:outbound:messaging:messagingJsonSchemaV1QualificationTest', 'verifyMessagingContracts' diff --git a/src/build.gradle b/src/build.gradle index ae7d90ea..3f18b94c 100644 --- a/src/build.gradle +++ b/src/build.gradle @@ -17,6 +17,13 @@ plugins { id 'ca.evidence' id 'ca.runtime-membership' id 'ca.architecture' + id 'ca.archive-hygiene' + id 'ca.public-path-snapshot' + id 'ca.notification-api-surface' + id 'ca.notification-evidence' + id 'ca.notification-configuration' + id 'ca.jpa-qualification' + id 'ca.messaging-qualification' } // feature-build-release-supply-chain-contract D1/D9 — a released archive carries an exact SemVer @@ -39,14 +46,9 @@ if (!(releaseVersion ==~ /\d+\.\d+\.\d+/)) { "releaseVersion must be MAJOR.MINOR.PATCH without a leading 'v', pre-release, or build metadata; got '${releaseVersion}'.") } -def localGitRevision = providers.exec { - commandLine 'git', 'rev-parse', '--short=12', 'HEAD' - ignoreExitValue = true -}.standardOutput.asText.map { it.trim() } String declaredRevision = providers.gradleProperty('gitRevision') .orElse(providers.environmentVariable('GIT_SHA')) .orElse(providers.environmentVariable('GITHUB_SHA')) - .orElse(localGitRevision) .getOrElse('') boolean revisionIsTraceable = declaredRevision ==~ /(?i)[0-9a-f]{7,40}/ String sourceRevision = revisionIsTraceable @@ -76,61 +78,11 @@ tasks.register('verifyReleaseProvenance') { } } -// Inbound gRPC adapter (adapter:inbound:grpc) — the Spring Boot BOM does NOT manage io.grpc:* or -// protobuf versions, so they are pinned here and imported by the grpc module as MODULE-scoped -// io.grpc:grpc-bom + protobuf-bom platforms (not the shared dependencyManagement block below), -// keeping the strict-locking blast radius to the grpc module alone. -// -// The numbers now live in gradle/libs.versions.toml. These two lines stay because they are the -// accessor, not the answer: eight leaf build files interpolate `${grpcVersion}` / `${protobufVersion}` -// into coordinate strings, and ca.grpc-platform-module.gradle — a convention plugin in the included -// build — reads `rootProject.findProperty('grpcVersion')` and fails closed when it is absent. Moving -// the number is what the catalog is for; moving the accessor would rewrite those nine files and -// regenerate their lockfiles for no change in what resolves. -// -// All three live protobuf versions are visible side by side in the catalog now, which is the whole -// point — the comment here used to say "this repo has no version catalog", which was already false: -// libs protobufGrpc 3.25.5 -> adapter/inbound/grpc (read through ext.protobufVersion) -// libs protobuf 4.33.2 -> adapter/inbound/websocket -// libs protobufLegacy 4.29.3 -> messaging/messaging-schema-protobuf -ext.grpcVersion = libs.versions.grpc.get() -ext.protobufVersion = libs.versions.protobufGrpc.get() -// Outbound objectstorage adapter (adapter:outbound:objectstorage) — the Spring Boot BOM does NOT -// manage software.amazon.awssdk:* versions. Pinned here so the objectstorage module imports -// software.amazon.awssdk:bom as a platform at MODULE scope (not the shared dependencyManagement -// block below) — mirroring the grpc approach above and keeping the strict-locking blast radius to -// the objectstorage module alone. The version is in the catalog; this is the accessor three leaves -// interpolate, for the same reason as `ext.grpcVersion` above. -ext.awsSdkVersion = libs.versions.awsSdk.get() -apply from: "${rootProject.projectDir}/gradle/archive-hygiene.gradle" -apply from: "${rootProject.projectDir}/gradle/public-path-snapshot.gradle" -apply from: "${rootProject.projectDir}/gradle/notification-api-surface.gradle" -apply from: "${rootProject.projectDir}/gradle/notification-evidence.gradle" -apply from: "${rootProject.projectDir}/gradle/notification-configuration.gradle" -apply from: "${rootProject.projectDir}/gradle/test-jvm-agents.gradle" - -// Platform qualification, kept out of this file on purpose. -// -// Both scripts register release-certification tasks for one platform each — a readiness card -// registry with its own DAG and evidence grammar, and a content-addressed evidence manifest writer. -// Together they were about 900 of this file's 3,200 lines, and neither is build policy: nothing here -// decides how anything compiles. They are applied rather than deleted because the task names are -// what CI and the support matrices call, and they are in gradle/qualification/ so that the next -// reader can see that is what they are. -apply from: "${rootProject.projectDir}/gradle/qualification/jpa-qualification.gradle" -apply from: "${rootProject.projectDir}/gradle/qualification/messaging-qualification.gradle" - -allprojects { - group = 'dev.caskeleton' - version = rootProject.ext.traceableVersion - - repositories { - mavenCentral() - } -} +group = 'dev.caskeleton' +version = traceableVersion Map> conditionalTransportEvidence = [ 'conditional-transport-graphql': @@ -172,8 +124,10 @@ tasks.register('resolveAndLockAll') { tasks.register('verifyDependencyLocks') { group = 'verification' - description = 'Verifies strict dependency lock state for all subprojects.' - dependsOn subprojects.findAll { it.childProjects.isEmpty() }.collect { "${it.path}:verifyDependencyLocks" } + description = 'Verifies strict dependency lock state for production CI subprojects.' + dependsOn subprojects.findAll { + it.childProjects.isEmpty() && it.path != ':sample-portfolio' + }.collect { "${it.path}:verifyDependencyLocks" } } // feature-developer-experience-contract D3 — one ordered first-run entrypoint. Each stage is a @@ -186,8 +140,8 @@ def composeCommand = ['docker', 'compose', '-f', baseComposeFile.absolutePath, def bootstrapCompile = tasks.register('bootstrapCompile') { group = 'developer experience' - description = 'Stage 1/5: compiles every main and test source set as a local sanity check.' - dependsOn subprojects.findAll { it.childProjects.isEmpty() }.collect { "${it.path}:compileTestJava" } + description = 'Stage 1/4: compiles the default application composition and its required upstream projects.' + dependsOn ':app-bootstrap:compileTestJava' } def bootstrapDockerPreflight = tasks.register('bootstrapDockerPreflight', Exec) { @@ -209,7 +163,7 @@ bootstrapDockerPreflight.configure { dependsOn bootstrapCompile } def bootstrapDependencies = tasks.register('bootstrapDependencies', Exec) { group = 'developer experience' - description = 'Stage 2/5: starts the local PostgreSQL dependency and waits for readiness.' + description = 'Stage 2/4: starts the local PostgreSQL dependency and waits for readiness.' commandLine composeCommand + ['up', '-d', '--wait', 'db'] } bootstrapDependencies.configure { dependsOn bootstrapCompile } @@ -217,21 +171,14 @@ bootstrapDependencies.configure { dependsOn bootstrapDockerPreflight } def bootstrapMigrateAndStart = tasks.register('bootstrapMigrateAndStart', Exec) { group = 'developer experience' - description = 'Stage 3/5: builds/starts the app; startup Flyway must finish before health is ready.' + description = 'Stage 3/4: builds/starts the app; startup Flyway must finish before health is ready.' commandLine composeCommand + ['up', '-d', '--build', '--wait', 'app'] } bootstrapMigrateAndStart.configure { dependsOn bootstrapDependencies } -def bootstrapSampleContract = tasks.register('bootstrapSampleContract') { - group = 'developer experience' - description = 'Stage 4/5: verifies the delegated sample production-isolation/build contract.' - dependsOn project(':app-bootstrap').tasks.named('bootstrapSampleContract') -} -bootstrapSampleContract.configure { dependsOn bootstrapMigrateAndStart } - def bootstrapSmoke = tasks.register('bootstrapSmoke') { group = 'developer experience' - description = 'Stage 5/5: requires HTTP 200 and status=UP from GET /api/healthcheck.' + description = 'Stage 4/4: requires HTTP 200 and status=UP from GET /api/healthcheck.' doLast { URI endpoint = URI.create('http://localhost:8080/api/healthcheck') long deadline = System.nanoTime() + java.util.concurrent.TimeUnit.SECONDS.toNanos(60) @@ -262,11 +209,11 @@ def bootstrapSmoke = tasks.register('bootstrapSmoke') { "bootstrapSmoke: /api/healthcheck did not become healthy within 60s; last result: ${lastFailure}") } } -bootstrapSmoke.configure { dependsOn bootstrapSampleContract } +bootstrapSmoke.configure { dependsOn bootstrapMigrateAndStart } tasks.register('bootstrap') { group = 'developer experience' - description = 'Runs the complete five-stage local bootstrap contract.' + description = 'Runs the complete four-stage local bootstrap contract.' dependsOn bootstrapSmoke } @@ -275,16 +222,16 @@ tasks.register('bootstrap') { // // ::check that leaf — compile, its tests, Spotless, Checkstyle, Error Prone // check every leaf's check (Gradle matches the name across projects) -// architectureCheck dependency direction, runtime membership, application-core purity, -// Git-carryable sources (ca.architecture) +// architectureCheck dependency direction + app-bootstrap whole-repository ArchUnit rules // qualityCheck SpotBugs + FindSecBugs across every leaf (ca.quality-conventions) // configContractCheck the environment contract (:app-bootstrap) // integrationCheck the declared strict test lanes (ca.strict-test-lane) -// ci check + architectureCheck + qualityCheck + configContractCheck +// qualificationCheck repository/TestKit behavior + full boot composition + auxiliary style +// ci leaf checks + repository gates + qualification // releaseCheck ci + archive hygiene + public path snapshot + release provenance // // There is no root `check`. There used to be, holding nine repository-wide gates, and before that -// every one of them hung off all 62 leaves — so `./gradlew :domain-core:check` compiled cache-redis, +// every one of them hung off all main-build leaves — so `./gradlew :domain-core:check` compiled cache-redis, // walked the repository twice and parsed every runbook. A leaf's check is that leaf's; a repository // question gets a repository task with a name that says which question. // --------------------------------------------------------------------------------------------- @@ -296,13 +243,19 @@ tasks.register('bootstrap') { // // This is the visible cost of leaves owning their own plugins, and it is the right trade: the root // no longer reaches into another project's task container at all. -List leafPaths = subprojects.findAll { it.childProjects.isEmpty() } - .collect { it.path } - .toSorted() +List leafPaths = subprojects.findAll { + it.childProjects.isEmpty() && it.path != ':sample-portfolio' +}.collect { it.path }.toSorted() + +tasks.register('quarantineTest') { + group = 'verification' + description = 'Runs advisory quarantine tests across production CI leaves only.' + dependsOn leafPaths.collect { "${it}:quarantineTest" } +} tasks.register('qualityCheck') { group = 'verification' - description = 'Runs SpotBugs and FindSecBugs across every leaf.' + description = 'Runs SpotBugs and FindSecBugs across every production CI leaf.' dependsOn leafPaths.collect { "${it}:qualityCheck" } } @@ -314,17 +267,30 @@ tasks.register('configContractCheck') { tasks.register('integrationCheck') { group = 'verification' - description = 'Runs every declared strict test lane. Several need a container runtime.' + description = "Runs every production CI leaf's declared strict test lane. Several need a container runtime." dependsOn leafPaths.collect { "${it}:strictTestLaneCheck" } } +tasks.named('architectureCheck') { + dependsOn ':app-bootstrap:architectureTest' +} + +tasks.register('qualificationCheck') { + group = 'verification' + description = 'Runs repository/build-behavior qualification that is intentionally outside local leaf checks.' + dependsOn ':app-bootstrap:functionalTest' + dependsOn ':app-bootstrap:bootCompositionTest' + dependsOn leafPaths.collect { "${it}:auxiliaryStyleCheck" } +} + tasks.register('ci') { group = 'verification' - description = 'The pull-request gate: every leaf check plus the repository-wide gates.' + description = 'The pull-request gate: every production CI leaf check plus the repository-wide gates.' dependsOn leafPaths.collect { "${it}:check" } dependsOn tasks.named('architectureCheck') dependsOn tasks.named('qualityCheck') dependsOn tasks.named('configContractCheck') + dependsOn tasks.named('qualificationCheck') } tasks.register('releaseCheck') { diff --git a/src/config/architecture/modules.json b/src/config/architecture/modules.json index 4b8e860c..ff9d9c64 100644 --- a/src/config/architecture/modules.json +++ b/src/config/architecture/modules.json @@ -1,28 +1,16 @@ { - "runtime_compositions": [ - "app-bootstrap", - "sample-portfolio" - ], "modules": [ { "id": "domain-core", "gradle_path": ":domain-core", "source_path": "src/domain-core", - "allowed_dependencies": [], - "runtime_memberships": [ - "app-bootstrap", - "sample-portfolio" - ] + "allowed_dependencies": [] }, { "id": "shared-contract", "gradle_path": ":shared-contract", "source_path": "src/shared-contract", - "allowed_dependencies": [], - "runtime_memberships": [ - "app-bootstrap", - "sample-portfolio" - ] + "allowed_dependencies": [] }, { "id": "application-core", @@ -31,10 +19,6 @@ "allowed_dependencies": [ "domain-core", "shared-contract" - ], - "runtime_memberships": [ - "app-bootstrap", - "sample-portfolio" ] }, { @@ -45,9 +29,6 @@ "domain-core", "application-core", "shared-contract" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -58,20 +39,13 @@ "domain-core", "application-core", "shared-contract" - ], - "runtime_memberships": [ - "app-bootstrap", - "sample-portfolio" ] }, { "id": "adapter-outbound-persistence-mongo", "gradle_path": ":adapter:outbound:persistence-mongo", "source_path": "src/adapter/outbound/persistence-mongo", - "allowed_dependencies": [], - "runtime_memberships": [ - "app-bootstrap" - ] + "allowed_dependencies": [] }, { "id": "adapter-outbound-identifier", @@ -80,10 +54,6 @@ "allowed_dependencies": [ "domain-core", "application-core" - ], - "runtime_memberships": [ - "app-bootstrap", - "sample-portfolio" ] }, { @@ -93,9 +63,6 @@ "allowed_dependencies": [ "application-core", "shared-contract" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -105,9 +72,6 @@ "allowed_dependencies": [ "application-core", "shared-contract" - ], - "runtime_memberships": [ - "sample-portfolio" ] }, { @@ -119,9 +83,6 @@ "application-core", "shared-contract", "adapter-outbound-support" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -133,9 +94,6 @@ "application-core", "shared-contract", "adapter-outbound-support" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -147,9 +105,6 @@ "application-core", "shared-contract", "adapter-outbound-support" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -161,9 +116,6 @@ "application-core", "shared-contract", "adapter-outbound-support" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -174,10 +126,6 @@ "domain-core", "application-core", "shared-contract" - ], - "runtime_memberships": [ - "app-bootstrap", - "sample-portfolio" ] }, { @@ -188,8 +136,7 @@ "domain-core", "application-core", "shared-contract" - ], - "runtime_memberships": [] + ] }, { "id": "adapter-inbound-graphql", @@ -199,9 +146,6 @@ "domain-core", "application-core", "shared-contract" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -212,8 +156,7 @@ "domain-core", "application-core", "shared-contract" - ], - "runtime_memberships": [] + ] }, { "id": "app-bootstrap", @@ -235,9 +178,6 @@ "adapter-outbound-persistence-mongo", "adapter-inbound-graphql", "messaging-spring-boot-starter" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -252,19 +192,13 @@ "adapter-outbound-objectstorage", "adapter-inbound-web", "shared-contract" - ], - "runtime_memberships": [ - "sample-portfolio" ] }, { "id": "messaging-core-api", "gradle_path": ":messaging:messaging-core-api", "source_path": "src/messaging/messaging-core-api", - "allowed_dependencies": [], - "runtime_memberships": [ - "app-bootstrap" - ] + "allowed_dependencies": [] }, { "id": "messaging-schema-api", @@ -272,9 +206,6 @@ "source_path": "src/messaging/messaging-schema-api", "allowed_dependencies": [ "messaging-core-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -284,9 +215,6 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-schema-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -296,8 +224,7 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-schema-api" - ], - "runtime_memberships": [] + ] }, { "id": "messaging-schema-protobuf", @@ -306,8 +233,7 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-schema-api" - ], - "runtime_memberships": [] + ] }, { "id": "messaging-cloudevents", @@ -316,9 +242,6 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-schema-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -328,9 +251,6 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-schema-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -341,9 +261,6 @@ "messaging-core-api", "messaging-schema-api", "messaging-policy" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -357,9 +274,6 @@ "messaging-transport-spi", "messaging-security", "messaging-observability" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -368,9 +282,6 @@ "source_path": "src/messaging/messaging-observability", "allowed_dependencies": [ "messaging-core-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -379,9 +290,6 @@ "source_path": "src/messaging/messaging-security", "allowed_dependencies": [ "messaging-core-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -396,9 +304,6 @@ "messaging-observability", "messaging-security", "messaging-admin-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -410,8 +315,7 @@ "messaging-policy", "messaging-transport-spi", "messaging-kafka" - ], - "runtime_memberships": [] + ] }, { "id": "messaging-rabbit", @@ -425,9 +329,6 @@ "messaging-observability", "messaging-security", "messaging-admin-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -436,9 +337,6 @@ "source_path": "src/messaging/messaging-reliability-api", "allowed_dependencies": [ "messaging-core-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -451,9 +349,6 @@ "messaging-policy", "messaging-observability", "messaging-admin-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -463,9 +358,6 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-reliability-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -475,9 +367,6 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-reliability-api" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -487,9 +376,6 @@ "allowed_dependencies": [ "messaging-core-api", "messaging-policy" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -503,9 +389,6 @@ "messaging-transport-spi", "messaging-security", "messaging-observability" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -520,8 +403,7 @@ "messaging-observability", "messaging-security", "messaging-admin-api" - ], - "runtime_memberships": [] + ] }, { "id": "messaging-nats-experimental", @@ -535,8 +417,7 @@ "messaging-observability", "messaging-security", "messaging-admin-api" - ], - "runtime_memberships": [] + ] }, { "id": "messaging-spring-cloud-stream-bridge", @@ -546,8 +427,7 @@ "messaging-core-api", "messaging-policy", "messaging-transport-spi" - ], - "runtime_memberships": [] + ] }, { "id": "messaging-spring-boot-starter", @@ -571,9 +451,6 @@ "messaging-schema-json", "messaging-security", "messaging-transport-spi" - ], - "runtime_memberships": [ - "app-bootstrap" ] }, { @@ -585,205 +462,11 @@ "messaging-schema-api", "messaging-policy", "messaging-transport-spi" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-core-api", - "gradle_path": ":grpc:grpc-core-api", - "source_path": "src/grpc/grpc-core-api", - "allowed_dependencies": [], - "runtime_memberships": [] - }, - { - "id": "grpc-proto-contract", - "gradle_path": ":grpc:grpc-proto-contract", - "source_path": "src/grpc/grpc-proto-contract", - "allowed_dependencies": [ - "grpc-core-api" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-codegen", - "gradle_path": ":grpc:grpc-codegen", - "source_path": "src/grpc/grpc-codegen", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-proto-contract" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-policy", - "gradle_path": ":grpc:grpc-policy", - "source_path": "src/grpc/grpc-policy", - "allowed_dependencies": [ - "grpc-core-api" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-server", - "gradle_path": ":grpc:grpc-server", - "source_path": "src/grpc/grpc-server", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-policy" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-client", - "gradle_path": ":grpc:grpc-client", - "source_path": "src/grpc/grpc-client", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-policy" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-discovery", - "gradle_path": ":grpc:grpc-discovery", - "source_path": "src/grpc/grpc-discovery", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-client" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-admin", - "gradle_path": ":grpc:grpc-admin", - "source_path": "src/grpc/grpc-admin", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-server" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-observability", - "gradle_path": ":grpc:grpc-observability", - "source_path": "src/grpc/grpc-observability", - "allowed_dependencies": [ - "grpc-core-api" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-operation-ledger-jpa", - "gradle_path": ":grpc:grpc-operation-ledger-jpa", - "source_path": "src/grpc/grpc-operation-ledger-jpa", - "allowed_dependencies": [ - "grpc-core-api" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-spring-boot-starter", - "gradle_path": ":grpc:grpc-spring-boot-starter", - "source_path": "src/grpc/grpc-spring-boot-starter", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-proto-contract", - "grpc-codegen", - "grpc-policy", - "grpc-server", - "grpc-client", - "grpc-discovery", - "grpc-admin", - "grpc-observability", - "grpc-operation-ledger-jpa" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-testkit", - "gradle_path": ":grpc:grpc-testkit", - "source_path": "src/grpc/grpc-testkit", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-proto-contract", - "grpc-codegen", - "grpc-policy", - "grpc-server", - "grpc-client", - "grpc-discovery", - "grpc-admin", - "grpc-observability", - "grpc-operation-ledger-jpa" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-advanced-bootstrap", - "gradle_path": ":grpc-advanced:grpc-advanced-bootstrap", - "source_path": "src/grpc-advanced/grpc-advanced-bootstrap", - "allowed_dependencies": [ - "grpc-core-api" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-advanced-edition", - "gradle_path": ":grpc-advanced:grpc-advanced-edition", - "source_path": "src/grpc-advanced/grpc-advanced-edition", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-proto-contract", - "grpc-advanced-bootstrap" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-advanced-streaming", - "gradle_path": ":grpc-advanced:grpc-advanced-streaming", - "source_path": "src/grpc-advanced/grpc-advanced-streaming", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-policy", - "grpc-advanced-bootstrap" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-advanced-resilience", - "gradle_path": ":grpc-advanced:grpc-advanced-resilience", - "source_path": "src/grpc-advanced/grpc-advanced-resilience", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-policy", - "grpc-client", - "grpc-discovery", - "grpc-advanced-bootstrap" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-advanced-compat", - "gradle_path": ":grpc-advanced:grpc-advanced-compat", - "source_path": "src/grpc-advanced/grpc-advanced-compat", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-policy", - "grpc-server", - "grpc-client", - "grpc-advanced-bootstrap" - ], - "runtime_memberships": [] - }, - { - "id": "grpc-advanced-diagnostics", - "gradle_path": ":grpc-advanced:grpc-advanced-diagnostics", - "source_path": "src/grpc-advanced/grpc-advanced-diagnostics", - "allowed_dependencies": [ - "grpc-core-api", - "grpc-client", - "grpc-advanced-bootstrap" - ], - "runtime_memberships": [] + ] } + ], + "composition_roots": [ + "app-bootstrap", + "sample-portfolio" ] } diff --git a/src/domain-core/build.gradle b/src/domain-core/build.gradle index ae29c405..245498fa 100644 --- a/src/domain-core/build.gradle +++ b/src/domain-core/build.gradle @@ -1,5 +1,7 @@ -// Pure domain layer. No Spring, no infra dependencies. -apply plugin: 'ca.java-library' +plugins { + id 'ca.java-library' +} +// Pure domain layer. No Spring, no infra dependencies. dependencies { } diff --git a/src/gradle.properties b/src/gradle.properties new file mode 100644 index 00000000..ffc84a89 --- /dev/null +++ b/src/gradle.properties @@ -0,0 +1,5 @@ +# Conservative defaults for this large multi-project build. CI or a developer with more headroom +# can override these with -Dorg.gradle.workers.max=N / GRADLE_OPTS as needed. +org.gradle.workers.max=4 +org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 +org.gradle.caching=true diff --git a/src/gradle/test-jvm-agents.gradle b/src/gradle/test-jvm-agents.gradle deleted file mode 100644 index 56ecfd88..00000000 --- a/src/gradle/test-jvm-agents.gradle +++ /dev/null @@ -1,56 +0,0 @@ -import org.gradle.api.GradleException -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.testing.Test -import org.gradle.process.CommandLineArgumentProvider - -abstract class MockitoAgentArgumentProvider implements CommandLineArgumentProvider { - @Classpath - abstract ConfigurableFileCollection getMockitoCoreClasspath() - - @Input - abstract Property getOwner() - - @Override - Iterable asArguments() { - List candidates = mockitoCoreClasspath.files.findAll { File file -> - file.isFile() && file.name ==~ 'mockito-core-[^/]+\\.jar' - }.sort { File left, File right -> left.absolutePath <=> right.absolutePath } - - if (candidates.size() != 1) { - throw new GradleException( - "${owner.get()}: expected exactly one mockito-core JAR for the test JVM, " + - "found ${candidates.size()}: " + - candidates.collect { it.absolutePath }) - } - - File mockitoCore = candidates[0] - ["-javaagent:${mockitoCore.absolutePath}", '-Xshare:off'] - } -} - -rootProject.subprojects { Project target -> - target.pluginManager.withPlugin('java') { - target.pluginManager.withPlugin('io.spring.dependency-management') { - def mockitoAgentDependencies = - target.configurations.dependencyScope('mockitoAgentDependencies') - def mockitoAgent = target.configurations.resolvable('mockitoAgent') { - description = 'Mockito core JAR used only as a Test JVM startup agent.' - extendsFrom(mockitoAgentDependencies.get()) - transitive = false - } - - target.dependencies.add( - mockitoAgentDependencies.get().name, 'org.mockito:mockito-core') - - target.tasks.withType(Test).configureEach { - def provider = target.objects.newInstance(MockitoAgentArgumentProvider) - provider.mockitoCoreClasspath.from(mockitoAgent) - provider.owner.set("${target.path}:${name}") - jvmArgumentProviders.add(provider) - } - } - } -} diff --git a/src/grpc-advanced/grpc-advanced-bootstrap/build.gradle b/src/grpc-advanced/grpc-advanced-bootstrap/build.gradle index 4310d025..347ed7df 100644 --- a/src/grpc-advanced/grpc-advanced-bootstrap/build.gradle +++ b/src/grpc-advanced/grpc-advanced-bootstrap/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // The Advanced boundary itself: capability grades, the `ca-skeleton.grpc.advanced.*` feature-flag // contract, the module guard that refuses an unflagged capability, and the per-capability diff --git a/src/grpc-advanced/grpc-advanced-compat/build.gradle b/src/grpc-advanced/grpc-advanced-compat/build.gradle index 56617acc..011791e7 100644 --- a/src/grpc-advanced/grpc-advanced-compat/build.gradle +++ b/src/grpc-advanced/grpc-advanced-compat/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Compatibility bridges: gRPC-Web, the Servlet HTTP/2 profile, the Spring Integration bridge, the // Reactor adapter, and the Kotlin coroutine/Flow boundary. diff --git a/src/grpc-advanced/grpc-advanced-diagnostics/build.gradle b/src/grpc-advanced/grpc-advanced-diagnostics/build.gradle index 07b2526a..e9cb9d07 100644 --- a/src/grpc-advanced/grpc-advanced-diagnostics/build.gradle +++ b/src/grpc-advanced/grpc-advanced-diagnostics/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Channelz/CSDS diagnostics for administrators, with the redactor that keeps socket authority, // credentials, certificate material, metadata and payload out of a snapshot, plus the advanced diff --git a/src/grpc-advanced/grpc-advanced-edition/build.gradle b/src/grpc-advanced/grpc-advanced-edition/build.gradle index bbfbff57..bbf33e2b 100644 --- a/src/grpc-advanced/grpc-advanced-edition/build.gradle +++ b/src/grpc-advanced/grpc-advanced-edition/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Protobuf Edition lanes. Edition 2024 is an opt-in Advanced lane that must produce cross-consumer // compile evidence before anything public moves onto it; Edition 2026 is a watch lane that records diff --git a/src/grpc-advanced/grpc-advanced-resilience/build.gradle b/src/grpc-advanced/grpc-advanced-resilience/build.gradle index 787ea9f9..1e56b2dc 100644 --- a/src/grpc-advanced/grpc-advanced-resilience/build.gradle +++ b/src/grpc-advanced/grpc-advanced-resilience/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Resilience and discovery capabilities that Stable refuses: read-only unary hedging, the custom // name resolver SPI, the custom load balancer SPI, and the proxyless xDS experimental profile. diff --git a/src/grpc-advanced/grpc-advanced-streaming/build.gradle b/src/grpc-advanced/grpc-advanced-streaming/build.gradle index 8ba3cd4e..2e5bbbdf 100644 --- a/src/grpc-advanced/grpc-advanced-streaming/build.gradle +++ b/src/grpc-advanced/grpc-advanced-streaming/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // The streaming shapes the Stable plan deliberately excludes: client streaming sessions with // dedup/checkpoint/resume, bidirectional sessions with independent per-direction sequences, and the diff --git a/src/grpc/CLAUDE.md b/src/grpc/CLAUDE.md index 5423a419..4d0fcb8a 100644 --- a/src/grpc/CLAUDE.md +++ b/src/grpc/CLAUDE.md @@ -1,9 +1,8 @@ # grpc — local authority for the gRPC platform family -이 문서는 `grpc:*` family의 **local authority**다. leaf 목록·gradle path·허용 의존성은 -`src/config/architecture/modules.json`이 SSOT이며 이 문서는 그것을 복제하지 않는다. 여기서 정하는 -것은 registry가 표현할 수 없는 것들이다: family별 framework 규칙, 증거 등급, Stable 범위의 경계, -그리고 `runtime_memberships`가 비어 있다는 사실의 의미. +이 문서는 `grpc:*` family의 **local authority**다. gRPC platform은 기본 application build와 분리된 +`src/optional-platforms` build가 소유한다. 여기서 정하는 것은 family별 framework 규칙, 증거 등급, +Stable 범위의 경계와 application에 채택될 때 필요한 계약이다. Root 정책(`CLAUDE.md` / `AGENTS.md`)과 충돌하면 root가 이긴다. @@ -70,17 +69,16 @@ capability flag 뒤에 있다. `GrpcPlatformStartupValidator`가 Stable catalog `grpc-spring-boot-starter`는 어떤 advanced module도 참조하지 않는다. registry의 `allowed_dependencies`가 이를 build time에, `GrpcStableBuildInvariant`가 runtime에 강제한다. -## Runtime membership이 비어 있다는 것의 의미 +## Optional build에 있다는 것의 의미 -registry의 모든 `grpc:*` leaf는 현재 `runtime_memberships`가 비어 있다. `messaging:*`가 처음 -착지했을 때와 같은 상태다: +`grpc:*`와 `grpc-advanced:*`는 기본 `ca-skeleton` settings에 include되지 않는다. 따라서 일반 +`:app-bootstrap:build`는 이 18개 project를 configure하지 않는다. 플랫폼 자체는 +`./gradlew -p optional-platforms ci`로 검증하고, application runtime에 실제 채택하려면 composition root가 +명시적으로 dependency를 가져오는 별도 변경이 필요하다. -- **배포 아티팩트가 싣고 있지 않다.** build-only다. -- 애플리케이션에 배선하려면 registry의 `runtime_memberships`를 먼저 바꾸고 - `verifyRuntimeModuleMembership`을 통과시켜야 한다. 코드만 추가하는 것으로는 런타임에 들어가지 않는다. -- **런타임에 있다는 것과 자격이 증명됐다는 것은 다르다.** 현재 in-process·Netty·fault lane은 실제로 - 실행되어 통과하지만, 실제 배포 환경에서의 soak·performance baseline은 없다. `GrpcStableReleaseGate`가 - 그 구분을 문서가 아니라 코드로 붙들고 있다. +런타임 포함 여부와 qualification은 별개다. in-process·Netty·fault lane이 통과해도 실제 배포 환경의 +soak/performance baseline을 자동으로 의미하지 않는다. 지원 범위는 release gate와 support matrix가 +별도로 정의한다. ## application이 이 family에 도달하는 경로 diff --git a/src/grpc/grpc-admin/build.gradle b/src/grpc/grpc-admin/build.gradle index 707966ef..fafabf09 100644 --- a/src/grpc/grpc-admin/build.gradle +++ b/src/grpc/grpc-admin/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Operational surface: the standard health registry, the reflection exposure policy, the drain // coordinator, and the secret-free runtime policy snapshot an administrator reads. diff --git a/src/grpc/grpc-client/build.gradle b/src/grpc/grpc-client/build.gradle index b6b83a77..3ea3d8d4 100644 --- a/src/grpc/grpc-client/build.gradle +++ b/src/grpc/grpc-client/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.grpc-platform-module' +plugins { + id 'ca.grpc-platform-module' +} // Client runtime: named channel profiles, channel runtime generations with drain, the typed stub // factory that refuses to hand a raw Channel to application code, and client metadata/credentials. @@ -7,6 +9,6 @@ dependencies { api project(':grpc:grpc-core-api') api project(':grpc:grpc-policy') - api "io.grpc:grpc-api:${grpcVersion}" - api "io.grpc:grpc-stub:${grpcVersion}" + api "io.grpc:grpc-api" + api "io.grpc:grpc-stub" } diff --git a/src/grpc/grpc-codegen/build.gradle b/src/grpc/grpc-codegen/build.gradle index 0e58993b..f45471ab 100644 --- a/src/grpc/grpc-codegen/build.gradle +++ b/src/grpc/grpc-codegen/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Contract governance: Buf format/lint/breaking policy, the single codegen owner declaration, and // the descriptor/schema-hash release artifact with its consumer-compile gate. diff --git a/src/grpc/grpc-core-api/build.gradle b/src/grpc/grpc-core-api/build.gradle index caea738a..ddde61d0 100644 --- a/src/grpc/grpc-core-api/build.gradle +++ b/src/grpc/grpc-core-api/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // The platform's port layer: identifiers, method policy, execution evidence, failure model, // deadline primitives and request context. diff --git a/src/grpc/grpc-discovery/build.gradle b/src/grpc/grpc-discovery/build.gradle index 7b58bb1a..bee89e7a 100644 --- a/src/grpc/grpc-discovery/build.gradle +++ b/src/grpc/grpc-discovery/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Stable discovery: Static/DNS resolvers, pick_first/round_robin load balancing, and the // Kubernetes VIP / headless / mesh routing profiles. Custom resolvers, custom load balancers and diff --git a/src/grpc/grpc-observability/build.gradle b/src/grpc/grpc-observability/build.gradle index 7b09ff8e..f6ed15c0 100644 --- a/src/grpc/grpc-observability/build.gradle +++ b/src/grpc/grpc-observability/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Bounded observability: logical RPC vs physical attempt vs stream lifecycle, with a cardinality // policy that refuses payload, raw metadata and any actor/tenant/object/stream/idempotency diff --git a/src/grpc/grpc-operation-ledger-jpa/build.gradle b/src/grpc/grpc-operation-ledger-jpa/build.gradle index 4e6409b1..abd71d2a 100644 --- a/src/grpc/grpc-operation-ledger-jpa/build.gradle +++ b/src/grpc/grpc-operation-ledger-jpa/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Durable mutation idempotency: the operation ledger entity, its state machine, the vendor-neutral // repository port and the Spring Data JPA binding, plus the migration that owns the unique diff --git a/src/grpc/grpc-policy/build.gradle b/src/grpc/grpc-policy/build.gradle index cfb7f0e6..17a4f5bf 100644 --- a/src/grpc/grpc-policy/build.gradle +++ b/src/grpc/grpc-policy/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.grpc-platform-module' +plugins { + id 'ca.grpc-platform-module' +} // Validation, context propagation, status/rich-error mapping, TLS/credential profiles, deadline // and cancellation, retry ownership and eligibility, idempotency and completion recovery, server @@ -9,6 +11,6 @@ dependencies { // api: interceptors, context binders and the error mapper name io.grpc types in public // signatures, so an adopter cannot compile against this module without them. - api "io.grpc:grpc-api:${grpcVersion}" - api "io.grpc:grpc-stub:${grpcVersion}" + api "io.grpc:grpc-api" + api "io.grpc:grpc-stub" } diff --git a/src/grpc/grpc-proto-contract/build.gradle b/src/grpc/grpc-proto-contract/build.gradle index 299583e7..d2c9d361 100644 --- a/src/grpc/grpc-proto-contract/build.gradle +++ b/src/grpc/grpc-proto-contract/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // Schema source of truth: the `.proto` files plus the rule engine that judges them. // diff --git a/src/grpc/grpc-server/build.gradle b/src/grpc/grpc-server/build.gradle index 2dd916fd..26c81421 100644 --- a/src/grpc/grpc-server/build.gradle +++ b/src/grpc/grpc-server/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.grpc-platform-module' +plugins { + id 'ca.grpc-platform-module' +} // Server boundary: the ArchUnit-shaped application boundary rules, the typed service adapter SPI, // the interceptor order contract, and the Netty server/executor/admission profiles. @@ -10,6 +12,6 @@ dependencies { api project(':grpc:grpc-core-api') api project(':grpc:grpc-policy') - api "io.grpc:grpc-api:${grpcVersion}" - api "io.grpc:grpc-stub:${grpcVersion}" + api "io.grpc:grpc-api" + api "io.grpc:grpc-stub" } diff --git a/src/grpc/grpc-spring-boot-starter/build.gradle b/src/grpc/grpc-spring-boot-starter/build.gradle index 77534d78..ca555563 100644 --- a/src/grpc/grpc-spring-boot-starter/build.gradle +++ b/src/grpc/grpc-spring-boot-starter/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'ca.platform-module' -apply plugin: 'ca.spring-config' +plugins { + id 'ca.platform-module' + id 'ca.spring-config' +} // The platform's composition boundary: typed properties, auto-configuration and the startup // validator that refuses a deployment whose configuration contradicts a Stable invariant. diff --git a/src/grpc/grpc-testkit/build.gradle b/src/grpc/grpc-testkit/build.gradle index f9a8ec8d..44c0f3c8 100644 --- a/src/grpc/grpc-testkit/build.gradle +++ b/src/grpc/grpc-testkit/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.grpc-platform-module' +plugins { + id 'ca.grpc-platform-module' +} // Certification. The Stable plan splits this across four modules (core / in-process / netty / // fault); this repository already expresses "these two runs are not the same kind of evidence" with @@ -50,11 +52,11 @@ dependencies { api project(':grpc:grpc-codegen') api project(':grpc:grpc-operation-ledger-jpa') - api "io.grpc:grpc-api:${grpcVersion}" - api "io.grpc:grpc-stub:${grpcVersion}" + api "io.grpc:grpc-api" + api "io.grpc:grpc-stub" // The transports the lanes stand on. In-process is fast contract evidence; Netty is the only // transport this platform certifies, and the lane separation is what keeps them distinguishable. - implementation "io.grpc:grpc-inprocess:${grpcVersion}" - implementation "io.grpc:grpc-netty-shaded:${grpcVersion}" + implementation "io.grpc:grpc-inprocess" + implementation "io.grpc:grpc-netty-shaded" } diff --git a/src/messaging/CLAUDE.md b/src/messaging/CLAUDE.md index c7036b2a..e99d76b1 100644 --- a/src/messaging/CLAUDE.md +++ b/src/messaging/CLAUDE.md @@ -43,29 +43,16 @@ leaf의 정확한 id는 registry에서 읽는다. family는 **디렉터리 이 등장하고 adopter가 이름 부를 일이 없다. - 새 public 타입은 그 모듈의 계약이다. 삭제·시그니처 변경은 breaking change로 취급한다. -## Runtime membership이 비어 있다는 것의 의미 +## Application runtime에 포함되는 기준 -**이 절은 한동안 사실이 아닌 채로 남아 있었다.** "registry의 모든 messaging leaf는 -`runtime_memberships`가 비어 있고 따라서 build-only"라고 쓰여 있었는데, 다섯 어댑터 remediation이 -`messaging-spring-boot-starter`를 `app-bootstrap` 의존성으로 넣으면서 그 closure 전체가 런타임 -classpath에 올라갔다. 정확한 목록은 registry가 소유하므로 여기서 세지 않는다 — 세는 순간 다시 -drift한다. +Messaging family는 기본 application이 `messaging-spring-boot-starter`를 dependency로 가지므로 그 +transitive project graph가 runtime에 포함된다. 이 사실의 SSOT는 Gradle `runtimeClasspath`이며 registry에 +동일한 membership 목록을 복제하지 않는다. `app.messaging.enabled=false`가 기본값이고 master switch가 +activation을 결정한다. -지금 의미는 이렇다: - -- 대부분의 leaf는 `runtime_memberships`에 `app-bootstrap`을 갖는다. **배포된 아티팩트가 싣고 있고, - master switch `app.messaging.enabled`로 켜진다.** 기본값은 `false`다. -- 여전히 비어 있는 leaf(experimental transport, 일부 codec, bridge, testkit)만이 build-only / - incubating이다. -- **런타임에 있다는 것과 자격이 증명됐다는 것은 다르다.** Kafka publish 경로만 실브로커 round trip으로 - 자격 검증됐고(`MessagingLiveRoundTripQualificationTest`), Rabbit·JDBC inbox/outbox·claim check·admin - plane은 shipped, inactive, unqualified다. 이 구분은 - `MessagingMembershipQualificationTest`가 문서가 아니라 테스트로 붙들고 있다. -- 애플리케이션에 새로 배선하려면 registry의 `runtime_memberships`를 먼저 바꾸고 - `verifyRuntimeModuleMembership`을 통과시켜야 한다. 코드만 추가하는 것으로는 런타임에 들어가지 - 않는다. -- 지원 상태 문서(`docs/messaging/support-matrix.md`)의 tier는 이 사실과 별개다. tier는 "무엇이 - 증명되었는가", membership은 "무엇이 실행되는가"다. +experimental transport, 일부 codec/bridge/testkit처럼 starter graph에 들어오지 않는 leaf는 build-only다. +새 leaf를 application에 채택할 때는 starter 또는 composition dependency와 architecture edge policy, +qualification/support 상태를 함께 변경한다. ## application이 이 family에 도달하는 경로 (MSG-015, 미해결) diff --git a/src/messaging/messaging-admin-api/build.gradle b/src/messaging/messaging-admin-api/build.gradle index 082a0833..55c8be51 100644 --- a/src/messaging/messaging-admin-api/build.gradle +++ b/src/messaging/messaging-admin-api/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-admin-runtime/build.gradle b/src/messaging/messaging-admin-runtime/build.gradle index bb00b83f..feb5781b 100644 --- a/src/messaging/messaging-admin-runtime/build.gradle +++ b/src/messaging/messaging-admin-runtime/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-claim-check/build.gradle b/src/messaging/messaging-claim-check/build.gradle index ee6baf3c..a145cc45 100644 --- a/src/messaging/messaging-claim-check/build.gradle +++ b/src/messaging/messaging-claim-check/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-cloudevents/build.gradle b/src/messaging/messaging-cloudevents/build.gradle index 26d72f7e..b65cf72d 100644 --- a/src/messaging/messaging-cloudevents/build.gradle +++ b/src/messaging/messaging-cloudevents/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-core-api/build.gradle b/src/messaging/messaging-core-api/build.gradle index ff4ac8c0..d202f8bf 100644 --- a/src/messaging/messaging-core-api/build.gradle +++ b/src/messaging/messaging-core-api/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { } diff --git a/src/messaging/messaging-inbox-jdbc-postgresql/build.gradle b/src/messaging/messaging-inbox-jdbc-postgresql/build.gradle index f14fb9b7..1cd44c7c 100644 --- a/src/messaging/messaging-inbox-jdbc-postgresql/build.gradle +++ b/src/messaging/messaging-inbox-jdbc-postgresql/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-kafka-share-experimental/build.gradle b/src/messaging/messaging-kafka-share-experimental/build.gradle index 40bd1ba8..fd42b56b 100644 --- a/src/messaging/messaging-kafka-share-experimental/build.gradle +++ b/src/messaging/messaging-kafka-share-experimental/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-kafka/build.gradle b/src/messaging/messaging-kafka/build.gradle index 849755a3..355925d6 100644 --- a/src/messaging/messaging-kafka/build.gradle +++ b/src/messaging/messaging-kafka/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.jmh-benchmarks' +plugins { + id 'ca.jmh-benchmarks' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-nats-experimental/build.gradle b/src/messaging/messaging-nats-experimental/build.gradle index aee4eb34..d635cefa 100644 --- a/src/messaging/messaging-nats-experimental/build.gradle +++ b/src/messaging/messaging-nats-experimental/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-observability/build.gradle b/src/messaging/messaging-observability/build.gradle index 35931a0c..b255ee77 100644 --- a/src/messaging/messaging-observability/build.gradle +++ b/src/messaging/messaging-observability/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-outbox-jdbc-postgresql/build.gradle b/src/messaging/messaging-outbox-jdbc-postgresql/build.gradle index 57464586..8aabecdb 100644 --- a/src/messaging/messaging-outbox-jdbc-postgresql/build.gradle +++ b/src/messaging/messaging-outbox-jdbc-postgresql/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-policy/build.gradle b/src/messaging/messaging-policy/build.gradle index 04f7b4f4..f2ca78e0 100644 --- a/src/messaging/messaging-policy/build.gradle +++ b/src/messaging/messaging-policy/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-pulsar-experimental/build.gradle b/src/messaging/messaging-pulsar-experimental/build.gradle index f07f5dc3..c98d34a5 100644 --- a/src/messaging/messaging-pulsar-experimental/build.gradle +++ b/src/messaging/messaging-pulsar-experimental/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-rabbit/build.gradle b/src/messaging/messaging-rabbit/build.gradle index dd68727f..b45f5fbd 100644 --- a/src/messaging/messaging-rabbit/build.gradle +++ b/src/messaging/messaging-rabbit/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.jmh-benchmarks' +plugins { + id 'ca.jmh-benchmarks' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-reliability-api/build.gradle b/src/messaging/messaging-reliability-api/build.gradle index 13f62eba..fac7b208 100644 --- a/src/messaging/messaging-reliability-api/build.gradle +++ b/src/messaging/messaging-reliability-api/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-runtime-core/build.gradle b/src/messaging/messaging-runtime-core/build.gradle index e1baddfe..5fb68290 100644 --- a/src/messaging/messaging-runtime-core/build.gradle +++ b/src/messaging/messaging-runtime-core/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} // The central publish and delivery orchestration. // diff --git a/src/messaging/messaging-schema-api/build.gradle b/src/messaging/messaging-schema-api/build.gradle index 13f62eba..fac7b208 100644 --- a/src/messaging/messaging-schema-api/build.gradle +++ b/src/messaging/messaging-schema-api/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-schema-avro/build.gradle b/src/messaging/messaging-schema-avro/build.gradle index 9fd42ad9..4044538f 100644 --- a/src/messaging/messaging-schema-avro/build.gradle +++ b/src/messaging/messaging-schema-avro/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-schema-json/build.gradle b/src/messaging/messaging-schema-json/build.gradle index f7b2d4d8..aebe0fe5 100644 --- a/src/messaging/messaging-schema-json/build.gradle +++ b/src/messaging/messaging-schema-json/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-schema-protobuf/build.gradle b/src/messaging/messaging-schema-protobuf/build.gradle index 588b9447..054e0943 100644 --- a/src/messaging/messaging-schema-protobuf/build.gradle +++ b/src/messaging/messaging-schema-protobuf/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-security/build.gradle b/src/messaging/messaging-security/build.gradle index 13f62eba..fac7b208 100644 --- a/src/messaging/messaging-security/build.gradle +++ b/src/messaging/messaging-security/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-spring-boot-starter/build.gradle b/src/messaging/messaging-spring-boot-starter/build.gradle index 0174f1d0..448d06a4 100644 --- a/src/messaging/messaging-spring-boot-starter/build.gradle +++ b/src/messaging/messaging-spring-boot-starter/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'ca.platform-module' -apply plugin: 'ca.spring-config' +plugins { + id 'ca.platform-module' + id 'ca.spring-config' +} // Scopes, not a flat list of `api`. // diff --git a/src/messaging/messaging-spring-cloud-stream-bridge/build.gradle b/src/messaging/messaging-spring-cloud-stream-bridge/build.gradle index 87d33c70..e9bd8ef0 100644 --- a/src/messaging/messaging-spring-cloud-stream-bridge/build.gradle +++ b/src/messaging/messaging-spring-cloud-stream-bridge/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-testkit/build.gradle b/src/messaging/messaging-testkit/build.gradle index c8fca955..559464fa 100644 --- a/src/messaging/messaging-testkit/build.gradle +++ b/src/messaging/messaging-testkit/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.jmh-benchmarks' +plugins { + id 'ca.jmh-benchmarks' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/messaging/messaging-transport-spi/build.gradle b/src/messaging/messaging-transport-spi/build.gradle index b2e7001d..81e3e70b 100644 --- a/src/messaging/messaging-transport-spi/build.gradle +++ b/src/messaging/messaging-transport-spi/build.gradle @@ -1,4 +1,6 @@ -apply plugin: 'ca.platform-module' +plugins { + id 'ca.platform-module' +} dependencies { api project(':messaging:messaging-core-api') diff --git a/src/optional-platforms/README.md b/src/optional-platforms/README.md new file mode 100644 index 00000000..cbd11f33 --- /dev/null +++ b/src/optional-platforms/README.md @@ -0,0 +1,13 @@ +# Optional gRPC platforms + +The default application does not depend on `grpc/*` or `grpc-advanced/*`, so those 18 projects are +kept out of the main multi-project build. This build preserves their existing project paths and +internal project dependencies while making their configuration opt-in. + +```bash +./gradlew -p optional-platforms ci +./gradlew -p optional-platforms :grpc:grpc-testkit:grpcInProcessContractTest +``` + +`adapter:inbound:grpc` remains in the main application build because it is an application adapter; +the reusable gRPC platform libraries are optional capability code and live here. diff --git a/src/optional-platforms/build.gradle b/src/optional-platforms/build.gradle new file mode 100644 index 00000000..4604ffbc --- /dev/null +++ b/src/optional-platforms/build.gradle @@ -0,0 +1,23 @@ +ext.sourceRevision = providers.gradleProperty('gitRevision') + .orElse(providers.environmentVariable('GIT_SHA')) + .orElse(providers.environmentVariable('GITHUB_SHA')) + .getOrElse('unknown') + +tasks.register('checkAll') { + group = 'verification' + description = 'Runs every gRPC optional-platform leaf check.' + dependsOn subprojects.findAll { it.childProjects.isEmpty() }.collect { "${it.path}:check" } +} + +tasks.register('qualityCheck') { + group = 'verification' + description = 'Runs production-bytecode static analysis for every optional-platform leaf.' + dependsOn subprojects.findAll { it.childProjects.isEmpty() }.collect { "${it.path}:qualityCheck" } +} + +tasks.register('ci') { + group = 'verification' + description = 'Runs the optional gRPC platform verification surface.' + dependsOn tasks.named('checkAll') + dependsOn tasks.named('qualityCheck') +} diff --git a/src/optional-platforms/settings.gradle b/src/optional-platforms/settings.gradle new file mode 100644 index 00000000..f6401e34 --- /dev/null +++ b/src/optional-platforms/settings.gradle @@ -0,0 +1,59 @@ +pluginManagement { + includeBuild('../build-logic') +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + } + versionCatalogs { + libs { + from(files('../gradle/libs.versions.toml')) + } + } +} + +rootProject.name = 'optional-platforms' + +include ':grpc' +project(':grpc').projectDir = file('../grpc') +include ':grpc-advanced' +project(':grpc-advanced').projectDir = file('../grpc-advanced') + +include ':grpc:grpc-admin' +project(':grpc:grpc-admin').projectDir = file('../grpc/grpc-admin') +include ':grpc:grpc-client' +project(':grpc:grpc-client').projectDir = file('../grpc/grpc-client') +include ':grpc:grpc-codegen' +project(':grpc:grpc-codegen').projectDir = file('../grpc/grpc-codegen') +include ':grpc:grpc-core-api' +project(':grpc:grpc-core-api').projectDir = file('../grpc/grpc-core-api') +include ':grpc:grpc-discovery' +project(':grpc:grpc-discovery').projectDir = file('../grpc/grpc-discovery') +include ':grpc:grpc-observability' +project(':grpc:grpc-observability').projectDir = file('../grpc/grpc-observability') +include ':grpc:grpc-operation-ledger-jpa' +project(':grpc:grpc-operation-ledger-jpa').projectDir = file('../grpc/grpc-operation-ledger-jpa') +include ':grpc:grpc-policy' +project(':grpc:grpc-policy').projectDir = file('../grpc/grpc-policy') +include ':grpc:grpc-proto-contract' +project(':grpc:grpc-proto-contract').projectDir = file('../grpc/grpc-proto-contract') +include ':grpc:grpc-server' +project(':grpc:grpc-server').projectDir = file('../grpc/grpc-server') +include ':grpc:grpc-spring-boot-starter' +project(':grpc:grpc-spring-boot-starter').projectDir = file('../grpc/grpc-spring-boot-starter') +include ':grpc:grpc-testkit' +project(':grpc:grpc-testkit').projectDir = file('../grpc/grpc-testkit') +include ':grpc-advanced:grpc-advanced-bootstrap' +project(':grpc-advanced:grpc-advanced-bootstrap').projectDir = file('../grpc-advanced/grpc-advanced-bootstrap') +include ':grpc-advanced:grpc-advanced-compat' +project(':grpc-advanced:grpc-advanced-compat').projectDir = file('../grpc-advanced/grpc-advanced-compat') +include ':grpc-advanced:grpc-advanced-diagnostics' +project(':grpc-advanced:grpc-advanced-diagnostics').projectDir = file('../grpc-advanced/grpc-advanced-diagnostics') +include ':grpc-advanced:grpc-advanced-edition' +project(':grpc-advanced:grpc-advanced-edition').projectDir = file('../grpc-advanced/grpc-advanced-edition') +include ':grpc-advanced:grpc-advanced-resilience' +project(':grpc-advanced:grpc-advanced-resilience').projectDir = file('../grpc-advanced/grpc-advanced-resilience') +include ':grpc-advanced:grpc-advanced-streaming' +project(':grpc-advanced:grpc-advanced-streaming').projectDir = file('../grpc-advanced/grpc-advanced-streaming') diff --git a/src/sample-portfolio/build.gradle b/src/sample-portfolio/build.gradle index d67fc392..f5d73367 100644 --- a/src/sample-portfolio/build.gradle +++ b/src/sample-portfolio/build.gradle @@ -1,11 +1,11 @@ +plugins { + id 'ca.spring-library' + id 'ca.spring-config' + id 'org.springframework.boot' +} + // Fixture/sample module. Production modules must not depend on this module. // Lean standalone boot: apply the Spring Boot plugin so bootJar / bootRun are available. -apply plugin: 'ca.spring-library' -apply plugin: 'ca.spring-config' - -apply plugin: 'org.springframework.boot' - - def repositoryRootForContractTests = rootProject.projectDir.parentFile def errorCodeRegistryForContractTests = new File( repositoryRootForContractTests, @@ -30,7 +30,7 @@ strictTestLanes { // runtime/test classpath. The AWS-SDK version SSOT stays ext.awsSdkVersion. dependencyManagement { imports { - mavenBom "software.amazon.awssdk:bom:${awsSdkVersion}" + mavenBom "software.amazon.awssdk:bom:${libs.versions.awsSdk.get()}" } } diff --git a/src/settings.gradle b/src/settings.gradle index 93357838..a62f952d 100644 --- a/src/settings.gradle +++ b/src/settings.gradle @@ -3,6 +3,7 @@ pluginManagement { // plugins are testable with TestKit and do not force every project to recompile when one of them // changes. includeBuild('build-logic') + includeBuild('build-qualification') } plugins { @@ -13,4 +14,11 @@ plugins { id 'ca.architecture-registry' } +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + } +} + rootProject.name = 'ca-skeleton' diff --git a/src/shared-contract/build.gradle b/src/shared-contract/build.gradle index c1871d1f..f409813a 100644 --- a/src/shared-contract/build.gradle +++ b/src/shared-contract/build.gradle @@ -1,6 +1,8 @@ -// Skeleton-wide operational contracts only. No business/domain concepts. +plugins { + id 'ca.java-library' +} -apply plugin: 'ca.java-library' +// Skeleton-wide operational contracts only. No business/domain concepts. dependencies { }