From 4fd62948939a8baf4ccaa92d51383f35641cd9d5 Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 15 Oct 2026 16:20:06 -0500 Subject: [PATCH 01/18] ci(release-rc): pass secrets to the shared workflow so it can sign the RC tag QRun-IO/.github#2 adds a tag-and-release job that signs vX with the QRUN_TAG_SIGNING_KEY secret after Maven Central confirms the artifact. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release-rc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index dd332a0..a342ba8 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -21,5 +21,6 @@ jobs: release-rc: if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc')) uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main + secrets: inherit with: dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }} -- 2.54.0 From 97ac662c4802ad2821cab04e0e1e096c0d94e660 Mon Sep 17 00:00:00 2001 From: James Maes Date: Tue, 20 Oct 2026 19:27:01 -0500 Subject: [PATCH 02/18] ci: adopt central QQQ orb 0.7.4 Co-Authored-By: Codex --- .circleci/config.yml | 68 +++++++------------------------------------- .qqq-ci.yml | 16 +++++++++++ 2 files changed, 27 insertions(+), 57 deletions(-) create mode 100644 .qqq-ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index aec59b2..6cbcc25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,60 +1,14 @@ version: 2.1 - +setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.6.9 - -commands: - configure-package-install: - steps: - - run: - name: Keep package installation unattended - command: | - # These build machines are discarded after the job. - sudo install -d /etc/needrestart/conf.d - printf '%s\n' '$nrconf{restart} = "l";' | sudo tee /etc/needrestart/conf.d/99-qqq-ci.conf >/dev/null - + qqq-orb: kingsrook/qqq-orb@0.7.4 workflows: - test_only: - jobs: - - qqq-orb/mvn_test_only: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - filters: - branches: - ignore: /(develop|release\/.*)/ - - publish_snapshot: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - branch_type: snapshot - filters: - branches: - only: [develop] - - publish_release_candidate: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - branch_type: release_candidate - filters: - branches: - only: [/release\/.*/] - - publish_release: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - branch_type: release - filters: - branches: - ignore: /.*/ - tags: - only: [/^v\d+\.\d+\.\d+$/] + setup: + jobs: + - qqq-orb/generate_pipeline: + orb_version: 0.7.4 + filters: + branches: + ignore: automation/qbit-rc-pending + tags: + only: /^v\d+\.\d+\.\d+$/ diff --git a/.qqq-ci.yml b/.qqq-ci.yml new file mode 100644 index 0000000..2d60cf7 --- /dev/null +++ b/.qqq-ci.yml @@ -0,0 +1,16 @@ +profile: qbit +orb_version: 0.7.4 +default_branch: develop +java_version: '21' +browsers: true +api_checks: false +docs_only: false +release_on_main: false +release_tag_policy: stable +test_branch_policy: non-release +publish: + snapshot: true + release_candidate: true + release: true + asciidoc: false +release_completion: false -- 2.54.0 From 07ffe9d0a2fe689c607e86a0c920d489c8e0c586 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 00:58:57 -0500 Subject: [PATCH 03/18] ci: adopt shared orb 0.7.11 (#7) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6cbcc25..def7f95 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.4 + qqq-orb: kingsrook/qqq-orb@0.7.11 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.4 + orb_version: 0.7.11 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 2d60cf7..7b032d6 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.4 +orb_version: 0.7.11 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 5c0cbbc17c5c11c2ffb008a81aa5ecbc80ec57f0 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 02:24:02 -0500 Subject: [PATCH 04/18] ci: centralize rc release completion in qqq orb (#8) Co-authored-by: Codex --- .github/workflows/release-rc.yml | 76 ++++++++++++++++++++++++++------ .qqq-ci.yml | 2 +- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index a342ba8..0158aa2 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -1,26 +1,74 @@ -# Cut the next release candidate (QRun-IO/qqq docs/QRUN-QBIT-REPO-STANDARD.md, C6): -# run this workflow by hand, or merge a develop PR labelled `release:rc`. It merges develop into -# the release line and the release/* push runs CircleCI publish_release_candidate. name: Release candidate - -on: +'on': workflow_dispatch: inputs: dry_run: - description: Print the plan without merging or creating branches + 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] - + 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.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc')) - uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main - secrets: inherit + 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@7d73ac90b6b5a6acb61b4d3fa56625c5b6dcc652 with: - dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }} + 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 diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 7b032d6..a8863c3 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -13,4 +13,4 @@ publish: release_candidate: true release: true asciidoc: false -release_completion: false +release_completion: true -- 2.54.0 From 3b98b71abd34d556c27660e748cd62612817c04f Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 08:52:04 -0500 Subject: [PATCH 05/18] ci: adopt central orb 0.7.12 and stable gates (#9) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .github/workflows/release-rc.yml | 2 +- .qqq-ci.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index def7f95..c9cb461 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.11 + qqq-orb: kingsrook/qqq-orb@0.7.12 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.11 + orb_version: 0.7.12 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index 0158aa2..b48a32a 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -53,7 +53,7 @@ jobs: || (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@7d73ac90b6b5a6acb61b4d3fa56625c5b6dcc652 + uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44 with: dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }} diff --git a/.qqq-ci.yml b/.qqq-ci.yml index a8863c3..be6666b 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.11 +orb_version: 0.7.12 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From a998995a3cea351815dee66d8d097ae1ed48e55c Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 12:18:21 -0500 Subject: [PATCH 06/18] ci: centralize branch verification and source scanning (#11) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .github/workflows/codeql.yml | 23 +++++++++++++++++++++++ .qqq-ci.yml | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/codeql.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index c9cb461..23c1a84 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.12 + qqq-orb: kingsrook/qqq-orb@0.7.15 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.12 + orb_version: 0.7.15 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..bd3ceaf --- /dev/null +++ b/.github/workflows/codeql.yml @@ -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 diff --git a/.qqq-ci.yml b/.qqq-ci.yml index be6666b..adef7c7 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.12 +orb_version: 0.7.15 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 09706fc1c5c4b4090364d3d9590f50dd04665170 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 14:15:11 -0500 Subject: [PATCH 07/18] ci: adopt shared orb 0.7.17 (#14) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23c1a84..1d4fa0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.15 + qqq-orb: kingsrook/qqq-orb@0.7.17 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.15 + orb_version: 0.7.17 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index adef7c7..a9e9f43 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.15 +orb_version: 0.7.17 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From ca309f22d2131d6bb2a2ed699f85089ffa53d7ad Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 14:43:13 -0500 Subject: [PATCH 08/18] ci: adopt shared orb 0.7.18 (#15) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d4fa0d..42b73df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.17 + qqq-orb: kingsrook/qqq-orb@0.7.18 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.17 + orb_version: 0.7.18 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index a9e9f43..967a18e 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.17 +orb_version: 0.7.18 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 1aee6cb0c5c470819dbd80ec962754c35bfbbe3a Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 15:27:59 -0500 Subject: [PATCH 09/18] ci: adopt managed orb 0.8 patch track (#16) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 42b73df..f890c28 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.18 + qqq-orb: kingsrook/qqq-orb@0.8 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.18 + orb_version: '0.8' filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 967a18e..6777ede 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.18 +orb_version: '0.8' default_branch: develop java_version: '21' browsers: true -- 2.54.0 From fbd6bdedcd03b5bc1c6a33448169a5114479d42f Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 04:13:52 +0000 Subject: [PATCH 10/18] ci: add the identical QRun release caller Co-Authored-By: Codex --- .gitea/workflows/qrun-release.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .gitea/workflows/qrun-release.yaml diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml new file mode 100644 index 0000000..122f7e9 --- /dev/null +++ b/.gitea/workflows/qrun-release.yaml @@ -0,0 +1,12 @@ +# Identical in every QRun Maven application: do not edit. It only delegates to the protected qrun-release-v1 channel. +name: qrun-release +on: + push: + branches: [main, develop, 'release/**', 'hotfix/**'] +permissions: + contents: read +jobs: + release: + uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-maven.yaml@qrun-release-v1 + with: + runner: linux-small -- 2.54.0 From abbadd628a2478d7ade2ec28c7d86f54138596bc Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 06:19:45 +0000 Subject: [PATCH 11/18] ci: move the QRun release caller to the thin v2 contract Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Codex --- .gitea/workflows/qrun-release.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml index 122f7e9..77794cf 100644 --- a/.gitea/workflows/qrun-release.yaml +++ b/.gitea/workflows/qrun-release.yaml @@ -1,12 +1,16 @@ -# Identical in every QRun Maven application: do not edit. It only delegates to the protected qrun-release-v1 channel. +# Standard QRun release caller (v2, thin contract): every push and manual dispatch delegates to the protected qrun-release-v1 channel. Do not edit per repository. name: qrun-release on: - push: - branches: [main, develop, 'release/**', 'hotfix/**'] + push: {} + workflow_dispatch: {} permissions: contents: read + actions: read jobs: release: - uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-maven.yaml@qrun-release-v1 + uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-docker.yaml@qrun-release-v1 with: runner: linux-small + docker-runner: container-build + browsers: true + browser-runner: browser-build -- 2.54.0 From f0d6be24202b988f40dbf94c24e48d5a774604da Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 15:51:51 +0000 Subject: [PATCH 12/18] ci: add the isolated postpublish job to the QRun release caller Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Codex --- .gitea/workflows/qrun-release.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml index 77794cf..5ebc530 100644 --- a/.gitea/workflows/qrun-release.yaml +++ b/.gitea/workflows/qrun-release.yaml @@ -14,3 +14,12 @@ jobs: docker-runner: container-build browsers: true browser-runner: browser-build + postpublish: + if: ${{ needs.release.outputs.capture == 'true' }} + needs: release + permissions: + contents: write + actions: write + uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/qrun-source-version.yaml@qrun-release-v1 + with: + runner: linux-small -- 2.54.0 From 01dde6eded18a797a52cc236f0793296bb0810a1 Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 20:18:23 +0000 Subject: [PATCH 13/18] ci: enroll shared Gitea release-candidate lifecycle Co-Authored-By: Codex --- .gitea/workflows/release-rc.yaml | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .gitea/workflows/release-rc.yaml diff --git a/.gitea/workflows/release-rc.yaml b/.gitea/workflows/release-rc.yaml new file mode 100644 index 0000000..12ec9fc --- /dev/null +++ b/.gitea/workflows/release-rc.yaml @@ -0,0 +1,72 @@ +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 release run id (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 +permissions: + contents: read +jobs: + release-rc: + # The old qbit-rc-published repository_dispatch is replaced by the already-proven workflow_dispatch + # completion: the publisher's own ActionsWRITE job dispatches this workflow with finalize=true plus + # merge_sha/bump_sha/branch/version (same inputs as the manual finalize retry). + permissions: + contents: write + actions: read + pull-requests: write + if: github.event_name == 'workflow_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: self:KofTwentyTwo/platform-ci/.gitea/workflows/qbit-release-rc.yaml@qrun-release-v1 + with: + dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false + }} + finalize: ${{ 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: ${{ inputs.merge_sha || '' }} + bump_sha: ${{ inputs.bump_sha || '' }} + branch: ${{ inputs.branch || '' }} + version: ${{ inputs.version || '' }} + secrets: + QRUN_TAG_SIGNING_KEY: ${{ secrets.QRUN_TAG_SIGNING_KEY }} -- 2.54.0 From 3a855384a08396bc178f52508db84c81213104e6 Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 5 Nov 2026 22:07:15 +0000 Subject: [PATCH 14/18] ci: use shared ci-workflows Maven workflow 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 --- .gitea/workflows/build.yaml | 7 +++ .gitea/workflows/qrun-release.yaml | 25 ----------- .gitea/workflows/release-rc.yaml | 72 ------------------------------ 3 files changed, 7 insertions(+), 97 deletions(-) create mode 100644 .gitea/workflows/build.yaml delete mode 100644 .gitea/workflows/qrun-release.yaml delete mode 100644 .gitea/workflows/release-rc.yaml diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..ed42608 --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,7 @@ +name: build +on: + push: +jobs: + maven: + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@main + secrets: inherit diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml deleted file mode 100644 index 5ebc530..0000000 --- a/.gitea/workflows/qrun-release.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Standard QRun release caller (v2, thin contract): every push and manual dispatch delegates to the protected qrun-release-v1 channel. Do not edit per repository. -name: qrun-release -on: - push: {} - workflow_dispatch: {} -permissions: - contents: read - actions: read -jobs: - release: - uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-docker.yaml@qrun-release-v1 - with: - runner: linux-small - docker-runner: container-build - browsers: true - browser-runner: browser-build - postpublish: - if: ${{ needs.release.outputs.capture == 'true' }} - needs: release - permissions: - contents: write - actions: write - uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/qrun-source-version.yaml@qrun-release-v1 - with: - runner: linux-small diff --git a/.gitea/workflows/release-rc.yaml b/.gitea/workflows/release-rc.yaml deleted file mode 100644 index 12ec9fc..0000000 --- a/.gitea/workflows/release-rc.yaml +++ /dev/null @@ -1,72 +0,0 @@ -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 release run id (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 -permissions: - contents: read -jobs: - release-rc: - # The old qbit-rc-published repository_dispatch is replaced by the already-proven workflow_dispatch - # completion: the publisher's own ActionsWRITE job dispatches this workflow with finalize=true plus - # merge_sha/bump_sha/branch/version (same inputs as the manual finalize retry). - permissions: - contents: write - actions: read - pull-requests: write - if: github.event_name == 'workflow_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: self:KofTwentyTwo/platform-ci/.gitea/workflows/qbit-release-rc.yaml@qrun-release-v1 - with: - dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false - }} - finalize: ${{ 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: ${{ inputs.merge_sha || '' }} - bump_sha: ${{ inputs.bump_sha || '' }} - branch: ${{ inputs.branch || '' }} - version: ${{ inputs.version || '' }} - secrets: - QRUN_TAG_SIGNING_KEY: ${{ secrets.QRUN_TAG_SIGNING_KEY }} -- 2.54.0 From f5e87ca6821df61d7caac369edbe0d8b4ca0ca27 Mon Sep 17 00:00:00 2001 From: CI Date: Thu, 5 Nov 2026 16:26:03 -0600 Subject: [PATCH 15/18] ci: rerun shared workflow -- 2.54.0 From 703859349c67875efb9e03afe3e68faa75021f46 Mon Sep 17 00:00:00 2001 From: James Maes Date: Fri, 6 Nov 2026 14:12:31 +0000 Subject: [PATCH 16/18] ci: pin shared workflow to v1 Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index ed42608..ce00351 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -3,5 +3,5 @@ on: push: jobs: maven: - uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@main + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1 secrets: inherit -- 2.54.0 From f87f8544174181a705f9754650ef79016e1e03b2 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 7 Nov 2026 18:04:05 +0000 Subject: [PATCH 17/18] build(deps): align with Gitea registry (parent 2.2.1, QQQ 4.1.0-RC.26) pom.xml: parent -> 2.2.1 pom.xml: qqq.snapshot.version: 4.1.0-RC.11 -> 4.1.0-RC.26 pom.xml: added qqq.version=4.1.0-RC.26 (parent BOM import) Co-Authored-By: Claude Opus 5.5 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fe9cf42..9ff3a1c 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ + 4.1.0-RC.26 0.1.0-SNAPSHOT 21 21 @@ -29,7 +30,7 @@ com.kingsrook qbit-build-parent - 2.0.0 + 2.2.1 com.kingsrook.qbits @@ -246,7 +247,7 @@ true - 4.1.0-RC.11 + 4.1.0-RC.26 -- 2.54.0 From 51c38739f7a885d19b41ae39a2107cc5f02ff071 Mon Sep 17 00:00:00 2001 From: James Maes <6+james.maes@noreply.git.kof22.com> Date: Sat, 7 Nov 2026 12:22:16 -0600 Subject: [PATCH 18/18] test: adapt to QQQ 4.1.0-RC.26 (shared BasicErrorResponseV1 schema, blob: connect-src) Regenerate the committed OpenAPI fragment, allow the core-provided BasicErrorResponseV1 schema, and expect core's blob: connect-src default. Co-Authored-By: Claude Opus 5.5 --- .../resources/openapi/qqq-v1/qa-capture.yaml | 132 ++++++++++++++++++ .../qacapture/QaCaptureApiContractTest.java | 2 +- .../qbits/qacapture/QaCaptureServiceTest.java | 2 +- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/main/resources/openapi/qqq-v1/qa-capture.yaml b/src/main/resources/openapi/qqq-v1/qa-capture.yaml index efdf5d9..5bfdd15 100644 --- a/src/main/resources/openapi/qqq-v1/qa-capture.yaml +++ b/src/main/resources/openapi/qqq-v1/qa-capture.yaml @@ -1,5 +1,44 @@ components: schemas: + BasicErrorResponseV1: + description: "The error body of every UI API operation (core and extensions)." + properties: + error: + description: "The error message (the same as message; kept for clients of\ + \ UI API 1.0 to 1.5)." + type: "string" + code: + description: "A stable, machine-readable code. Well-known values: BAD_REQUEST\ + \ (400), UNAUTHENTICATED (401),\nPERMISSION_DENIED (403), NOT_FOUND (404),\ + \ CONFLICT (409), RATE_LIMITED (429), INTERNAL_ERROR (500),\nERROR (other).\ + \ Extensions may add codes; treat an unknown code by the HTTP status.\ + \ Since 1.6.0." + type: "string" + message: + description: "A message to show the user. Since 1.6.0." + type: "string" + fieldErrors: + description: "Errors about particular request fields, when known. Since\ + \ 1.6.0." + items: + properties: + field: + description: "The field (or parameter) name." + type: "string" + message: + description: "The error about that field." + type: "string" + code: + description: "An optional machine-readable code, e.g. REQUIRED, TOO_LONG,\ + \ PATTERN." + type: "string" + type: "object" + type: "array" + traceId: + description: "Identifies this request in the server logs (also the X-QQQ-Trace-Id\ + \ response header); quote it when reporting a problem. Since 1.6.0." + type: "string" + type: "object" QaCaptureCapabilityV1: description: "What QA capture means for the signed-in user. Clients that see\ \ enabled=false do nothing else." @@ -237,9 +276,42 @@ components: type: "object" securitySchemes: sessionUuidCookie: + description: "The session cookie set by POST /qqq/v1/manageSession (web clients)." in: "cookie" name: "sessionUUID" type: "apiKey" + bearerAuth: + bearerFormat: "JWT or session UUID" + description: "`Authorization: Bearer `, where the token is either\n-\ + \ an OAuth2 access token (a signed JWT) issued by the app's identity provider\ + \ to the app's client,\n or to its native client (`values.nativeClientId`\ + \ in GET /qqq/v1/metaData/authentication), for\n example from the device-authorization\ + \ grant or auth-code + PKCE. It is verified against the\n provider's keys\ + \ (issuer, audience, expiry, signature) and gives the same user and permissions\n\ + \ as signing in on the web. When it expires (401), refresh it at the provider\ + \ and retry; or\n- a session UUID returned by POST /qqq/v1/manageSession,\ + \ which the server keeps refreshed." + scheme: "bearer" + type: "http" + oauth2: + description: "The app's OAuth2 / OpenID Connect provider. The real endpoints\ + \ come from the provider's discovery\ndocument: `{values.baseUrl}.well-known/openid-configuration`,\ + \ with values from\nGET /qqq/v1/metaData/authentication. Native clients use\ + \ `values.nativeClientId` (a public client)\nwith auth-code + PKCE (loopback\ + \ or custom-scheme redirect URI registered at the provider) or the\ndevice-authorization\ + \ grant (`device_authorization_endpoint`, RFC 8628), then send the access\ + \ token\nas `Authorization: Bearer`." + flows: + authorizationCode: + authorizationUrl: "/authorize" + scopes: + email: "Email address" + offline_access: "A refresh token, to renew the access token without signing\ + \ in again" + openid: "OpenID Connect sign-in" + profile: "Name, username and groups (permissions come from the groups)" + tokenUrl: "/token" + type: "oauth2" info: contact: email: "contact@kingsrook.com" @@ -280,10 +352,37 @@ paths: schema: $ref: "#/components/schemas/QaCaptureCapabilityV1" description: "Successful Response" + 404: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Not found, or not visible to this user (code NOT_FOUND)." + 409: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Conflict with the current state, e.g. an edit conflict or\ + \ an invalid transition (code CONFLICT)." + 429: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Too many requests (code RATE_LIMITED); retry later." security: - sessionUuidCookie: - "N/A" + - bearerAuth: + - "N/A" + - oauth2: + - "openid" + - "profile" + - "email" summary: "Read the QA capture capability" + tags: + - "qa-capture" /qqq/v1/qbit/qa-capture/report: post: description: "Stores the report (page, app, table, record, process, recent request\ @@ -322,7 +421,40 @@ paths: schema: $ref: "#/components/schemas/QaCaptureReportResultV1" description: "Successful Response" + 404: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Not found, or not visible to this user (code NOT_FOUND)." + 409: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Conflict with the current state, e.g. an edit conflict or\ + \ an invalid transition (code CONFLICT)." + 429: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Too many requests (code RATE_LIMITED); retry later." security: - sessionUuidCookie: - "N/A" + - bearerAuth: + - "N/A" + - oauth2: + - "openid" + - "profile" + - "email" summary: "Create a QA report" + tags: + - "qa-capture" +servers: +- description: "The QQQ server that serves this document (paths include /qqq/v1)." + url: "/" +tags: +- description: "Operations of the qa-capture extension." + name: "qa-capture" diff --git a/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureApiContractTest.java b/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureApiContractTest.java index 5cd0444..da8a2c0 100644 --- a/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureApiContractTest.java +++ b/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureApiContractTest.java @@ -58,7 +58,7 @@ class QaCaptureApiContractTest extends BaseQaCaptureTest "./mvnw -Dtest=QaCaptureApiContractTest -Dqacapture.openapi.update=true test")).isEmpty(); var model = version.generateExtensionOpenAPIModel("qqq", extension); - assertThat(model.getComponents().getSchemas().keySet()).allMatch(name -> name.startsWith("QaCapture")); + assertThat(model.getComponents().getSchemas().keySet()).allMatch(name -> name.startsWith("QaCapture") || name.equals("BasicErrorResponseV1")); assertThat(model.getPaths().keySet()).containsExactlyInAnyOrder("/qqq/v1/qbit/qa-capture/capability", "/qqq/v1/qbit/qa-capture/report"); assertThat(extension.getSemanticVersion()).isEqualTo("1.0.0"); assertThat(extension.getEffectivePathPrefix()).isEqualTo("/qbit/qa-capture"); diff --git a/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureServiceTest.java b/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureServiceTest.java index 0001cb8..9ae1816 100644 --- a/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureServiceTest.java +++ b/src/test/java/com/kingsrook/qbits/qacapture/QaCaptureServiceTest.java @@ -248,7 +248,7 @@ class QaCaptureServiceTest extends BaseQaCaptureTest QaCaptureCsp.customize(headers); assertThat(headers.getSources("script-src")).containsExactly("'self'", "https://js.jam.dev"); assertThat(headers.getSources("frame-src")).containsExactly("'self'", "https://*.jam.dev"); - assertThat(headers.getSources("connect-src")).containsExactly("'self'", "https://*.jam.dev"); + assertThat(headers.getSources("connect-src")).containsExactly("'self'", "blob:", "https://*.jam.dev"); /////////////////////////////////////////////////////////////////// // a broken database is logged and leaves the headers unchanged // -- 2.54.0