Compare commits

..
Author SHA1 Message Date
CI 843fdb4fad Bump version to 0.3.0-SNAPSHOT [skip ci] 2026-11-07 17:25:18 +00:00
james.maes 595b9c5932 Merge pull request 'build(deps): align with Gitea releases (parent 2.2.1, QQQ 4.1.0-RC.25)' (#13) from deps/align-gitea into develop
build / maven (push) Successful in 3m25s
maven / build (push) Successful in 3m25s
2026-11-07 17:21:43 +00:00
james.maesandClaude Opus 5.5 831b879678 build(deps): align with Gitea releases (parent 2.2.1, QQQ 4.1.0-RC.25)
build / maven (push) Successful in 3m1s
maven / build (push) Successful in 3m1s
pom.xml: parent -> 2.2.1
pom.xml: qqq.version: 4.1.0-RC.21 -> 4.1.0-RC.25

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 17:09:17 +00:00
CI c3ddd4bde4 Bump version to 0.2.0-SNAPSHOT [skip ci] 2026-11-06 18:14:36 +00:00
james.maes 71126ebce4 Merge pull request 'ci: pin shared workflow to v1' (#11) from ci/pin-v1-develop into develop
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Waiting to run
build / maven (push) Failing after 6m28s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 10s
QRun-IO/ci-policy: native / native-result (push) Successful in 7s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
maven / build (push) Failing after 6m29s
2026-11-06 15:35:32 +00:00
james.maes 63e42c8153 Merge pull request 'docs(release): prepare the 0.1.0 release on the 0.1 train' (#12) from release/first-release-20261106 into develop
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 8s
build / maven (push) Failing after 4m59s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 10s
QRun-IO/ci-policy: native / native-result (push) Successful in 11s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
maven / build (push) Failing after 4m59s
2026-11-06 15:32:25 +00:00
james.maesandClaude Fable 5.1 e267f4f84a docs(release): prepare the 0.1.0 release on the 0.1 train
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
build / maven (push) Failing after 9s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 8s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 10s
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 9s
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 7s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 8s
maven / build (push) Failing after 9s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
Add CHANGELOG.md for the first published release and docs/RELEASES.md
describing the shared ci-workflows versioning (develop SNAPSHOT,
release/0.1 RC.N, tag vX.Y.Z stable), registry consumption and the
maintainer path. README now states the 0.1 train (0.1.0-RC.1 published,
0.1.0 cut by tag) instead of the coordinated snapshot; SNAPSHOT-RELEASE.md
is marked historical. The POM revision stays 0.1.0-SNAPSHOT on develop
because the workflow owns version computation.

Verified locally: ./mvnw.cmd verify, 33 tests, 0 failures/errors/skips,
JaCoCo gates met.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-06 08:45:34 -06:00
james.maesandClaude Opus 5.5 94802b888b ci: pin shared workflow to v1
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
build / maven (push) Successful in 3m31s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 8s
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 8s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
maven / build (push) Successful in 3m31s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:12:59 +00:00
james.maes 0bafd255cb Merge pull request 'ci: use shared ci-workflows Maven workflow' (#9) from ci/ci-workflows into develop
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Waiting to run
build / maven (push) Failing after 8s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 9s
QRun-IO/ci-policy: native / native-result (push) Successful in 9s
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 10s
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Waiting to run
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 12s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
maven / build (push) Failing after 8s
QRun-IO/ci-policy: ci / Scoped workflow [workflow d20a578c7d818bc7ca32a2ac2c675da4e4f27385] (push) Successful in 5s
2026-11-05 23:17:48 +00:00
CI 07e3bffc54 ci: rerun shared workflow
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1b3648d9421543350067e95e54959dfb471b26d5] (pull_request) Failing after 2s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
build / maven (push) Successful in 3m9s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 1s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
maven / build (push) Successful in 3m9s
2026-11-05 16:26:25 -06:00
james.maesandClaude Opus 5.5 0509d47893 ci: use shared ci-workflows Maven workflow
build / maven (push) Failing after 1s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1b3648d9421543350067e95e54959dfb471b26d5] (pull_request) Failing after 1s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 1s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Failing after 2s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
maven / build (push) Failing after 1s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
Replace the platform-ci release callers with one caller of
KofTwentyTwo/ci-workflows maven.yaml (qqq-orb port).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:07:46 +00:00
james.maes 4f169f2a7b Merge pull request 'feat(redis): integrate qualified Redis topology work on QQQ 4.1.0-RC.21' (#8) from feature/kof22-redis-canonical into develop
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
Shared Java Maven CI with Docker / secrets (push) Successful in 21s
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (push) Successful in 17s
Shared Java Maven CI with Docker / verify (push) Successful in 3m50s
QRun-IO/ci-policy: ci / ci-result (push) Successful in 4s
qrun-release / postpublish (push) Successful in 5m36s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 5e6bfd71ee8a5674882f7680c695a0ada599622f] (push) Successful in 7s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 7s
qrun-release / release (push) Successful in 4m7s
QRun-IO/ci-policy: ci / java-docker-4 (push) Successful in 3m26s
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
Shared Java Maven CI with Docker / gate (push) Successful in 12s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (push) Successful in 3m11s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (push) Successful in 13s
Shared QRun source version / version (push) Successful in 5m36s
2026-11-05 17:02:37 +00:00
james.maesandClaude Sonnet 5.5 cc9c3d280c build: depend on the published QQQ 4.1.0-RC.21 core
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
Shared Java Maven CI with Docker / secrets (push) Successful in 15s
QRun-IO/ci-policy: ci / Scoped workflow [workflow 5e6bfd71ee8a5674882f7680c695a0ada599622f] (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
Shared Java Maven CI with Docker / verify (push) Canceled after 0s
qrun-release / postpublish (push) Canceled after 0s
Release candidate / release-rc (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
qrun-release / release (push) Canceled after 17m43s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (pull_request) Successful in 13s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (pull_request) Successful in 19s
Shared Java Maven CI with Docker / gate (push) Canceled after 0s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (pull_request) Successful in 2m34s
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Successful in 22m56s
Replace the pending 4.1.0-SNAPSHOT property with the published qqq-bom-pom 4.1.0-RC.21
(Maven Central) as the one BOM version. Redis needs no core API newer than that release:
the main (16) and test (14) sources compile with zero errors under javac against the
published RC.21 jars plus Jedis 7.2.0, so no feature is dropped and nothing is guessed.
The QQQ BOM and the parent's JUnit BOM are on the same 6.0 line. README wording updated;
the historical snapshot-release record is unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 10:29:49 -06:00
James MaesandClaude Sonnet 5.5 7152f0a6a2 feat(redis): integrate qualified Redis topology work into develop with fixture tests
Merges feature/redis-integration-20261029 (endpoint switching, write health,
pool recovery, mTLS/promotion/partition qualification, feature-snapshot CI
tag filter) into current develop b1fd263 and adds the endpoint fixture and
test adjustments used by the host acceptance runs (FixtureEndpoint(+Test),
RedisPromotionTest, RedisAuthenticationTest, RedisPartitionHealingTest).

Conflict choices: docs/SNAPSHOT-RELEASE.md keeps develop's current text;
the core dependency stays on the canonical 4.1.0-SNAPSHOT line (the
feature-snapshot pin is not carried) with a marker to repin to the
published core version after the core integration PR is released.
No PRIVATE or feature-snapshot coordinate is introduced.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 04:55:42 -06:00
forge-recovery b1fd26385f ci: enroll shared Gitea lifecycle
Shared Java Maven CI with Docker / verify (push) Successful in 2m55s
qrun-release / postpublish (push) Successful in 29m24s
QRun-IO/ci-policy: ci / ci-result (push) Successful in 5s
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
Shared Java Maven CI with Docker / secrets (push) Successful in 13s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (push) Successful in 13s
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Successful in 41m7s
QRun-IO/ci-policy: native / native-result (push) Successful in 4s
qrun-release / release (push) Successful in 41m31s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (push) Successful in 2m10s
Shared QRun source version / version (push) Successful in 29m24s
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (push) Successful in 13s
Shared Java Maven CI with Docker / gate (push) Successful in 13s
2026-11-02 22:23:05 +00:00
forge-recovery 318bcad3ad ci: enroll shared Gitea lifecycle
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (push) Successful in 28s
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 10s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 11s
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Successful in 17m2s
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (push) Successful in 2m25s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (push) Successful in 22s
2026-11-02 21:34:01 +00:00
forge-recoveryandCodex 78f0ea1af6 ci: enroll shared Gitea release-candidate lifecycle
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
Release candidate / release-rc (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (pull_request) Successful in 14s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (pull_request) Successful in 11s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (pull_request) Successful in 2m3s
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Successful in 1h3m47s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-02 20:19:14 +00:00
9657131703 ci: add the isolated postpublish job to the QRun release caller
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
Shared Java Maven CI with Docker / secrets (push) Successful in 11s
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
Shared Java Maven CI with Docker / verify (push) Canceled after 0s
qrun-release / postpublish (push) Canceled after 0s
Release candidate / release-rc (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 5s
qrun-release / release (push) Canceled after 2h30m57s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (pull_request) Successful in 12s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (pull_request) Successful in 12s
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
Shared Java Maven CI with Docker / gate (push) Canceled after 0s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (pull_request) Successful in 1m42s
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Successful in 3h3m20s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
2026-11-02 15:52:18 +00:00
036af0698f ci: move the QRun release caller to the thin v2 contract
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 4s
Shared Java Maven CI with Docker / secrets (push) Successful in 19s
Shared Java Maven CI with Docker / verify (push) Canceled after 0s
QRun-IO/ci-policy: ci / java-frontend-browser-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 7s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / java-docker-2 (pull_request) Canceled after 28m30s
qrun-release / release (push) Canceled after 28m37s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-7 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (pull_request) Successful in 21s
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (pull_request) Canceled after 0s
Shared Java Maven CI with Docker / gate (push) Canceled after 0s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
2026-11-02 06:20:22 +00:00
forge-recoveryandCodex 76d2d6f3f8 ci: add the identical QRun release caller
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-7 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / gate (pull_request) Failing after 9s
QRun-IO/ci-policy: ci / java-docker-2 (pull_request) Failing after 53m22s
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 4s
QRun-IO/ci-policy: ci / java-frontend-browser-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / verify (pull_request) Successful in 1m49s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-8 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI with Docker / secrets (pull_request) Failing after 1s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-02 04:14:08 +00:00
james.maesandCodex d4f5fd7aef docs: update shared snapshot publication handoff (#2)
Co-authored-by: Codex <noreply@openai.com>
2026-10-22 04:51:48 -05:00
james.maesandCodex 33b21f9c0a ci: adopt managed orb and shared maintenance callers (#1)
Co-authored-by: Codex <noreply@openai.com>
2026-10-22 04:22:59 -05:00
13 changed files with 582 additions and 40 deletions

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
name: build
on:
push:
jobs:
maven:
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
secrets: inherit
+23
View File
@@ -0,0 +1,23 @@
name: CodeQL
'on':
push:
branches:
- main
- develop
- release/**
- hotfix/**
pull_request: {}
schedule:
- cron: 41 16 * * 0
workflow_dispatch: {}
permissions:
contents: read
actions: read
security-events: write
packages: read
concurrency:
group: codeql-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
analyze:
uses: QRun-IO/qqq-orb/.github/workflows/qbit-codeql.yml@c288a8864fe9054f68410e1d78f5863aea4c4d22
+74
View File
@@ -0,0 +1,74 @@
name: Release candidate
'on':
workflow_dispatch:
inputs:
dry_run:
description: Plan a cut without changing release state
type: boolean
default: false
finalize:
description: Retry finalization of an existing cut; do not cut another RC
type: boolean
default: false
recover:
description: Recover the same published RC after a deployment timeout; never
reupload
type: boolean
default: false
circle_build:
description: Original Circle publisher build number (recovery only)
type: string
default: ''
merge_sha:
description: Existing cut merge_sha (finalize retry only)
type: string
default: ''
bump_sha:
description: Existing cut bump_sha (finalize retry only)
type: string
default: ''
branch:
description: Existing cut branch (finalize retry only)
type: string
default: ''
version:
description: Existing cut version (finalize retry only)
type: string
default: ''
pull_request:
types:
- closed
branches:
- develop
repository_dispatch:
types:
- qbit-rc-published
permissions:
contents: write
actions: read
checks: read
jobs:
release-rc:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch'
|| (github.event_name == 'pull_request' && github.event.pull_request.merged
== true && github.event.pull_request.base.ref == 'develop' && contains(github.event.pull_request.labels.*.name,
'release:rc'))
uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44
with:
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false
}}
finalize: ${{ github.event_name == 'repository_dispatch' || (github.event_name
== 'workflow_dispatch' && inputs.finalize) }}
recover: ${{ github.event_name == 'workflow_dispatch' && inputs.recover || false
}}
circle_build: ${{ github.event_name == 'workflow_dispatch' && inputs.circle_build
|| '' }}
merge_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.merge_sha
|| inputs.merge_sha || '' }}
bump_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.bump_sha
|| inputs.bump_sha || '' }}
branch: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch
|| inputs.branch || '' }}
version: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.version
|| inputs.version || '' }}
secrets: inherit
+33
View File
@@ -0,0 +1,33 @@
# Changelog
## 0.1.0
First published release of `com.kingsrook.qbits:qbit-redis`, built on the
published QQQ `4.1.0-RC.21` core and Jedis `7.2.0` (Java 21). The release
candidate `0.1.0-RC.1` was published from `release/0.1` (source `6b0814d`, the
`develop` head `0bafd25` plus the CI version bump); the stable `0.1.0` is cut by
tagging `v0.1.0` on that train. See [docs/RELEASES.md](docs/RELEASES.md).
- Named, bounded Redis connections with required TLS (hostname verification on),
host-provided credentials and TLS providers resolved at runtime, startup
validation/ping of every pool, failed-startup cleanup and QInstance isolation.
- Shared value cache (`RedisAccess.cache`): collision-safe namespaces, explicit
size limits, positive TTL on every write, atomic SET with expiry and atomic
GETEX read-and-renew; outages throw rather than reading as misses.
- Trusted domain Lua through `RedisAccess.scripts` with EVALSHA/NOSCRIPT recovery.
- Redis Pub/Sub realtime transport (candidate) with bounded hint queue, resync
on overflow/reconnect, per-node heartbeat and transport metrics.
- Framework health check `redis.connections` with aggregate pool metrics; an
opt-in per-connection write probe (`withWriteHealthKeyPrefix`, default off,
`SET NX PX 1000` under a reserved prefix) reports `writeChecks`.
- Admin app and widgets with native help bundle; no host SQL schema.
- Qualification fixtures (not runtime behavior): controlled replica promotion,
stable endpoint switch, primary fencing and partition healing on real Redis
containers ([docs/TOPOLOGY-QUALIFICATION.md](docs/TOPOLOGY-QUALIFICATION.md)).
Compared with the isolated development build `0.1.0-forge.54adcfed62b0` that
GG Forge qualified, the published artifact compiles against the same QQQ
`4.1.0-RC.21` (now the POM default rather than a command-line override), adds
the optional write-health prefix and `writeChecks` detail, rewords the health
check status messages, and adds test fixtures and CI. No API was removed and
no default behavior changed.
+9 -6
View File
@@ -1,10 +1,13 @@
# QBit Redis
Reusable Redis connections and bounded shared caches for QQQ applications.
**Coordinated feature snapshot published; not a stable/GA release.** Track
[QQQ #1295](https://github.com/QRun-IO/qqq/issues/1295) and [completed milestone and follow-ups](docs/TODO.md).
See [published snapshot](docs/SNAPSHOT-RELEASE.md) for current normal-flow evidence,
exact compatible versions and the explicitly deferred optional scenarios.
Release train `0.1`: candidate `0.1.0-RC.1` is published to the Gitea Maven
registry (`com.kingsrook.qbits:qbit-redis`, QQQ `4.1.0-RC.21`, Java 21); the
stable `0.1.0` is cut from the same train. See [CHANGELOG.md](CHANGELOG.md) and
[release operations](docs/RELEASES.md) for consumption, versioning and the
maintainer path, and [follow-ups](docs/TODO.md) for the optional scenarios that
no release declares supported. [SNAPSHOT-RELEASE.md](docs/SNAPSHOT-RELEASE.md)
is the historical coordinated-snapshot record.
## Host integration
@@ -96,7 +99,7 @@ or proof that every application command/key is authorized. A writable stale
primary can pass; topology fencing must remain an infrastructure guarantee.
The host decides whether this check gates readiness. Do not enable the probe on
an existing ACL until its dedicated prefix and write permission are configured.
This option is currently an unpublished candidate.
This option is off by default and first ships in 0.1.0.
Other qbits consume only `com.kingsrook.qbits.redis.api`: `RedisAccess`,
`RedisNamespace`, `RedisCache`, `RedisScript`, `RedisScriptExecutor`,
@@ -168,7 +171,7 @@ Java 21, Maven wrapper and Docker are required for the full current tests:
.\mvnw.cmd test
```
The current candidate uses local QQQ `4.1.0-SNAPSHOT` and Jedis `7.2.0`. Tests use
The 0.1 release train uses the published QQQ `4.1.0-RC.21` and Jedis `7.2.0`. Tests use
real disposable Redis containers. They cover sharing, expiry, isolation, outages,
registration, failed startup cleanup, credential-error redaction, lifecycle,
authentication, bounded pool exhaustion and TLS. TLS tests generate temporary
+69
View File
@@ -0,0 +1,69 @@
# Release operations
The current release train is `0.1`. Its candidate `0.1.0-RC.1` is published to
the Gitea Maven registry from `release/0.1` (tag `v0.1.0-RC.1`, source
`6b0814d83e18f70e3ea315a99e5aece1a562a213`). The stable `0.1.0` is cut by
tagging `v0.1.0` on that train as described below. Changes are listed in
[CHANGELOG.md](../CHANGELOG.md). Gitea (`git.kof22.com/QRun-IO/qbit-redis`) is
primary for source, pull requests, CI, packages and releases; GitHub is a
shadow/archive and its workflows are historical.
## Use the Maven registry
Configure the consuming application's Maven repository setup with repository ID
`qrun-io` and URL `https://git.kof22.com/api/packages/QRun-IO/maven` (releases
enabled). The registry requires authentication for reads as well as writes:
match the ID to a `<server>` entry in your private `settings.xml` that references
your managed username/token through environment variables or encrypted Maven
credentials. Never commit a token or put it on a command line. Then depend on
`com.kingsrook.qbits:qbit-redis:<version>`; QQQ `4.1.0-RC.21` and Jedis resolve
from Maven Central. Verify the downloaded JAR/POM against the hashes recorded
for the release before adopting it.
## Versioning
The POM version is the `revision` property. On `develop` it stays
`0.1.0-SNAPSHOT` and is never hand-set: the shared
`KofTwentyTwo/ci-workflows` Maven workflow (called by
`.gitea/workflows/build.yaml` on every push) computes and commits versions.
| Ref | Build | Published version |
| --- | --- | --- |
| feature branches, pull requests, `main` | `mvn verify` only | none |
| `develop` | `mvn deploy` | current `X.Y.0-SNAPSHOT` (bumped to the next minor after a release merges back) |
| `release/X.Y` | `mvn deploy`, bump commit, tag `vX.Y.0-RC.N` | `X.Y.0-RC.N` |
| tag `vX.Y.Z` | `mvn deploy`, bump commit pushed to `main` | `X.Y.Z` |
| `hotfix/*` | `mvn deploy`, bump commit, tag | `X.Y.(Z+1)` |
The workflow runs the tests once inside `mvn deploy` on the `container-build`
runner, which provides the Docker daemon the Redis Testcontainers suites need.
## Maintainer release path
1. Merge reviewed changes into `develop` through a normal pull request; the
`develop` run publishes the SNAPSHOT.
2. Merge `develop` into `release/0.1` (pull request or fast-forward). The push
publishes the next `0.1.0-RC.N`, commits `Bump version to 0.1.0-RC.N [skip ci]`
and tags `v0.1.0-RC.N` on that bump commit. Consumers qualify the RC.
3. Bring `main` to the release-train head before tagging: the tag run pushes its
`Bump version to 0.1.0` commit with `git push origin HEAD:main`, which must be
a fast-forward. `main` currently carries CI-only commits with different SHAs
from the equivalent `develop` commits, so it must be reset or merged to the
`release/0.1` head first.
4. Tag the qualified source: `git tag -s v0.1.0 <release/0.1 head>` and push the
tag to origin only. The tag run sets `revision` to `0.1.0`, deploys
`com.kingsrook.qbits:qbit-redis:0.1.0` to the registry and pushes the bump
commit to `main`. A passing run alone does not prove publication: resolve the
exact version's JAR/POM from the registry with an authenticated client and
record their SHA-256 values here.
5. Create a Gitea release for `v0.1.0` (the shared workflow does not), attach the
same `qbit-redis-0.1.0.jar`, `qbit-redis-0.1.0.pom` and a flat `SHA256SUMS`,
read them back with the approved forge identity, then publish. Never replace
different bytes under a released version.
6. Merge `release/0.1` back into `develop`; the next `develop` run moves it to
`0.2.0-SNAPSHOT`.
Publication is separate from host adoption. The optional scenarios in
[TODO.md](TODO.md) (QQQ #1300/#1301/#1302) are not declared supported by a
release; [TOPOLOGY-QUALIFICATION.md](TOPOLOGY-QUALIFICATION.md) records what the
fixtures demonstrate.
+22 -23
View File
@@ -1,35 +1,34 @@
# Coordinated published snapshot
# Coordinated candidate snapshot
The main-line milestone is complete. These feature snapshots are published and verified together; this is not a stable/GA release or a production deployment.
Historical record. The 0.1 release train (`0.1.0-RC.1` published from `release/0.1`, stable `0.1.0` cut by tag) supersedes the CircleCI snapshot path below; see [RELEASES.md](RELEASES.md).
| Component | Version |
| --- | --- |
| QQQ BOM/modules | `4.1.0-collab-33464d37e-SNAPSHOT` |
| qbit-redis | `0.1.0-redis-7fe5492-SNAPSHOT` |
| qbit-collaboration | `0.1.0-collab-8f348da-SNAPSHOT` |
| qbit-session-store | `0.1.0-session-b5276ab-SNAPSHOT` |
| Next frontend source | `8d372142f8af0c41a75a353edc6d150c33ccf613` |
Status, 2026-10-22: source is uploaded, canonical branch CI is verified and enforced, and the required Core snapshot is published. Publication of Redis `0.1.0-SNAPSHOT` through the canonical `develop` pipeline remains to be verified. This is not a production deployment checklist or an RC certification.
Use the public Maven snapshot repository https://central.sonatype.com/repository/maven-snapshots/ and import the candidate QQQ BOM. All four publishers succeeded. Independent JAR/POM downloads and a standalone consumer using an initially empty Maven cache passed; all five QQQ modules align and only Jedis7.2.0 is resolved.
## Local evidence
## Verification
- Collaboration default verify:179tests, no failures/errors/skips, coverage and guardrails passed.
- UI focused34 plus Chromium normal-flow suites16 passed; TypeScript passed.
- Live browser with mocks disabled: two independent QQQ JVMs share presence; editing focus, drafts, table projection and departure verified.
- Redis admin shows live pool/realtime metrics and real framework-persisted health results, with navigation to the standard health check.
- Standalone Maven consumer built and ran packaged collaboration/Redis candidates without source paths or a reactor; Redis-absent consumer uses the new issued-lease protocol successfully.
Redis23, collaboration177 and session-store96 tests passed against published prerequisites, with zero failures/errors/skips and configured coverage gates passing. Core verification4209 and frontend acceptance39683690943 passed. Frontend local3664 tests and TypeScript passed. The frontend acceptance run contains the tested export; no dashboard container image was published by that run.
## Publication order and remaining checks
Final browser checks used the downloaded published JARs, exact CI frontend export, two independent JVMs and one Redis endpoint. Cross-node avatars, self exclusion, editor focus, preserved drafts, table presence and departure passed. Redis admin metrics, subscription state, persisted framework UP health history and health-record navigation passed. The fixtures use mock users and independent memory records; database concurrency and authentication have separate core/frontend tests. Test-owned processes were stopped afterward.
1. Capture reviewed source revisions for the coordinated core, Redis, collaboration, session-store and frontend candidates. Preserve unrelated worktrees and edits.
2. Publish the corresponding QQQ4.1.0-SNAPSHOT parent/BOM and required core modules first. The consumer graph currently includes backend-core, backend-module-rdbms, middleware-javalin and openapi.
3. The public [Redis repository](https://github.com/QRun-IO/qbit-redis) has canonical `main` and `develop` branches; `develop` is the default. [PR #1](https://github.com/QRun-IO/qbit-redis/pull/1) installed the shared orb declarations at `33b21f9c0a1ea316f7f720bbde2465ac11d81af0`. CircleCI pipelines 10 and 11 passed on that revision. Both branches enforce the `setup` and `verify` checks from CircleCI app 18001, including for administrators.
4. Publish `0.1.0-SNAPSHOT` through the shared QBit profile declared in `.qqq-ci.yml`. The canonical caller tracks `kingsrook/qqq-orb@0.8`; the orb owns verification, version calculation, signing and deployment. Normal `develop` changes select snapshot publication. CI-only declaration changes intentionally verify without publishing, so pipelines 10 and 11 do not prove artifact publication. Follow the actual publisher to success and resolve its timestamped POM/JAR from the remote repository before marking this step complete. Do not add a repository-specific publishing workflow.
5. Run and publish collaboration0.1.0-SNAPSHOT and the coordinated session-store/frontend candidates as applicable. Frontend and collaboration lease endpoints require coordinated rollout; do not pair the new frontend with an older backend.
6. Resolve exact published artifacts in a fresh consumer cache, repeat normal host startup/presence/admin checks, and record remote coordinates, source SHAs and artifact checksums. Local cache success is insufficient for this step.
Exact release receipts and evidence: [QQQ #1293](https://github.com/QRun-IO/qqq/issues/1293#issuecomment-6329618640). Host integration and migration guide: [coordinated candidate](https://github.com/QRun-IO/qbit-collaboration/blob/c3182e2/docs/CANDIDATE-RELEASE.md).
## Verified dependency baseline
## Supported milestone
Core commit `47eb326c4a36e481a8cf36b93c2e022760d08c76` completed publisher job 9606. Its published `4.1.0-SNAPSHOT` artifacts were checked remotely: 19 resolved POMs and the backend-core/Javalin JAR APIs needed by Redis (`getIfStarted`, message audience accessors, audience-provider configuration and hub audience filtering). Redis pipeline 8 then passed against the published dependency before PR #1 merged.
Multiple QQQ web nodes share one configured Redis endpoint. Collaboration owns presence semantics; Redis owns connection/cache infrastructure; session-store retains session semantics. Browser delivery uses SSE plus HTTP lease writes, and Redis Pub/Sub carries invalidation hints. Configure node affinity for SSE subscription updates. Shared Redis does not make connection IDs portable. Presence is advisory; versioned database compare-and-write protects saves.
This baseline establishes the Core dependency and Redis CI path. It does not certify the coordinated collaboration/session-store/frontend deployment or a stable Redis release. Retain the separate consumer and host checks in steps 5 and 6.
Before host adoption, configure authoritative identity/tenant scope, unique deployment/application namespaces, TLS/authentication, bounded pools/timeouts, explicit QInstance session registration, owner-scoped shutdown, and schema migrations. Upgrade the frontend/backend lease protocol together. Existing hosts are not automatically migrated by library publication; rollout is tracked in [#1315](https://github.com/QRun-IO/qqq/issues/1315).
## Operational scope
## Optional future work
Initial path: multiple QQQ web nodes, one configured Redis endpoint, host-provided authentication/tenant scope, HTTP lease writes, SSE browser delivery and Redis Pub/Sub invalidation. Presence is advisory; database version checks protect saves.
The owner's main-line-only scope leaves attachment failure qualification #1289, capacity/My Work failure qualification #1291, selectable tag/EAV projection #1196, data loss/restore #1300, additional Redis topologies #1301, sustained scale #1302, additional proxy deployments #1316 and admin/client coupling enhancements #1317 open. Tooling/dependency maintenance remains #1304/#1305/#1307/#1309/#1310/#1312/#1314. No measured production capacity or disaster-recovery guarantee is claimed.
## Publication lessons
Use short feature branch names: the help bundle version limit is 50 characters. The first long core candidate failed before publication; unchanged source passed with the short coordinate above. Preserve original worktree branches and failed tags. Feature-enabled QBit setup must use the canonical `/publish.*/` tag filter; schema validation alone does not check that contract. Redis/session-store use orb0.7.18; collaboration uses the managed0.8 track. Pin actual published prerequisite versions before downstream publication and verify remote resolution afterward.
Optional future work remains QQQ #1300 (loss/restore), #1301 (failover topology) and #1302 (sustained scale/failure injection). No transparent disaster-recovery guarantee is part of this snapshot. If the normal presence/admin flow fails in staging, stop rollout and retain the prior coordinated frontend/backend set; do not downgrade only one side of the lease protocol.
+5 -4
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.kingsrook</groupId>
<artifactId>qbit-build-parent</artifactId>
<version>2.1.1</version>
<version>2.2.1</version>
</parent>
<groupId>com.kingsrook.qbits</groupId>
<artifactId>qbit-redis</artifactId>
@@ -35,9 +35,10 @@
<tag>HEAD</tag>
</scm>
<properties>
<revision>0.1.0-SNAPSHOT</revision>
<revision>0.3.0-SNAPSHOT</revision>
<maven.compiler.release>21</maven.compiler.release>
<qqq.snapshot.version>4.1.0-collab-33464d37e-SNAPSHOT</qqq.snapshot.version>
<!-- Published QQQ 4.1 core (Maven Central). Redis needs no API newer than this release: main and test sources compile against it. -->
<qqq.version>4.1.0-RC.25</qqq.version>
<jedis.version>7.2.0</jedis.version>
</properties>
<dependencyManagement>
@@ -45,7 +46,7 @@
<dependency>
<groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-bom-pom</artifactId>
<version>${qqq.snapshot.version}</version>
<version>${qqq.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -0,0 +1,238 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.redis;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.github.dockerjava.api.command.InspectContainerResponse;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.GenericContainer;
/*******************************************************************************
** Where a disposable test container is reachable FROM THIS JVM.
**
** The original behavior is Testcontainers' own: the host it reports and the
** published port. That is kept everywhere except one measured topology: a JVM
** inside a build container that shares the daemon's default bridge with the
** fixture (the full Linux verify, which talks to the host daemon through its
** socket). On the Docker Desktop runner the published port is reached through
** the host-gateway hop; measured on an idle machine that path showed round trips
** of about 1 ms with stalls of 0.3 s and about 2 s, against about 0.13 ms and
** no stalls when dialing the sibling directly on the bridge. These tests use
** 250 ms to 1 s socket timeouts, so such stalls can expire them; that is a
** plausible contributor, not shown to be the only cause of the observed
** "Read timed out" failures. In that one topology the fixture is dialed on its
** bridge address and its own port.
**
** The choice is made from the daemon's own description of the build container's
** networks (no connection probing). If the build container cannot be identified
** or is not on the default bridge (a job network, host networking, a remote
** daemon), the original published endpoint is used unchanged. Timeouts,
** scenarios, assertions and server configuration are unchanged.
*******************************************************************************/
final class FixtureEndpoint
{
private static final String DEFAULT_BRIDGE = "bridge";
private static final Pattern MOUNTINFO_CONTAINER_ID = Pattern.compile("/docker/containers/([0-9a-f]{64})/");
private static volatile Boolean buildContainerOnDefaultBridge;
/***************************************************************************
** Host and port to dial.
***************************************************************************/
record Address(String host, int port)
{
}
/***************************************************************************
** Utility class.
***************************************************************************/
private FixtureEndpoint()
{
}
/***************************************************************************
** Host this JVM must dial for the container.
***************************************************************************/
static String host(GenericContainer<?> container)
{
return (choose(onBridgeWithBuildContainer(), bridgeIp(container), 6379, container.getHost(), publishedPort(container)).host());
}
/***************************************************************************
** Port matching {@link #host(GenericContainer)}.
***************************************************************************/
static int port(GenericContainer<?> container)
{
return (choose(onBridgeWithBuildContainer(), bridgeIp(container), 6379, container.getHost(), publishedPort(container)).port());
}
/***************************************************************************
** A container that lives only on a custom test network is also attached to the
** default bridge when (and only when) the build container is on that bridge, so
** that the build container can reach it directly. The test network, and therefore
** every partition scenario between the fixture servers, is untouched: attach only
** the container the test client dials, never one whose reachability the test cuts
** or whose peers must stay isolated from it. No-op in every other topology.
***************************************************************************/
static void attachToDefaultBridgeWhenInsideContainer(GenericContainer<?> container)
{
if(!onBridgeWithBuildContainer())
{
return;
}
var docker = container.getDockerClient();
var networks = docker.inspectContainerCmd(container.getContainerId()).exec().getNetworkSettings().getNetworks();
if(networks == null || !networks.containsKey(DEFAULT_BRIDGE))
{
docker.connectToNetworkCmd().withContainerId(container.getContainerId()).withNetworkId(DEFAULT_BRIDGE).exec();
}
}
/***************************************************************************
** Whether this JVM runs in a container that the daemon reports on the default
** bridge. Decided once per JVM from the daemon's description of this container.
***************************************************************************/
private static boolean onBridgeWithBuildContainer()
{
Boolean known = buildContainerOnDefaultBridge;
if(known == null)
{
known = decideBuildContainerOnDefaultBridge();
buildContainerOnDefaultBridge = known;
}
return (known);
}
/***************************************************************************
** Inspect the build container; anything that cannot be established means the
** original published endpoint (false).
***************************************************************************/
private static boolean decideBuildContainerOnDefaultBridge()
{
if(!Files.exists(Path.of("/.dockerenv")))
{
return (false);
}
try
{
String id = ownContainerId(Files.readString(Path.of("/proc/self/mountinfo")), System.getenv("HOSTNAME"));
if(id == null)
{
return (false);
}
var networks = DockerClientFactory.instance().client().inspectContainerCmd(id).exec().getNetworkSettings().getNetworks();
return (networks != null && networks.containsKey(DEFAULT_BRIDGE));
}
catch(Exception e)
{
return (false);
}
}
/***************************************************************************
** The id of the container this JVM runs in: from the mount table when it names
** the container directory, otherwise the host name (the container id unless
** overridden); null when neither exists.
***************************************************************************/
static String ownContainerId(String mountInfo, String hostName)
{
Matcher matcher = MOUNTINFO_CONTAINER_ID.matcher(mountInfo == null ? "" : mountInfo);
if(matcher.find())
{
return (matcher.group(1));
}
return (hostName == null || hostName.isBlank() ? null : hostName.trim());
}
/***************************************************************************
** The container's address on the default bridge, or null when it has none.
***************************************************************************/
private static String bridgeIp(GenericContainer<?> container)
{
if(!onBridgeWithBuildContainer())
{
return (null);
}
InspectContainerResponse inspected = container.getDockerClient().inspectContainerCmd(container.getContainerId()).exec();
var networks = inspected.getNetworkSettings().getNetworks();
return (networks == null || networks.get(DEFAULT_BRIDGE) == null ? null : networks.get(DEFAULT_BRIDGE).getIpAddress());
}
/***************************************************************************
** Testcontainers' published port for the Redis port, or null when the build
** container dials the bridge (so the published port is not consulted).
***************************************************************************/
private static Integer publishedPort(GenericContainer<?> container)
{
return (onBridgeWithBuildContainer() ? null : container.getMappedPort(6379));
}
/***************************************************************************
** Pure decision, separately tested. With the build container on the default
** bridge: the sibling's bridge address and its own port. Otherwise the published
** endpoint exactly as Testcontainers reports it.
***************************************************************************/
static Address choose(boolean buildContainerOnDefaultBridge, String bridgeIp, int internalPort, String publishedHost, Integer publishedPort)
{
if(buildContainerOnDefaultBridge)
{
if(bridgeIp == null || bridgeIp.isBlank())
{
throw new IllegalStateException("FIXTURE_ENDPOINT_NO_BRIDGE_ADDRESS: the build container cannot reach a sibling without its bridge address");
}
return (new Address(bridgeIp, internalPort));
}
if(publishedHost == null || publishedHost.isBlank() || publishedPort == null)
{
throw new IllegalStateException("FIXTURE_ENDPOINT_NOT_PUBLISHED: no published endpoint to dial");
}
return (new Address(publishedHost, publishedPort));
}
}
@@ -0,0 +1,92 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.redis;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
** The topology decision of the sibling-container fixtures.
*******************************************************************************/
class FixtureEndpointTest
{
private static final String CONTAINER_ID = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
/***************************************************************************
** Build container on the default bridge: dial the sibling bridge IP and its own port, never the published port.
***************************************************************************/
@Test
void buildContainerOnTheBridgeUsesTheBridgeAddress()
{
assertEquals(new FixtureEndpoint.Address("172.17.0.9", 6379), FixtureEndpoint.choose(true, "172.17.0.9", 6379, "localhost", 41111));
assertEquals(new FixtureEndpoint.Address("172.17.0.9", 6379), FixtureEndpoint.choose(true, "172.17.0.9", 6379, null, null));
}
/***************************************************************************
** Every other topology (JVM on the Docker host, remote daemon, job network, host networking, unidentified
** build container) keeps the original Testcontainers host and published port exactly, whatever the host is.
***************************************************************************/
@Test
void everyOtherTopologyKeepsTheOriginalPublishedEndpoint()
{
assertEquals(new FixtureEndpoint.Address("localhost", 41111), FixtureEndpoint.choose(false, "172.17.0.9", 6379, "localhost", 41111));
assertEquals(new FixtureEndpoint.Address("docker.example.test", 32768), FixtureEndpoint.choose(false, null, 6379, "docker.example.test", 32768));
assertEquals(new FixtureEndpoint.Address("host.docker.internal", 40000), FixtureEndpoint.choose(false, null, 6379, "host.docker.internal", 40000));
}
/***************************************************************************
** An unknown endpoint is an error, never a guessed address.
***************************************************************************/
@Test
void missingEndpointFailsClosed()
{
assertThrows(IllegalStateException.class, () -> FixtureEndpoint.choose(true, null, 6379, "localhost", 41111));
assertThrows(IllegalStateException.class, () -> FixtureEndpoint.choose(true, " ", 6379, "localhost", 41111));
assertThrows(IllegalStateException.class, () -> FixtureEndpoint.choose(false, "172.17.0.9", 6379, "localhost", null));
assertThrows(IllegalStateException.class, () -> FixtureEndpoint.choose(false, "172.17.0.9", 6379, " ", 41111));
}
/***************************************************************************
** The build container id comes from the mount table when present, else the host name.
***************************************************************************/
@Test
void ownContainerIdPrefersTheMountTableThenTheHostName()
{
String mountInfo = "700 699 0:50 /docker/containers/" + CONTAINER_ID + "/hostname /etc/hostname rw,relatime - ext4 /dev/sda1 rw";
assertEquals(CONTAINER_ID, FixtureEndpoint.ownContainerId(mountInfo, "unrelated"));
assertEquals("abc123", FixtureEndpoint.ownContainerId("no container mounts here", " abc123 "));
assertNull(FixtureEndpoint.ownContainerId("", null));
assertNull(FixtureEndpoint.ownContainerId(null, " "));
}
}
@@ -54,7 +54,7 @@ class RedisAuthenticationTest
RotatingCredentials.owner = instance;
RotatingCredentials.password = TEST_PASSWORD;
new RedisQBitProducer().withQBitConfig(new RedisQBitConfig().withConnection("realtime", new RedisConnectionConfig()
.withHost(redis.getHost()).withPort(redis.getMappedPort(6379)).withTls(false)
.withHost(FixtureEndpoint.host(redis)).withPort(FixtureEndpoint.port(redis)).withTls(false)
.withCredentialsProvider(new QCodeReference(RotatingCredentials.class)))
.withRealtime("realtime", "test", "rotation")).produce(instance);
var runtime = new RedisRuntimeService();
@@ -136,7 +136,7 @@ class RedisAuthenticationTest
RotatingCredentials.owner = instance;
RotatingCredentials.password = TEST_PASSWORD;
new RedisQBitProducer().withQBitConfig(new RedisQBitConfig().withConnection("cache", new RedisConnectionConfig()
.withHost(redis.getHost()).withPort(redis.getMappedPort(6379)).withTls(false)
.withHost(FixtureEndpoint.host(redis)).withPort(FixtureEndpoint.port(redis)).withTls(false)
.withCredentialsProvider(new QCodeReference(RotatingCredentials.class)))).produce(instance);
var runtime = new RedisRuntimeService();
try
@@ -217,7 +217,7 @@ class RedisAuthenticationTest
.withCommand("redis-server", "--requirepass", TEST_PASSWORD).withExposedPorts(6379))
{
redis.start();
var settings = new RedisConnectionConfig().withHost(redis.getHost()).withPort(redis.getMappedPort(6379))
var settings = new RedisConnectionConfig().withHost(FixtureEndpoint.host(redis)).withPort(FixtureEndpoint.port(redis))
.withTls(false).withMaximumConnections(1).withMaximumWaitMillis(100).withSocketTimeoutMillis(250)
.withCredentialsProvider(new QCodeReference(ValidCredentials.class));
var instance = RedisRuntimeTest.hostInstance();
@@ -240,7 +240,7 @@ class RedisAuthenticationTest
assertEquals(0, connection.metrics().active());
long initialClients = clientCount(redis);
var bad = new RedisConnectionConfig().withHost(redis.getHost()).withPort(redis.getMappedPort(6379))
var bad = new RedisConnectionConfig().withHost(FixtureEndpoint.host(redis)).withPort(FixtureEndpoint.port(redis))
.withTls(false).withCredentialsProvider(new QCodeReference(WrongCredentials.class));
assertRejected(bad);
assertEquals(initialClients, clientCount(redis));
@@ -52,6 +52,7 @@ class RedisPartitionHealingTest
{
primary.start();
replica.start();
FixtureEndpoint.attachToDefaultBridgeWhenInsideContainer(primary);
try(var oldPool = connection(primary))
{
assertTimeoutPreemptively(Duration.ofSeconds(20), () ->
@@ -137,7 +138,7 @@ class RedisPartitionHealingTest
***************************************************************************/
private static RedisConnection connection(GenericContainer<?> endpoint)
{
return (new RedisConnection(new HostAndPort(endpoint.getHost(), endpoint.getMappedPort(6379)),
return (new RedisConnection(new HostAndPort(FixtureEndpoint.host(endpoint), FixtureEndpoint.port(endpoint)),
DefaultJedisClientConfig.builder().connectionTimeoutMillis(500).socketTimeoutMillis(1000).build(),
2, Duration.ofMillis(500)));
}
@@ -52,11 +52,13 @@ class RedisPromotionTest
{
primary.start();
replica.start();
FixtureEndpoint.attachToDefaultBridgeWhenInsideContainer(primary);
FixtureEndpoint.attachToDefaultBridgeWhenInsideContainer(replica);
var firstInstance = host(replica);
var secondInstance = host(replica);
var firstRuntime = new RedisRuntimeService();
var secondRuntime = new RedisRuntimeService();
try(var oldPrimary = new RedisConnection(new HostAndPort(primary.getHost(), primary.getMappedPort(6379)),
try(var oldPrimary = new RedisConnection(new HostAndPort(FixtureEndpoint.host(primary), FixtureEndpoint.port(primary)),
DefaultJedisClientConfig.builder().connectionTimeoutMillis(500).socketTimeoutMillis(500).build(), 2, Duration.ofMillis(500)))
{
try
@@ -156,7 +158,7 @@ class RedisPromotionTest
private static QInstance host(GenericContainer<?> endpoint) throws Exception
{
var instance = RedisRuntimeTest.hostInstance();
var settings = new RedisConnectionConfig().withHost(endpoint.getHost()).withPort(endpoint.getMappedPort(6379)).withTls(false)
var settings = new RedisConnectionConfig().withHost(FixtureEndpoint.host(endpoint)).withPort(FixtureEndpoint.port(endpoint)).withTls(false)
.withMaximumConnections(2).withConnectionTimeoutMillis(500).withSocketTimeoutMillis(1000);
new RedisQBitProducer().withQBitConfig(new RedisQBitConfig().withConnection("realtime", settings)
.withRealtime("realtime", "test", "promotion")).produce(instance);