name: QingLong 3.0 CI on: push: branches: - next pull_request: branches: - next workflow_dispatch: permissions: contents: read concurrency: group: ql3-ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: backend: name: Backend (${{ matrix.arch }}, Node ${{ matrix.node }}) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 arch: x64 node: '24.18.0' - runner: ubuntu-24.04-arm arch: arm64 node: '24.18.0' steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: ${{ matrix.node }} cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Initialize the public test environment run: | cp .env.example .env mkdir -p data/db - name: Rebuild the reviewed native SQLite binding run: pnpm rebuild @whyour/sqlite3 - name: Build backend and QL3 workspace from source run: | pnpm build:back pnpm run build:packages:ql3 - name: Test backend migrations run: pnpm test:back - name: Audit edge dependency and import isolation run: | pnpm audit:edge-imports:ql3 pnpm audit:cluster-dependencies:ql3 pnpm audit:cluster-deployment:ql3 - name: Smoke benchmark edge executor run: pnpm benchmark:edge -- --json - name: Prove disabled AI Profile has zero storage or credential reachability run: >- pnpm benchmark:ai-profile -- --json --max-rss-delta-mb=8 --max-disabled-activation-ms=50 - name: Benchmark Node SQLite transaction boundary run: >- pnpm benchmark:db:node-sqlite -- --json --iterations=250 --batch-size=10 --max-transaction-p95-ms=250 --max-batch-stall-ms=2500 --max-rss-delta-mb=64 - name: Enforce bounded receipt maintenance under edge budget run: >- docker run --rm --memory=256m --memory-swap=256m --cpus=1 --pids-limit=128 --volume "${{ github.workspace }}:/workspace:ro" --workdir /workspace node:${{ matrix.node }}-bookworm-slim node --test test/back/completionReceiptOrphanAuditor.test.cjs service-manager-bridge: name: systemd/OpenRC dual-authority bridge runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Build the complete local test fixture workspace run: pnpm run build:packages:ql3 - name: Build and test Local Owner product closure run: pnpm --filter @qinglong/local-owner-cli test - name: Audit root bridge static import closure run: pnpm audit:service-manager-bridge-imports:ql3 - name: Run real systemd/OpenRC root and non-root bridge gate run: pnpm test:service-manager-bridge:ql3 linux-resource-envelopes: name: Linux resource envelopes (${{ matrix.arch }}, Node 24) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 arch: x64 - runner: ubuntu-24.04-arm arch: arm64 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Initialize the public test environment run: | cp .env.example .env mkdir -p data/db - name: Rebuild the reviewed native SQLite binding run: pnpm rebuild @whyour/sqlite3 - name: Build benchmark targets once run: | pnpm build:back pnpm run build:packages:ql3 - name: Test fail-closed Linux resource gate run: node --test test/back/ql3LinuxResourceGate.test.cjs test/back/ql3LinuxResourceWorkflow.test.cjs test/back/ql3LegacyShadowResourceRollback.test.cjs test/back/ql3PhysicalEdgeEvidence.test.cjs test/back/ql3PhysicalEdgeIdleSampler.test.cjs test/back/ql3PhysicalEdgeFaultProbe.test.cjs test/back/ql3PhysicalEdgeTaskScale.test.cjs - name: Prepare architecture evidence directory run: mkdir -p "${RUNNER_TEMP}/ql3-linux-resource-evidence" - name: Stress router headroom without claiming a supported minimum run: >- docker run --rm --read-only --security-opt no-new-privileges --user 65532:65532 --memory=128m --memory-swap=128m --cpus=0.5 --pids-limit=64 --tmpfs /tmp:rw,nosuid,nodev,noexec,mode=1777,size=64m --volume "${{ github.workspace }}:/workspace:ro" --workdir /workspace node:24.18.0-bookworm-slim node scripts/ql3-linux-resource-gate.cjs --tier=router-stress-ci --expected-arch=${{ matrix.arch }} --json > "${RUNNER_TEMP}/ql3-linux-resource-evidence/router-stress-ci.json" - name: Enforce the emulated edge release envelope run: >- docker run --rm --read-only --security-opt no-new-privileges --user 65532:65532 --memory=256m --memory-swap=256m --cpus=1 --pids-limit=128 --tmpfs /tmp:rw,nosuid,nodev,noexec,mode=1777,size=64m --volume "${{ github.workspace }}:/workspace:ro" --workdir /workspace node:24.18.0-bookworm-slim node scripts/ql3-linux-resource-gate.cjs --tier=edge-release-ci --expected-arch=${{ matrix.arch }} --json > "${RUNNER_TEMP}/ql3-linux-resource-evidence/edge-release-ci.json" - name: Enforce the cluster control-plane guard independently run: >- docker run --rm --read-only --security-opt no-new-privileges --user 65532:65532 --memory=512m --memory-swap=512m --cpus=2 --pids-limit=256 --tmpfs /tmp:rw,nosuid,nodev,noexec,mode=1777,size=32m --volume "${{ github.workspace }}:/workspace:ro" --workdir /workspace node:24.18.0-bookworm-slim node scripts/ql3-linux-resource-gate.cjs --tier=cluster-control-ci --expected-arch=${{ matrix.arch }} --json > "${RUNNER_TEMP}/ql3-linux-resource-evidence/cluster-control-ci.json" - name: Bind native resource evidence to this workflow run env: SOURCE_REPOSITORY: ${{ github.repository }} SOURCE_REVISION: ${{ github.sha }} SOURCE_WORKFLOW: ${{ github.workflow }} SOURCE_RUN_ID: ${{ github.run_id }} SOURCE_RUN_ATTEMPT: ${{ github.run_attempt }} run: >- node scripts/ql3-linux-resource-release-evidence.cjs --mode=bundle --repository="${SOURCE_REPOSITORY}" --revision="${SOURCE_REVISION}" --workflow="${SOURCE_WORKFLOW}" --run-id="${SOURCE_RUN_ID}" --run-attempt="${SOURCE_RUN_ATTEMPT}" --architecture="${{ matrix.arch }}" --router-stress-ci="${RUNNER_TEMP}/ql3-linux-resource-evidence/router-stress-ci.json" --edge-release-ci="${RUNNER_TEMP}/ql3-linux-resource-evidence/edge-release-ci.json" --cluster-control-ci="${RUNNER_TEMP}/ql3-linux-resource-evidence/cluster-control-ci.json" --output="${RUNNER_TEMP}/ql3-linux-resource-evidence/${{ matrix.arch }}.json" - name: Upload native resource evidence uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ql3-linux-resource-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.arch }} path: ${{ runner.temp }}/ql3-linux-resource-evidence/${{ matrix.arch }}.json if-no-files-found: error retention-days: 14 compression-level: 0 overwrite: false include-hidden-files: false linux-resource-release-evidence: name: Cross-architecture Linux resource release evidence needs: linux-resource-envelopes runs-on: ubuntu-24.04 permissions: contents: read steps: - uses: actions/checkout@v6 - name: Download native x64 resource evidence uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ql3-linux-resource-${{ github.run_id }}-${{ github.run_attempt }}-x64 path: ${{ runner.temp }}/ql3-linux-resource-evidence/x64 - name: Download native arm64 resource evidence uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ql3-linux-resource-${{ github.run_id }}-${{ github.run_attempt }}-arm64 path: ${{ runner.temp }}/ql3-linux-resource-evidence/arm64 - name: Merge source-bound cross-architecture evidence env: SOURCE_REPOSITORY: ${{ github.repository }} SOURCE_REVISION: ${{ github.sha }} SOURCE_WORKFLOW: ${{ github.workflow }} SOURCE_RUN_ID: ${{ github.run_id }} SOURCE_RUN_ATTEMPT: ${{ github.run_attempt }} run: >- node scripts/ql3-linux-resource-release-evidence.cjs --mode=merge --repository="${SOURCE_REPOSITORY}" --revision="${SOURCE_REVISION}" --workflow="${SOURCE_WORKFLOW}" --run-id="${SOURCE_RUN_ID}" --run-attempt="${SOURCE_RUN_ATTEMPT}" --x64="${RUNNER_TEMP}/ql3-linux-resource-evidence/x64/x64.json" --arm64="${RUNNER_TEMP}/ql3-linux-resource-evidence/arm64/arm64.json" --output="${RUNNER_TEMP}/ql3-linux-resource-evidence/cross-architecture.json" - name: Upload cross-architecture resource release evidence uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ql3-linux-resource-release-${{ github.run_id }}-${{ github.run_attempt }} path: ${{ runner.temp }}/ql3-linux-resource-evidence/cross-architecture.json if-no-files-found: error retention-days: 14 compression-level: 0 overwrite: false include-hidden-files: false supply-chain: name: Profile supply-chain gate runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Test bounded fail-closed audit classifier run: >- node --test test/back/ql3ProfileVulnerabilityAudit.test.cjs test/back/ql3ClusterImageSbom.test.cjs test/back/ql3ClusterImageReleaseAudit.test.cjs test/back/ql3ReleaseCandidateContract.test.cjs test/back/ql3PrivateReleaseEvidenceReceiptContract.test.cjs test/back/ql3ReleaseSetContract.test.cjs test/back/ql3ReleaseCatalogContract.test.cjs test/back/ql3ReleaseDeploymentReadinessContract.test.cjs test/back/ql3ReleaseTagFinalizer.test.cjs test/back/ql3ReleasePublicationClosureContract.test.cjs test/back/ql3ReleaseCatalogConsumptionCeremony.test.cjs test/back/ql3DeploymentLockContract.test.cjs test/back/ql3ImageOsVulnerabilityPolicy.test.cjs test/back/ql3LocalImageAudit.test.cjs test/back/ql3VersionTransition.test.cjs - name: Audit cluster image release contract run: | pnpm audit:release-version:ql3 pnpm audit:image-release:ql3 pnpm audit:deployment-lock-surfaces:ql3 pnpm audit:local-image:ql3 - name: Reject high or critical advisories in 3.0 Profile importers run: pnpm audit:profiles:ql3 local-image: name: Local application image (${{ matrix.image_arch }}) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 node_arch: x64 image_arch: amd64 - runner: ubuntu-24.04-arm node_arch: arm64 image_arch: arm64 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.node_arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Audit immutable local image inputs run: | node --test test/back/ql3LocalImageAudit.test.cjs pnpm audit:local-image:ql3 - name: Build the native AI-excluded image env: IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} run: >- docker build --file deploy/containers/ql3-local-application/Dockerfile --build-arg SOURCE_REVISION=${{ github.sha }} --tag "${IMAGE}" . - name: Materialize the reviewed local OS vulnerability exceptions run: >- node scripts/ql3-image-os-vulnerability-policy.cjs --image=local --output=${{ runner.temp }}/ql3-local-${{ matrix.image_arch }}.trivyignore.yaml - name: Reject unexcepted high or critical local OS vulnerabilities uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: version: 'v0.70.0' image-ref: qinglong3-local-application:ci-${{ matrix.image_arch }} scanners: 'vuln' vuln-type: 'os' severity: 'HIGH,CRITICAL' ignore-unfixed: 'false' exit-code: '1' format: 'table' hide-progress: 'true' timeout: '10m0s' cache: 'false' trivyignores: ${{ runner.temp }}/ql3-local-${{ matrix.image_arch }}.trivyignore.yaml - name: Verify non-root identity and architecture env: IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} EXPECTED: ${{ matrix.image_arch }} 65532:65532 2,3,4 51 51 51 1 run: | set -euo pipefail actual="$(docker image inspect --format '{{.Architecture}} {{.Config.User}} {{index .Config.Labels "io.qinglong.local.application-config"}} {{index .Config.Labels "io.qinglong.local.sqlite-contract-min"}} {{index .Config.Labels "io.qinglong.local.sqlite-contract-max"}} {{index .Config.Labels "io.qinglong.local.sqlite-write-contract"}} {{index .Config.Labels "io.qinglong.local.compose-selection"}}' "${IMAGE}")" if [[ "${actual}" != "${EXPECTED}" ]]; then echo "unexpected image contract: ${actual}" >&2 exit 1 fi - name: Reconcile the exact production package inventory env: IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} run: >- docker run --rm --read-only --network none --cap-drop ALL --security-opt no-new-privileges --volume "${{ github.workspace }}:/audit:ro" --workdir /audit --entrypoint node "${IMAGE}" scripts/ql3-local-image-inventory.cjs --inventory-root=/opt/qinglong/node_modules - name: Generate and reconcile the reviewed CycloneDX SBOM env: IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} run: | node scripts/ql3-cluster-image-sbom.cjs \ --image=local \ --output="${{ runner.temp }}/ql3-local-application.cdx.json" docker run --rm --read-only \ --network none \ --cap-drop ALL \ --security-opt no-new-privileges \ --volume "${{ github.workspace }}:/audit:ro" \ --workdir /audit \ --entrypoint node \ "${IMAGE}" \ scripts/ql3-cluster-image-sbom.cjs \ --image=local \ --inventory-root=/opt/qinglong/node_modules - name: Exercise the entrypoint under the router stress envelope env: IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} run: >- docker run --rm --read-only --network none --cap-drop ALL --security-opt no-new-privileges --memory=128m --memory-swap=128m --cpus=0.5 --pids-limit=64 --tmpfs /tmp:rw,nosuid,nodev,noexec,size=16m "${IMAGE}" --help - name: Build the short-lived fresh deployment authority working-directory: packages/ql3-local-owner-cli run: node ../../scripts/ql3-build-package-closure.cjs - name: Run fresh Edge and Standalone SQLite lifecycles env: IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} run: | node scripts/ql3-local-image-live-contract.cjs --image="${IMAGE}" --profile=edge node scripts/ql3-local-image-live-contract.cjs --image="${IMAGE}" --profile=standalone - name: Run authenticated Local API cancellation through real Linux processes env: QL3_LOCAL_API_CANCELLATION_LIVE: '1' run: | install -d -m 0700 "${{ runner.temp }}/ql3-local-api-cancellation" node scripts/ql3-local-api-cancellation-live-contract.cjs \ --profile=edge \ --report="${{ runner.temp }}/ql3-local-api-cancellation/edge.json" node scripts/ql3-local-api-cancellation-live-contract.cjs \ --profile=standalone \ --report="${{ runner.temp }}/ql3-local-api-cancellation/standalone.json" node scripts/ql3-local-api-cancellation-live-audit.cjs \ --report="${{ runner.temp }}/ql3-local-api-cancellation/edge.json" node scripts/ql3-local-api-cancellation-live-audit.cjs \ --report="${{ runner.temp }}/ql3-local-api-cancellation/standalone.json" cluster-image: name: Cluster ${{ matrix.image }} image (${{ matrix.image_arch }}) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 node_arch: x64 image_arch: amd64 image: control repository: qinglong3-cluster-control runtime_user: 10001:10001 dockerfile: deploy/containers/ql3-cluster-control/Dockerfile target: runtime - runner: ubuntu-24.04-arm node_arch: arm64 image_arch: arm64 image: control repository: qinglong3-cluster-control runtime_user: 10001:10001 dockerfile: deploy/containers/ql3-cluster-control/Dockerfile target: runtime - runner: ubuntu-24.04 node_arch: x64 image_arch: amd64 image: control-ai repository: qinglong3-cluster-control-ai runtime_user: 10001:10001 dockerfile: deploy/containers/ql3-cluster-control/Dockerfile target: runtime-ai - runner: ubuntu-24.04-arm node_arch: arm64 image_arch: arm64 image: control-ai repository: qinglong3-cluster-control-ai runtime_user: 10001:10001 dockerfile: deploy/containers/ql3-cluster-control/Dockerfile target: runtime-ai - runner: ubuntu-24.04 node_arch: x64 image_arch: amd64 image: admin repository: qinglong3-cluster-admin runtime_user: 10001:10001 dockerfile: deploy/containers/ql3-cluster-admin/Dockerfile target: runtime - runner: ubuntu-24.04-arm node_arch: arm64 image_arch: arm64 image: admin repository: qinglong3-cluster-admin runtime_user: 10001:10001 dockerfile: deploy/containers/ql3-cluster-admin/Dockerfile target: runtime - runner: ubuntu-24.04 node_arch: x64 image_arch: amd64 image: worker repository: qinglong3-worker runtime_user: 65532:65532 dockerfile: deploy/containers/ql3-worker/Dockerfile target: runtime - runner: ubuntu-24.04-arm node_arch: arm64 image_arch: arm64 image: worker repository: qinglong3-worker runtime_user: 65532:65532 dockerfile: deploy/containers/ql3-worker/Dockerfile target: runtime steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.node_arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Test exact SBOM and release contract failures run: node --test test/back/ql3ClusterImageSbom.test.cjs test/back/ql3ClusterImageReleaseAudit.test.cjs test/back/ql3ClusterCopilotConsoleCapacityEvidence.test.cjs - name: Audit deployment and image release contracts run: | pnpm audit:cluster-deployment:ql3 pnpm audit:image-release:ql3 - name: Build the native production image env: IMAGE: ${{ matrix.repository }}:ci-${{ matrix.image_arch }} run: >- docker build --file ${{ matrix.dockerfile }} --target ${{ matrix.target }} --build-arg SOURCE_REVISION=${{ github.sha }} --tag "${IMAGE}" . - name: Materialize the reviewed Cluster OS vulnerability exceptions run: >- node scripts/ql3-image-os-vulnerability-policy.cjs --image=${{ matrix.image }} --output=${{ runner.temp }}/ql3-${{ matrix.image }}-${{ matrix.image_arch }}.trivyignore.yaml - name: Reject unexcepted high or critical Cluster OS vulnerabilities uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: version: 'v0.70.0' image-ref: ${{ matrix.repository }}:ci-${{ matrix.image_arch }} scanners: 'vuln' vuln-type: 'os' severity: 'HIGH,CRITICAL' ignore-unfixed: 'false' exit-code: '1' format: 'table' hide-progress: 'true' timeout: '10m0s' cache: 'false' trivyignores: ${{ runner.temp }}/ql3-${{ matrix.image }}-${{ matrix.image_arch }}.trivyignore.yaml - name: Verify architecture and non-root runtime identity env: IMAGE: ${{ matrix.repository }}:ci-${{ matrix.image_arch }} EXPECTED: ${{ matrix.image_arch }} ${{ matrix.runtime_user }} run: | set -euo pipefail actual="$(docker image inspect --format '{{.Architecture}} {{.Config.User}}' "${IMAGE}")" if [[ "${actual}" != "${EXPECTED}" ]]; then echo "unexpected image contract: ${actual}" >&2 exit 1 fi - name: Run the bounded Cluster Admin product facade if: matrix.image == 'admin' env: IMAGE: qinglong3-cluster-admin:ci-${{ matrix.image_arch }} QL3_CLUSTER_ADMIN_PRODUCT_LIVE: '1' run: node scripts/ql3-cluster-admin-product-live-contract.cjs --image="${IMAGE}" - name: Capture the fixed Cluster Copilot Console capacity envelope if: matrix.image == 'admin' timeout-minutes: 10 env: IMAGE: qinglong3-cluster-admin:ci-${{ matrix.image_arch }} QL3_CLUSTER_COPILOT_CONSOLE_CAPACITY_LIVE: '1' SOURCE_REPOSITORY: ${{ github.repository }} SOURCE_REVISION: ${{ github.sha }} SOURCE_WORKFLOW: ${{ github.workflow }} SOURCE_RUN_ID: ${{ github.run_id }} SOURCE_RUN_ATTEMPT: ${{ github.run_attempt }} run: | mkdir -p "${RUNNER_TEMP}/ql3-cluster-console-capacity" node scripts/ql3-cluster-copilot-console-capacity-evidence.cjs \ --mode=capture \ --repository="${SOURCE_REPOSITORY}" \ --revision="${SOURCE_REVISION}" \ --workflow="${SOURCE_WORKFLOW}" \ --run-id="${SOURCE_RUN_ID}" \ --run-attempt="${SOURCE_RUN_ATTEMPT}" \ --architecture="${{ matrix.node_arch }}" \ --image="${IMAGE}" \ --output="${RUNNER_TEMP}/ql3-cluster-console-capacity/${{ matrix.node_arch }}.json" - name: Upload native Cluster Copilot Console capacity evidence if: matrix.image == 'admin' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ql3-cluster-console-capacity-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.node_arch }} path: ${{ runner.temp }}/ql3-cluster-console-capacity/${{ matrix.node_arch }}.json if-no-files-found: error retention-days: 14 compression-level: 0 overwrite: false include-hidden-files: false - name: Generate the reviewed application SBOM run: >- node scripts/ql3-cluster-image-sbom.cjs --image=${{ matrix.image }} --output=${{ runner.temp }}/ql3-cluster-${{ matrix.image }}.cdx.json - name: Reconcile SBOM with the actual read-only image inventory env: IMAGE: ${{ matrix.repository }}:ci-${{ matrix.image_arch }} run: >- docker run --rm --read-only --security-opt no-new-privileges --user ${{ matrix.runtime_user }} --volume "${{ github.workspace }}:/audit:ro" --workdir /audit --entrypoint node "${IMAGE}" scripts/ql3-cluster-image-sbom.cjs --image=${{ matrix.image }} --inventory-root=/opt/qinglong/node_modules cluster-console-capacity-release-evidence: name: Cross-architecture Cluster Copilot Console capacity evidence needs: cluster-image runs-on: ubuntu-24.04 timeout-minutes: 5 permissions: contents: read steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24.18.0' - name: Download native x64 Console capacity evidence uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ql3-cluster-console-capacity-${{ github.run_id }}-${{ github.run_attempt }}-x64 path: ${{ runner.temp }}/ql3-cluster-console-capacity/x64 - name: Download native arm64 Console capacity evidence uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ql3-cluster-console-capacity-${{ github.run_id }}-${{ github.run_attempt }}-arm64 path: ${{ runner.temp }}/ql3-cluster-console-capacity/arm64 - name: Merge and audit the source-bound Console capacity evidence env: SOURCE_REPOSITORY: ${{ github.repository }} SOURCE_REVISION: ${{ github.sha }} SOURCE_WORKFLOW: ${{ github.workflow }} SOURCE_RUN_ID: ${{ github.run_id }} SOURCE_RUN_ATTEMPT: ${{ github.run_attempt }} run: | node scripts/ql3-cluster-copilot-console-capacity-evidence.cjs \ --mode=merge \ --repository="${SOURCE_REPOSITORY}" \ --revision="${SOURCE_REVISION}" \ --workflow="${SOURCE_WORKFLOW}" \ --run-id="${SOURCE_RUN_ID}" \ --run-attempt="${SOURCE_RUN_ATTEMPT}" \ --x64="${RUNNER_TEMP}/ql3-cluster-console-capacity/x64/x64.json" \ --arm64="${RUNNER_TEMP}/ql3-cluster-console-capacity/arm64/arm64.json" \ --output="${RUNNER_TEMP}/ql3-cluster-console-capacity/cross-architecture.json" node scripts/ql3-cluster-copilot-console-capacity-evidence.cjs \ --mode=audit \ --repository="${SOURCE_REPOSITORY}" \ --revision="${SOURCE_REVISION}" \ --workflow="${SOURCE_WORKFLOW}" \ --run-id="${SOURCE_RUN_ID}" \ --run-attempt="${SOURCE_RUN_ATTEMPT}" \ --report="${RUNNER_TEMP}/ql3-cluster-console-capacity/cross-architecture.json" - name: Upload cross-architecture Console capacity evidence uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ql3-cluster-console-capacity-release-${{ github.run_id }}-${{ github.run_attempt }} path: ${{ runner.temp }}/ql3-cluster-console-capacity/cross-architecture.json if-no-files-found: error retention-days: 14 compression-level: 0 overwrite: false include-hidden-files: false image-oci: name: ${{ matrix.image }} multi-architecture OCI evidence runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: include: - image: control dockerfile: deploy/containers/ql3-cluster-control/Dockerfile target: runtime - image: control-ai dockerfile: deploy/containers/ql3-cluster-control/Dockerfile target: runtime-ai - image: admin dockerfile: deploy/containers/ql3-cluster-admin/Dockerfile target: runtime - image: local dockerfile: deploy/containers/ql3-local-application/Dockerfile target: runtime - image: worker dockerfile: deploy/containers/ql3-worker/Dockerfile target: runtime steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4 - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Test bounded OCI layout audit failures run: node --test test/back/ql3ClusterOciLayoutAudit.test.cjs - name: Build an attested amd64 and arm64 OCI layout run: >- docker buildx build --platform linux/amd64,linux/arm64 --file ${{ matrix.dockerfile }} --target ${{ matrix.target }} --build-arg SOURCE_REVISION=${{ github.sha }} --attest type=provenance,mode=max --attest type=sbom --output type=oci,dest=${{ runner.temp }}/ql3-${{ matrix.image }}.oci.tar . - name: Audit both image manifests and bound attestations run: | mkdir "${{ runner.temp }}/ql3-${{ matrix.image }}-oci" tar -xf "${{ runner.temp }}/ql3-${{ matrix.image }}.oci.tar" \ -C "${{ runner.temp }}/ql3-${{ matrix.image }}-oci" node scripts/ql3-cluster-oci-layout-audit.cjs \ --image="${{ matrix.image }}" \ --layout="${{ runner.temp }}/ql3-${{ matrix.image }}-oci" \ --expected-revision="${{ github.sha }}" worker-runtime: name: Worker runtime (${{ matrix.arch }}, Node 24) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 arch: x64 - runner: ubuntu-24.04-arm arch: arm64 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Initialize the public test environment run: cp .env.example .env - name: Audit profile dependency boundaries run: | pnpm audit:edge-imports:ql3 pnpm audit:cluster-dependencies:ql3 - name: Build QL3 workspace once run: pnpm run build:packages:ql3 - name: Test local identity, atomic install and explicit renewal run: node --test packages/ql3-worker-runtime/test/*.test.cjs local-profiles: name: Local Profiles (${{ matrix.arch }}, Node 24) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 arch: x64 - runner: ubuntu-24.04-arm arch: arm64 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Audit exact Profile dependency and import boundaries run: pnpm audit:cluster-dependencies:ql3 - name: Test bounded QL3 artifact cleanup scope run: node --test test/back/ql3CleanPackageArtifacts.test.cjs - name: Build once and test all QL3 packages run: pnpm run test:packages:ql3 - name: Exercise Owner authorities as root in a read-only container run: >- docker run --rm --read-only --memory=192m --memory-swap=192m --cpus=1 --pids-limit=64 --tmpfs /tmp:rw,nosuid,nodev,mode=1777,size=64m --volume "${{ github.workspace }}:/workspace:ro" --workdir /workspace node:24.18.0-bookworm-slim node scripts/ql3-local-owner-readonly-contract.cjs --mode=root - name: Exercise Owner authorities as non-root in a read-only container run: >- docker run --rm --read-only --user 65532:65532 --memory=192m --memory-swap=192m --cpus=1 --pids-limit=64 --tmpfs /tmp:rw,nosuid,nodev,mode=1777,size=64m --volume "${{ github.workspace }}:/workspace:ro" --workdir /workspace node:24.18.0-bookworm-slim node scripts/ql3-local-owner-readonly-contract.cjs --mode=nonroot - name: Build and audit production-only local Profile artifacts run: | pnpm audit:artifact:edge:ql3 pnpm audit:artifact:standalone:ql3 pnpm audit:artifact:edge-adopted:ql3 pnpm audit:artifact:standalone-adopted:ql3 pnpm audit:artifact:edge-application:ql3 pnpm audit:artifact:standalone-application:ql3 pnpm audit:artifact:edge-application-api:ql3 pnpm audit:artifact:standalone-application-api:ql3 pnpm audit:artifact:edge-ai:ql3 pnpm audit:artifact:standalone-ai:ql3 cluster-postgres: name: Cluster PostgreSQL ${{ matrix.postgres }} (${{ matrix.arch }}) runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 arch: x64 postgres: '16' - runner: ubuntu-24.04 arch: x64 postgres: '18' - runner: ubuntu-24.04-arm arch: arm64 postgres: '16' - runner: ubuntu-24.04-arm arch: arm64 postgres: '18' services: postgres: image: postgres:${{ matrix.postgres }} env: POSTGRES_DB: ql3_contract POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres ports: - 5432:5432 options: >- --health-cmd "pg_isready -U postgres -d ql3_contract" --health-interval 5s --health-timeout 5s --health-retries 12 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Audit exact cluster dependency graph run: pnpm audit:cluster-dependencies:ql3 - name: Build QL3 workspace once run: pnpm run build:packages:ql3 - name: Test profile-neutral runtime contracts run: node --test packages/ql3-runtime-core/test/*.test.cjs - name: Test optional bounded AI gateway and remote provider contracts run: node --test packages/ql3-ai/test/*.test.cjs - name: Build and test lazy pg.Pool binding run: node --test packages/ql3-cluster-postgres/test/*.test.cjs - name: Test readiness-first cluster composition root run: node --test packages/ql3-cluster-control/test/*.test.cjs - name: Test isolated short-lived cluster administration root run: node --test packages/ql3-cluster-admin/test/*.test.cjs - name: Provision unprivileged database roles before migration run: | docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_migration LOGIN PASSWORD 'ql3_migration_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_runtime LOGIN PASSWORD 'ql3_runtime_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_admin LOGIN PASSWORD 'ql3_admin_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_ai_maintenance LOGIN PASSWORD 'ql3_ai_maintenance_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_ai_credential_manager LOGIN PASSWORD 'ql3_ai_credential_manager_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_ai_credential_tester LOGIN PASSWORD 'ql3_ai_credential_tester_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_automation_manager LOGIN PASSWORD 'ql3_automation_manager_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_approval_manager LOGIN PASSWORD 'ql3_approval_manager_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_run_manager LOGIN PASSWORD 'ql3_run_manager_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_package_manager LOGIN PASSWORD 'ql3_package_manager_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_package_executor LOGIN PASSWORD 'ql3_package_executor_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_worker_credential_manager LOGIN PASSWORD 'ql3_worker_credential_manager_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_worker_credential_executor LOGIN PASSWORD 'ql3_worker_credential_executor_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_worker_ingress LOGIN PASSWORD 'ql3_worker_ingress_test'" docker exec ${{ job.services.postgres.id }} psql -U postgres -d postgres -v ON_ERROR_STOP=1 -c "ALTER DATABASE ql3_contract OWNER TO ql3_migration" - name: Create schema and exact grants through the reviewed migration stream env: QL3_POSTGRES_MIGRATION_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract QL3_POSTGRES_TLS_MODE: disable QL3_POSTGRES_ALLOW_INSECURE: 'true' run: node packages/ql3-cluster-postgres/dist/migration/migrationCli.js - name: Test readiness, isolated roles, shared Repositories, rollback and SQLSTATE mapping env: QL3_TEST_POSTGRES_MIGRATION_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_RUNTIME_URL: postgresql://ql3_runtime:ql3_runtime_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_ADMIN_URL: postgresql://ql3_admin:ql3_admin_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_AUTOMATION_MANAGER_URL: postgresql://ql3_automation_manager:ql3_automation_manager_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_RUN_MANAGER_URL: postgresql://ql3_run_manager:ql3_run_manager_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_PACKAGE_MANAGER_URL: postgresql://ql3_package_manager:ql3_package_manager_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_PACKAGE_EXECUTOR_URL: postgresql://ql3_package_executor:ql3_package_executor_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_WORKER_INGRESS_URL: postgresql://ql3_worker_ingress:ql3_worker_ingress_test@127.0.0.1:5432/ql3_contract run: node --test --test-concurrency=1 packages/ql3-cluster-postgres/test/postgres.integration.test.cjs - name: Test automation management concurrency and post-commit response loss over mTLS env: QL3_TEST_POSTGRES_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_AUTOMATION_MANAGER_URL: postgresql://ql3_automation_manager:ql3_automation_manager_test@127.0.0.1:5432/ql3_contract run: pnpm --filter @qinglong/cluster-admin test:automation-integration - name: Test bootstrap-owned cluster recovery before admission env: QL3_TEST_POSTGRES_MIGRATION_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_RUNTIME_URL: postgresql://ql3_runtime:ql3_runtime_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_FAULT_INJECTION_URL: postgresql://postgres:postgres@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_WORKER_INGRESS_URL: postgresql://ql3_worker_ingress:ql3_worker_ingress_test@127.0.0.1:5432/ql3_contract run: node --test packages/ql3-cluster-control/test/postgres.integration.test.cjs - name: Test optional AI invocation and price catalog against isolated roles env: QL3_TEST_POSTGRES_MIGRATION_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_RUNTIME_URL: postgresql://ql3_runtime:ql3_runtime_test@127.0.0.1:5432/ql3_contract QL3_TEST_POSTGRES_ADMIN_URL: postgresql://ql3_admin:ql3_admin_test@127.0.0.1:5432/ql3_contract run: >- node --test --test-concurrency=1 packages/ql3-ai/test/postgresModelInvocationRepository.integration.test.cjs packages/ql3-ai/test/postgresModelPriceCatalogRepository.integration.test.cjs cluster-postgres-ha: name: PostgreSQL 18 physical promotion (${{ matrix.arch }}) runs-on: ${{ matrix.runner }} timeout-minutes: 15 strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 arch: x64 - runner: ubuntu-24.04-arm arch: arm64 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Verify native runner architecture run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)" - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Audit exact cluster dependency graph run: pnpm audit:cluster-dependencies:ql3 - name: Build QL3 workspace once run: pnpm run build:packages:ql3 - name: Rotate PostgreSQL TLS trust through overlap and rollback run: pnpm test:postgres-tls-rotation:ql3 - name: Fence the old primary, promote the standby and reactivate two controls env: QL3_HA_REPORT: ${{ runner.temp }}/ql3-postgres-ha/report.json run: | install -d -m 0700 "${RUNNER_TEMP}/ql3-postgres-ha" pnpm test:postgres-ha:ql3 pnpm audit:postgres-ha-evidence:ql3 -- \ --report="${RUNNER_TEMP}/ql3-postgres-ha/report.json" - name: Upload PostgreSQL HA evidence if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ql3-postgres-ha-${{ matrix.arch }}-${{ github.run_id }}-${{ github.run_attempt }} path: ${{ runner.temp }}/ql3-postgres-ha/report.json if-no-files-found: warn retention-days: 14 cluster-cloudnativepg-live: name: CloudNativePG live failover contract runs-on: ubuntu-24.04 timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install verified kind v0.31.0 run: | curl --fail --location --silent --show-error \ --output kind-linux-amd64 \ https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64 curl --fail --location --silent --show-error \ --output kind.sha256sum \ https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64.sha256sum sha256sum --check kind.sha256sum chmod +x kind-linux-amd64 - name: Install verified kubectl v1.32.8 run: | curl --fail --location --silent --show-error \ --output kubectl \ https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl curl --fail --location --silent --show-error \ --output kubectl.sha256 \ https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl.sha256 test "$(cat kubectl.sha256)" = "$(sha256sum kubectl | cut -d ' ' -f 1)" chmod +x kubectl - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Build migration evidence dependency run: pnpm run build:packages:ql3 - name: Audit the locked CloudNativePG deployment contract run: | pnpm audit:cloudnativepg:ql3 pnpm audit:cloudnativepg-backup:ql3 pnpm audit:barman-cloud-supply-chain:ql3 pnpm audit:cert-manager-selection:ql3 kubectl kustomize deploy/kubernetes/ql3-cluster/operations/cloudnative-pg-restore >/dev/null - name: Prove operator, roles, migration, runtime and fenced node failover env: QL3_CLOUDNATIVEPG_LIVE: '1' QL3_KIND_BIN: ${{ github.workspace }}/kind-linux-amd64 QL3_KUBECTL_BIN: ${{ github.workspace }}/kubectl QL3_KIND_CLUSTER: ql3-cnpg-ci run: pnpm test:cloudnativepg-live:ql3 cluster-provider-credential-test-kubernetes-live: name: Provider credential test K3s and CloudNativePG live contract runs-on: ubuntu-24.04 timeout-minutes: 60 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install verified kubectl v1.34.3 run: | curl --fail --location --silent --show-error \ --output kubectl \ https://dl.k8s.io/release/v1.34.3/bin/linux/amd64/kubectl curl --fail --location --silent --show-error \ --output kubectl.sha256 \ https://dl.k8s.io/release/v1.34.3/bin/linux/amd64/kubectl.sha256 test "$(cat kubectl.sha256)" = "$(sha256sum kubectl | cut -d ' ' -f 1)" chmod +x kubectl - name: Fetch locked K3s and CloudNativePG release manifest run: | docker pull rancher/k3s:v1.34.3-k3s1 curl --fail --location --silent --show-error \ --output cnpg-1.30.0.yaml \ https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.30.0/cnpg-1.30.0.yaml echo "f8bede43fe4ee0d478c2355b204a36876b2ae4faac60f2a9452280b293da3b88 cnpg-1.30.0.yaml" | sha256sum --check - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Build QL3 workspace once run: pnpm run build:packages:ql3 - name: Audit the static provider credential test deployment run: pnpm audit:provider-credential-test-deployment:ql3 - name: Prove eight isolated Jobs, material and CIDR rotation, and database failover env: QL3_PROVIDER_CREDENTIAL_TEST_KUBERNETES_LIVE: '1' QL3_CNPG_OPERATOR_MANIFEST_FILE: ${{ github.workspace }}/cnpg-1.30.0.yaml QL3_KUBECTL_BIN: ${{ github.workspace }}/kubectl run: pnpm test:provider-credential-test-kubernetes-live:ql3 cluster-plugin-package-kubernetes-live: name: Plugin Package Kubernetes CAS and RBAC live contract runs-on: ubuntu-24.04 timeout-minutes: 20 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' - name: Install verified kind v0.31.0 run: | curl --fail --location --silent --show-error \ --output kind-linux-amd64 \ https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64 curl --fail --location --silent --show-error \ --output kind.sha256sum \ https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64.sha256sum sha256sum --check kind.sha256sum chmod +x kind-linux-amd64 - name: Install verified kubectl v1.32.8 run: | curl --fail --location --silent --show-error \ --output kubectl \ https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl curl --fail --location --silent --show-error \ --output kubectl.sha256 \ https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl.sha256 test "$(cat kubectl.sha256)" = "$(sha256sum kubectl | cut -d ' ' -f 1)" chmod +x kubectl - name: Prove two restricted recovery Pods converge without overreach env: QL3_PLUGIN_PACKAGE_KUBERNETES_LIVE: '1' QL3_KIND_BIN: ${{ github.workspace }}/kind-linux-amd64 QL3_KUBECTL_BIN: ${{ github.workspace }}/kubectl QL3_KIND_CLUSTER: ql3-plugin-activation-ci run: pnpm test:plugin-package-kubernetes-live:ql3 cluster-plugin-package-recovery-e2e: name: Plugin Package PostgreSQL OCI Kubernetes recovery E2E runs-on: ubuntu-24.04 timeout-minutes: 35 steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 with: version: '8.3.1' - uses: actions/setup-node@v6 with: node-version: '24.18.0' cache: pnpm cache-dependency-path: pnpm-lock.yaml - name: Install verified kind v0.31.0 run: | curl --fail --location --silent --show-error \ --output kind-linux-amd64 \ https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64 curl --fail --location --silent --show-error \ --output kind.sha256sum \ https://kind.sigs.k8s.io/dl/v0.31.0/kind-linux-amd64.sha256sum sha256sum --check kind.sha256sum chmod +x kind-linux-amd64 - name: Install verified kubectl v1.32.8 run: | curl --fail --location --silent --show-error \ --output kubectl \ https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl curl --fail --location --silent --show-error \ --output kubectl.sha256 \ https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl.sha256 test "$(cat kubectl.sha256)" = "$(sha256sum kubectl | cut -d ' ' -f 1)" chmod +x kubectl - name: Install workspace dependencies without lifecycle scripts run: pnpm install --frozen-lockfile --ignore-scripts - name: Pull exact PostgreSQL 18.4 fixture image run: >- docker pull postgres:18.4-bookworm@sha256:1961f96e6029a02c3812d7cb329a3b03a3ac2bb067058dec17b0f5596aca9296 - name: Prove migration, durable OCI recovery and rollout ordering env: QL3_PLUGIN_PACKAGE_RECOVERY_E2E_LIVE: '1' QL3_SOURCE_REVISION: ${{ github.sha }} QL3_KIND_BIN: ${{ github.workspace }}/kind-linux-amd64 QL3_KUBECTL_BIN: ${{ github.workspace }}/kubectl QL3_KIND_CLUSTER: ql3-plugin-recovery-e2e-ci run: | install -d -m 0700 "${RUNNER_TEMP}/ql3-plugin-package-recovery-e2e" pnpm test:plugin-package-recovery-e2e:ql3 \ --report="${RUNNER_TEMP}/ql3-plugin-package-recovery-e2e/report.json" pnpm audit:plugin-package-recovery-e2e:ql3 \ --report="${RUNNER_TEMP}/ql3-plugin-package-recovery-e2e/report.json" - name: Upload Plugin Package recovery E2E evidence if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ql3-plugin-package-recovery-e2e-${{ github.run_id }}-${{ github.run_attempt }} path: ${{ runner.temp }}/ql3-plugin-package-recovery-e2e/report.json if-no-files-found: warn retention-days: 14