diff --git a/.circleci/config.yml b/.circleci/config.yml index 113ce37..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.3 + qqq-orb: kingsrook/qqq-orb@0.8 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.3 + orb_version: '0.8' filters: branches: ignore: automation/qbit-rc-pending diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml new file mode 100644 index 0000000..5ebc530 --- /dev/null +++ b/.gitea/workflows/qrun-release.yaml @@ -0,0 +1,25 @@ +# 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 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 }} 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/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index 5aab78d..b48a32a 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -10,6 +10,15 @@ name: Release candidate 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 @@ -44,12 +53,16 @@ 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@ee8953bd937d7f69c7a54308ffd5f9aed95aa768 + 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 diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 6ade08d..6777ede 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.3 +orb_version: '0.8' default_branch: develop java_version: '21' browsers: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0620f12..b37cefc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project are documented here. The format follows Keep ## [0.1.0] - Unreleased ### Fixed +- SQL changelog headers: explanatory "-- Include it from the host..." comments were parsed as a licensed Liquibase `include` directive. Replaced with neutral wording; changeset bodies and schema are unchanged. - Under OAuth2, per-recipient checks (alert visibility for external channels, follower record access, audience sessions) never worked: they used token-less automated sessions, which OAuth2 rejects, so external sends fell back to the conservative rule. When the host declares the QQQ `alerts` service principal (`AlertsQBitConfig.withServicePrincipal`, default `alerts`), these checks run on the recipient's behalf through it: the recipient's current access intersected with the principal's. Alert recording and routing bookkeeping stay on the system session, so alerting does not depend on the identity provider being up. With no principal declared, behavior is unchanged. Needs the QQQ release with service identities (qqq#1255, #1256). - Audit noise: every monitor run (lease claim, then run result) and every reminder check wrote a "Record was Edited" audit (about 7,000 `alerts_monitor` and 1,800 `alerts_alert` audits in 6 hours on one production host, most with no field details). Engine updates that touch only bookkeeping fields (`RuntimeFields`: monitor `claimedBy`, `claimedUntil`, `lastRunAt`, `nextRunAt`, `lastStatus`, `lastValue`, `lastError`, `lastElapsedMs`, `consecutiveFailures`, `conditionSince`; alert `timedNotificationCheckedAt`, `lastNotifiedAt`, `recordRefCount`) are written with `omitDmlAudit`. Configuration edits, raises and alert state changes are audited as before, as are UI and API edits of any field. - Read the QBit metadata version from the Maven-filtered artifact version so release builds report their release version. diff --git a/src/main/resources/db/liquibase/qbit-alerts/alerts-defaults.changelog.sql b/src/main/resources/db/liquibase/qbit-alerts/alerts-defaults.changelog.sql index 77ac3c2..b693aa8 100644 --- a/src/main/resources/db/liquibase/qbit-alerts/alerts-defaults.changelog.sql +++ b/src/main/resources/db/liquibase/qbit-alerts/alerts-defaults.changelog.sql @@ -3,7 +3,7 @@ -- qbit-alerts starter defaults: the two channels every install needs, and the -- two groups the qbit itself files alerts under, so Delivery health and the -- group lists are not empty before the first alert. --- Include it from the host's master changelog AFTER alerts.changelog.sql: +-- Load this file from the host's master changelog AFTER alerts.changelog.sql: -- -- - include: { file: db/liquibase/qbit-alerts/alerts-defaults.changelog.sql } -- diff --git a/src/main/resources/db/liquibase/qbit-alerts/alerts.changelog.sql b/src/main/resources/db/liquibase/qbit-alerts/alerts.changelog.sql index 6a0406b..bd5f839 100644 --- a/src/main/resources/db/liquibase/qbit-alerts/alerts.changelog.sql +++ b/src/main/resources/db/liquibase/qbit-alerts/alerts.changelog.sql @@ -1,7 +1,7 @@ -- liquibase formatted sql -- qbit-alerts schema (QRun-IO/qqq docs/specs/2026-10-12-qbit-alerts-design.md section 4). --- Include it from the host's master changelog: +-- Load this file from the host's master changelog: -- -- - include: { file: db/liquibase/qbit-alerts/alerts.changelog.sql } --