fix(ql3): stream sparse process logs within exact byte quotas #217
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: QingLong 3.0 CI | |
| on: | |
| push: | |
| branches: | |
| - next | |
| pull_request: | |
| branches: | |
| - next | |
| workflow_dispatch: | |
| inputs: | |
| produce_alpha_artifacts: | |
| description: Package tested native images as commit-bound Alpha artifacts | |
| required: true | |
| default: false | |
| type: boolean | |
| alpha_artifact_scope: | |
| description: Select the product family packaged by an authorized Alpha milestone | |
| required: true | |
| default: local | |
| type: choice | |
| options: | |
| - local | |
| - cluster | |
| - all | |
| local_alpha_variant: | |
| description: Select the Local Alpha deployment surface without changing the default headless image | |
| required: true | |
| default: headless | |
| type: choice | |
| options: | |
| - headless | |
| - console | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ql3-ci-${{ github.workflow }}-${{ github.ref }}-${{ github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && github.run_id || 'validation' }} | |
| cancel-in-progress: ${{ !(github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts) }} | |
| 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 | |
| pnpm audit:security-administration-kubernetes: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 | |
| legacy-panel-compatibility: | |
| name: Legacy panel compatibility build (Node 20 migration toolchain) | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| with: | |
| version: '8.3.1' | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20.20.2' | |
| 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 capability-gated legacy panel source | |
| run: pnpm build:front | |
| - name: Materialize and audit the bounded legacy panel artifact | |
| run: >- | |
| node scripts/ql3-legacy-panel-bundle.cjs | |
| --source="${GITHUB_WORKSPACE}/static/dist" | |
| --output="${RUNNER_TEMP}/ql3-legacy-panel" | |
| - name: Upload the bounded legacy panel artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-legacy-panel-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: ${{ runner.temp }}/ql3-legacy-panel | |
| if-no-files-found: error | |
| retention-days: 14 | |
| compression-level: 0 | |
| overwrite: false | |
| include-hidden-files: false | |
| 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 }}) | |
| needs: legacy-panel-compatibility | |
| 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 | |
| - name: Download the bounded legacy panel artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-legacy-panel-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: .ql3-panel-dist | |
| - 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: Audit the downloaded legacy panel closure | |
| run: node scripts/ql3-legacy-panel-bundle.cjs --audit="${GITHUB_WORKSPACE}/.ql3-panel-dist" | |
| - 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 | |
| node --test test/back/ql3LocalOperatorImageAudit.test.cjs | |
| node --test test/back/ql3LocalAlphaMilestone.test.cjs | |
| pnpm audit:local-image:ql3 | |
| pnpm audit:local-operator-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 | |
| --target runtime | |
| --build-arg SOURCE_REVISION=${{ github.sha }} | |
| --tag "${IMAGE}" | |
| . | |
| - name: Build the opt-in Local Console image | |
| env: | |
| CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} | |
| run: >- | |
| docker build | |
| --file deploy/containers/ql3-local-application/Dockerfile | |
| --target runtime-console | |
| --build-arg SOURCE_REVISION=${{ github.sha }} | |
| --tag "${CONSOLE_IMAGE}" | |
| . | |
| - name: Build the native short-lived Local operator image | |
| env: | |
| OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} | |
| run: >- | |
| docker build | |
| --file deploy/containers/ql3-local-operator/Dockerfile | |
| --build-arg SOURCE_REVISION=${{ github.sha }} | |
| --tag "${OPERATOR_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 | |
| node scripts/ql3-image-os-vulnerability-policy.cjs \ | |
| --image=local-operator \ | |
| --output=${{ runner.temp }}/ql3-local-operator-${{ matrix.image_arch }}.trivyignore.yaml | |
| node scripts/ql3-image-os-vulnerability-policy.cjs \ | |
| --image=local-console \ | |
| --output=${{ runner.temp }}/ql3-local-console-${{ 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: Reject high or critical Local operator OS vulnerabilities | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| version: 'v0.70.0' | |
| image-ref: qinglong3-local-operator: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-operator-${{ matrix.image_arch }}.trivyignore.yaml | |
| - name: Reject unexcepted high or critical Local Console OS vulnerabilities | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| version: 'v0.70.0' | |
| image-ref: qinglong3-local-console: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-console-${{ 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 52 52 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: Verify the short-lived Local operator identity | |
| env: | |
| OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} | |
| EXPECTED: ${{ matrix.image_arch }} 65532:65532 short-lived local-owner-management none-by-default | |
| run: | | |
| set -euo pipefail | |
| actual="$(docker image inspect --format '{{.Architecture}} {{.Config.User}} {{index .Config.Labels "io.qinglong.lifecycle"}} {{index .Config.Labels "io.qinglong.authority"}} {{index .Config.Labels "io.qinglong.network"}}' "${OPERATOR_IMAGE}")" | |
| if [[ "${actual}" != "${EXPECTED}" ]]; then | |
| echo "unexpected Local operator image contract: ${actual}" >&2 | |
| exit 1 | |
| fi | |
| - name: Verify the opt-in Local Console identity | |
| env: | |
| CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} | |
| EXPECTED: ${{ matrix.image_arch }} 65532:65532 edge-application-api,standalone-application-api offline-loopback legacy-capability-gated@v1 256 13631488 | |
| run: | | |
| set -euo pipefail | |
| actual="$(docker image inspect --format '{{.Architecture}} {{.Config.User}} {{index .Config.Labels "io.qinglong.profile"}} {{index .Config.Labels "io.qinglong.local.console"}} {{index .Config.Labels "io.qinglong.local.panel"}} {{index .Config.Labels "io.qinglong.local.panel-max-files"}} {{index .Config.Labels "io.qinglong.local.panel-max-bytes"}}' "${CONSOLE_IMAGE}")" | |
| if [[ "${actual}" != "${EXPECTED}" ]]; then | |
| echo "unexpected Local Console 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: Reconcile the short-lived Local operator package inventory | |
| env: | |
| OPERATOR_IMAGE: qinglong3-local-operator: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 | |
| "${OPERATOR_IMAGE}" | |
| scripts/ql3-local-operator-image-inventory.cjs | |
| --inventory-root=/opt/qinglong/node_modules | |
| - name: Reconcile the opt-in Local Console package inventory | |
| env: | |
| CONSOLE_IMAGE: qinglong3-local-console: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 | |
| "${CONSOLE_IMAGE}" | |
| scripts/ql3-local-console-image-inventory.cjs | |
| --inventory-root=/opt/qinglong/node_modules | |
| - name: Audit the installed legacy panel closure | |
| env: | |
| CONSOLE_IMAGE: qinglong3-local-console: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 | |
| "${CONSOLE_IMAGE}" | |
| scripts/ql3-legacy-panel-bundle.cjs | |
| --audit=/opt/qinglong/node_modules/@qinglong/local-api/assets/panel | |
| - 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: Generate and reconcile the Local operator CycloneDX SBOM | |
| env: | |
| OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} | |
| run: | | |
| node scripts/ql3-cluster-image-sbom.cjs \ | |
| --image=local-operator \ | |
| --output="${{ runner.temp }}/ql3-local-operator.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 \ | |
| "${OPERATOR_IMAGE}" \ | |
| scripts/ql3-cluster-image-sbom.cjs \ | |
| --image=local-operator \ | |
| --inventory-root=/opt/qinglong/node_modules | |
| - name: Generate and reconcile the Local Console CycloneDX SBOM | |
| env: | |
| CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} | |
| run: | | |
| node scripts/ql3-cluster-image-sbom.cjs \ | |
| --image=local-console \ | |
| --output="${{ runner.temp }}/ql3-local-console.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 \ | |
| "${CONSOLE_IMAGE}" \ | |
| scripts/ql3-cluster-image-sbom.cjs \ | |
| --image=local-console \ | |
| --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: Exercise the opt-in Console entrypoint under the router stress envelope | |
| env: | |
| CONSOLE_IMAGE: qinglong3-local-console: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 | |
| "${CONSOLE_IMAGE}" | |
| --help | |
| - name: Exercise the short-lived Local operator entrypoint | |
| env: | |
| OPERATOR_IMAGE: qinglong3-local-operator: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=32 \ | |
| --tmpfs /tmp:rw,nosuid,nodev,noexec,size=8m \ | |
| "${OPERATOR_IMAGE}" --version | |
| 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=32 \ | |
| --tmpfs /tmp:rw,nosuid,nodev,noexec,size=8m \ | |
| "${OPERATOR_IMAGE}" setup --help | |
| docker run --rm --read-only \ | |
| --network none \ | |
| --cap-drop ALL \ | |
| --security-opt no-new-privileges \ | |
| --entrypoint /usr/bin/docker \ | |
| "${OPERATOR_IMAGE}" --version | |
| - name: Verify live sparse output and byte quotas in the installed runtime | |
| env: | |
| IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} | |
| CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} | |
| run: | | |
| for capture_image in "${IMAGE}" "${CONSOLE_IMAGE}"; do | |
| 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 \ | |
| --volume "${GITHUB_WORKSPACE}/packages/ql3-local-process/test:/opt/qinglong/node_modules/@qinglong/local-process/test:ro" \ | |
| --entrypoint node "${capture_image}" \ | |
| --test --test-name-pattern='publishes sparse|keeps byte-exact|rejects unsupported' \ | |
| /opt/qinglong/node_modules/@qinglong/local-process/test/launcher.test.cjs | |
| done | |
| - 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 the downloadable Local Alpha trial kit journey | |
| env: | |
| IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} | |
| OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} | |
| run: | | |
| node scripts/ql3-local-alpha-trial-kit-live-contract.cjs \ | |
| --application-image="${IMAGE}" \ | |
| --operator-image="${OPERATOR_IMAGE}" \ | |
| --profile=edge \ | |
| --variant=headless | |
| node scripts/ql3-local-alpha-trial-kit-live-contract.cjs \ | |
| --application-image="${IMAGE}" \ | |
| --operator-image="${OPERATOR_IMAGE}" \ | |
| --profile=standalone \ | |
| --variant=headless | |
| - name: Run the Local Console loopback journey | |
| env: | |
| IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} | |
| OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} | |
| run: | | |
| node scripts/ql3-local-alpha-trial-kit-live-contract.cjs \ | |
| --application-image="${IMAGE}" \ | |
| --operator-image="${OPERATOR_IMAGE}" \ | |
| --profile=edge \ | |
| --variant=console | |
| # Keep the audited gate-order marker stable; the harness executes the actual panel client. | |
| - 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" | |
| - name: Materialize and offline-audit the native Local Alpha trial kit | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && (inputs.alpha_artifact_scope == 'local' || inputs.alpha_artifact_scope == 'all') | |
| env: | |
| HEADLESS_IMAGE: qinglong3-local-application:ci-${{ matrix.image_arch }} | |
| CONSOLE_IMAGE: qinglong3-local-console:ci-${{ matrix.image_arch }} | |
| OPERATOR_IMAGE: qinglong3-local-operator:ci-${{ matrix.image_arch }} | |
| QL3_LOCAL_DEPLOYMENT_DIAGNOSTICS: '1' | |
| TRIAL_VARIANT: ${{ inputs.local_alpha_variant }} | |
| run: | | |
| set -euo pipefail | |
| umask 077 | |
| if [[ "${TRIAL_VARIANT}" == 'console' ]]; then | |
| IMAGE="${CONSOLE_IMAGE}" | |
| APPLICATION_SBOM="${RUNNER_TEMP}/ql3-local-console.cdx.json" | |
| else | |
| IMAGE="${HEADLESS_IMAGE}" | |
| APPLICATION_SBOM="${RUNNER_TEMP}/ql3-local-application.cdx.json" | |
| fi | |
| BUNDLE_ROOT="${RUNNER_TEMP}/ql3-alpha-local-${TRIAL_VARIANT}-${{ matrix.image_arch }}" | |
| node scripts/ql3-local-alpha-trial-kit-bundle.cjs \ | |
| --mode=record-verification \ | |
| --application-image="${IMAGE}" \ | |
| --operator-image="${OPERATOR_IMAGE}" \ | |
| --architecture=${{ matrix.image_arch }} \ | |
| --variant="${TRIAL_VARIANT}" \ | |
| --source-revision=${{ github.sha }} \ | |
| --repository=${{ github.repository }} \ | |
| --workflow-ref="${{ github.workflow_ref }}" \ | |
| --workflow-sha=${{ github.workflow_sha }} \ | |
| --event=${{ github.event_name }} \ | |
| --job=${{ github.job }} \ | |
| --run-id=${{ github.run_id }} \ | |
| --run-attempt=${{ github.run_attempt }} \ | |
| --output="${RUNNER_TEMP}/ql3-local-alpha-verification-${{ matrix.image_arch }}.json" | |
| node scripts/ql3-local-alpha-trial-kit-bundle.cjs \ | |
| --mode=create \ | |
| --application-image="${IMAGE}" \ | |
| --operator-image="${OPERATOR_IMAGE}" \ | |
| --architecture=${{ matrix.image_arch }} \ | |
| --variant="${TRIAL_VARIANT}" \ | |
| --source-revision=${{ github.sha }} \ | |
| --application-sbom="${APPLICATION_SBOM}" \ | |
| --operator-sbom="${RUNNER_TEMP}/ql3-local-operator.cdx.json" \ | |
| --verification-evidence="${RUNNER_TEMP}/ql3-local-alpha-verification-${{ matrix.image_arch }}.json" \ | |
| --readme=docs/operations/ql3-local-alpha-trial-kit.md \ | |
| --output="${BUNDLE_ROOT}" | |
| node scripts/ql3-local-alpha-trial-kit-bundle.cjs \ | |
| --mode=audit --bundle="${BUNDLE_ROOT}" | |
| QUICKSTART_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-quickstart-${{ matrix.image_arch }}" | |
| QUICKSTART_ROOT="${RUNNER_TEMP}/ql3-alpha-${TRIAL_VARIANT}-quickstart-${{ matrix.image_arch }}" | |
| trap 'docker rm --force "${QUICKSTART_CONTAINER}" >/dev/null 2>&1 || true' EXIT | |
| sh "${BUNDLE_ROOT}/quickstart.sh" \ | |
| edge "${QUICKSTART_ROOT}" "${QUICKSTART_CONTAINER}" | |
| if [[ "${TRIAL_VARIANT}" == 'console' ]]; then | |
| test "$(curl --silent --output /dev/null --write-out '%{http_code}' http://127.0.0.1:5700/)" = '200' | |
| test "$(curl --silent --output /dev/null --write-out '%{http_code}' http://127.0.0.1:5700/api/v3/projects/default/tasks)" = '401' | |
| fi | |
| docker stop --time 30 "${QUICKSTART_CONTAINER}" | |
| docker rm "${QUICKSTART_CONTAINER}" | |
| test -s "${QUICKSTART_ROOT}/qinglong3.sqlite" | |
| test -s "${QUICKSTART_ROOT}/owner-delivery/credential-019f8680-143d-4000-8000-000000000021.ready.json" | |
| LEGACY_READINESS_ROOT="${RUNNER_TEMP}/ql3-alpha-legacy-readiness-${{ matrix.image_arch }}" | |
| UPGRADE_EVIDENCE_ROOT="${RUNNER_TEMP}/ql3-alpha-upgrade-evidence-${{ matrix.image_arch }}" | |
| node scripts/ql3-local-alpha-upgrade-readiness-fixture.cjs \ | |
| --output="${LEGACY_READINESS_ROOT}" | |
| sh "${BUNDLE_ROOT}/upgrade-readiness.sh" \ | |
| edge "${LEGACY_READINESS_ROOT}" "${UPGRADE_EVIDENCE_ROOT}" | |
| grep -q '"status":"inspected"' \ | |
| "${UPGRADE_EVIDENCE_ROOT}/results/sqlite-inspect.result.json" | |
| grep -q '"status":"inspected"' \ | |
| "${UPGRADE_EVIDENCE_ROOT}/results/data-directory-inspect.result.json" | |
| SQLITE_PLAN_DIGEST="$(node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).evidence.planDigest)' \ | |
| "${UPGRADE_EVIDENCE_ROOT}/results/sqlite-inspect.result.json")" | |
| DIRECTORY_PLAN_DIGEST="$(node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).evidence.planDigest)' \ | |
| "${UPGRADE_EVIDENCE_ROOT}/results/data-directory-inspect.result.json")" | |
| UPGRADE_REHEARSAL_ROOT="${RUNNER_TEMP}/ql3-alpha-upgrade-cutover-rehearsal-${{ matrix.image_arch }}" | |
| RECONCILIATION_REHEARSAL_ROOT="${RUNNER_TEMP}/ql3-alpha-upgrade-reconciliation-rehearsal-${{ matrix.image_arch }}" | |
| RECONCILIATION_CAPTURE_ROOT="${RUNNER_TEMP}/ql3-alpha-upgrade-reconciliation-capture-${{ matrix.image_arch }}" | |
| RECONCILIATION_FLOW_ROOT="${RUNNER_TEMP}/ql3-alpha-reconciliation-flow-${{ matrix.image_arch }}" | |
| RECONCILIATION_DECISION_ROOT="${RUNNER_TEMP}/ql3-alpha-reconciliation-decisions-${{ matrix.image_arch }}" | |
| RECONCILIATION_REVIEW_DECISION_ROOT="${RECONCILIATION_DECISION_ROOT}/review" | |
| RECONCILIATION_AUTOMATION_DECISION_ROOT="${RECONCILIATION_DECISION_ROOT}/automation" | |
| COMPLETION_LEGACY_ROOT="${RUNNER_TEMP}/ql3-alpha-completion-legacy-${{ matrix.image_arch }}" | |
| COMPLETION_UPGRADE_EVIDENCE_ROOT="${RUNNER_TEMP}/ql3-alpha-completion-upgrade-evidence-${{ matrix.image_arch }}" | |
| COMPLETION_REHEARSAL_ROOT="${RUNNER_TEMP}/ql3-alpha-completion-rehearsal-${{ matrix.image_arch }}" | |
| COMPLETION_CAPTURE_ROOT="${RUNNER_TEMP}/ql3-alpha-completion-capture-${{ matrix.image_arch }}" | |
| COMPLETION_FLOW_ROOT="${RUNNER_TEMP}/ql3-alpha-completion-flow-${{ matrix.image_arch }}" | |
| COMPLETION_DECISION_ROOT="${RUNNER_TEMP}/ql3-alpha-completion-decisions-${{ matrix.image_arch }}" | |
| COMPLETION_REVIEW_DECISION_ROOT="${COMPLETION_DECISION_ROOT}/review" | |
| COMPLETION_AUTOMATION_DECISION_ROOT="${COMPLETION_DECISION_ROOT}/automation" | |
| COMPLETION_SECRET_CONFIG_DECISION_ROOT="${COMPLETION_DECISION_ROOT}/secret-config" | |
| LEGACY_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-upgrade-legacy-${{ matrix.image_arch }}" | |
| TARGET_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-upgrade-target-${{ matrix.image_arch }}" | |
| RECONCILIATION_LEGACY_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-reconciliation-legacy-${{ matrix.image_arch }}" | |
| RECONCILIATION_TARGET_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-reconciliation-target-${{ matrix.image_arch }}" | |
| COMPLETION_LEGACY_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-completion-legacy-${{ matrix.image_arch }}" | |
| COMPLETION_TARGET_CONTAINER="ql3-alpha-${TRIAL_VARIANT}-completion-target-${{ matrix.image_arch }}" | |
| trap 'docker rm --force "${QUICKSTART_CONTAINER}" "${TARGET_CONTAINER}" "${LEGACY_CONTAINER}" "${RECONCILIATION_TARGET_CONTAINER}" "${RECONCILIATION_LEGACY_CONTAINER}" "${COMPLETION_TARGET_CONTAINER}" "${COMPLETION_LEGACY_CONTAINER}" >/dev/null 2>&1 || true' EXIT | |
| sh "${BUNDLE_ROOT}/upgrade-cutover-rehearsal.sh" \ | |
| edge "${LEGACY_READINESS_ROOT}" "${UPGRADE_REHEARSAL_ROOT}" \ | |
| "${SQLITE_PLAN_DIGEST}" "${DIRECTORY_PLAN_DIGEST}" \ | |
| "${LEGACY_CONTAINER}" "${TARGET_CONTAINER}" | |
| grep -q '"status":"verified"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/stage-summary.json" | |
| grep -q '"legacySource":"read_only"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/stage-summary.json" | |
| grep -q '"cutover":"not_authorized"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/stage-summary.json" | |
| grep -q '"status":"rollback_candidate"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/cutover-summary.json" | |
| grep -q '"variant":"'"${TRIAL_VARIANT}"'"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/cutover-summary.json" | |
| grep -q '"legacySource":"unchanged"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/cutover-summary.json" | |
| grep -q '"target":"stopped"' \ | |
| "${UPGRADE_REHEARSAL_ROOT}/cutover-summary.json" | |
| docker rm "${TARGET_CONTAINER}" "${LEGACY_CONTAINER}" | |
| sh "${BUNDLE_ROOT}/upgrade-cutover-rehearsal.sh" \ | |
| edge "${LEGACY_READINESS_ROOT}" "${RECONCILIATION_REHEARSAL_ROOT}" \ | |
| "${SQLITE_PLAN_DIGEST}" "${DIRECTORY_PLAN_DIGEST}" \ | |
| "${RECONCILIATION_LEGACY_CONTAINER}" "${RECONCILIATION_TARGET_CONTAINER}" \ | |
| --capture-after-write "${RECONCILIATION_CAPTURE_ROOT}" | |
| grep -q '"status":"reconciliation_captured"' \ | |
| "${RECONCILIATION_REHEARSAL_ROOT}/reconciliation-capture-summary.json" | |
| grep -q '"operation":"task.put"' \ | |
| "${RECONCILIATION_REHEARSAL_ROOT}/reconciliation-capture-summary.json" | |
| grep -q '"rollback":"not_authorized"' \ | |
| "${RECONCILIATION_REHEARSAL_ROOT}/reconciliation-capture-summary.json" | |
| grep -q '"next":"review_required"' \ | |
| "${RECONCILIATION_REHEARSAL_ROOT}/reconciliation-capture-summary.json" | |
| grep -q '"status":"verified"' \ | |
| "${RECONCILIATION_REHEARSAL_ROOT}/results/reconciliation-capture-verify.result.json" | |
| test -s "${RECONCILIATION_CAPTURE_ROOT}/019f8680-143d-4000-8000-000000000161/manifest.json" | |
| test -s "${RECONCILIATION_CAPTURE_ROOT}/019f8680-143d-4000-8000-000000000161/receipt.json" | |
| test -d "${RECONCILIATION_CAPTURE_ROOT}/019f8680-143d-4000-8000-000000000161/assets" | |
| install -d -m 0700 \ | |
| "${RECONCILIATION_DECISION_ROOT}" \ | |
| "${RECONCILIATION_REVIEW_DECISION_ROOT}" \ | |
| "${RECONCILIATION_AUTOMATION_DECISION_ROOT}" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| prepare edge "${RECONCILIATION_REHEARSAL_ROOT}" \ | |
| "${RECONCILIATION_CAPTURE_ROOT}" "${RECONCILIATION_FLOW_ROOT}" \ | |
| Asia/Shanghai | |
| grep -q '"status":"operator_decision_required"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"automaticDecision":"not_authorized"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| node scripts/ql3-local-alpha-reconciliation-decision-fixture.cjs \ | |
| --mode=review \ | |
| --reconciliation-root="${RECONCILIATION_FLOW_ROOT}" \ | |
| --output="${RECONCILIATION_REVIEW_DECISION_ROOT}/review.ndjson" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| review edge "${RECONCILIATION_REHEARSAL_ROOT}" \ | |
| "${RECONCILIATION_CAPTURE_ROOT}" "${RECONCILIATION_FLOW_ROOT}" \ | |
| "${RECONCILIATION_REVIEW_DECISION_ROOT}/review.ndjson" | |
| grep -q '"status":"automation_decision_required"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"decisionAuthority":"authenticated_user"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"automaticRowDecision":"not_authorized"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| node scripts/ql3-local-alpha-reconciliation-decision-fixture.cjs \ | |
| --mode=automation \ | |
| --reconciliation-root="${RECONCILIATION_FLOW_ROOT}" \ | |
| --output="${RECONCILIATION_AUTOMATION_DECISION_ROOT}/automation.ndjson" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| apply-rollback edge "${RECONCILIATION_REHEARSAL_ROOT}" \ | |
| "${RECONCILIATION_CAPTURE_ROOT}" "${RECONCILIATION_FLOW_ROOT}" \ | |
| "${RECONCILIATION_AUTOMATION_DECISION_ROOT}/automation.ndjson" \ | |
| "${LEGACY_READINESS_ROOT}" | |
| grep -q '"status":"reconciliation_automation_rolled_back"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"completion":"not_attempted"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"targetRestart":"not_attempted"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"legacyRestart":"not_attempted"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/summary.json" | |
| grep -q '"state":"reconciliation_automation_applied"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/results/automation-apply-verify.result.json" | |
| grep -q '"state":"reconciliation_automation_rolled_back"' \ | |
| "${RECONCILIATION_FLOW_ROOT}/results/automation-rollback-verify.result.json" | |
| docker rm "${RECONCILIATION_TARGET_CONTAINER}" "${RECONCILIATION_LEGACY_CONTAINER}" | |
| test ! -e "${LEGACY_READINESS_ROOT}/db/database.sqlite-wal" | |
| test ! -e "${LEGACY_READINESS_ROOT}/db/database.sqlite-journal" | |
| node scripts/ql3-local-alpha-upgrade-readiness-fixture.cjs \ | |
| --shape=completion-ready \ | |
| --output="${COMPLETION_LEGACY_ROOT}" | |
| sh "${BUNDLE_ROOT}/upgrade-readiness.sh" \ | |
| edge "${COMPLETION_LEGACY_ROOT}" "${COMPLETION_UPGRADE_EVIDENCE_ROOT}" | |
| COMPLETION_SQLITE_PLAN_DIGEST="$(node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).evidence.planDigest)' \ | |
| "${COMPLETION_UPGRADE_EVIDENCE_ROOT}/results/sqlite-inspect.result.json")" | |
| COMPLETION_DIRECTORY_PLAN_DIGEST="$(node -e 'process.stdout.write(JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).evidence.planDigest)' \ | |
| "${COMPLETION_UPGRADE_EVIDENCE_ROOT}/results/data-directory-inspect.result.json")" | |
| sh "${BUNDLE_ROOT}/upgrade-cutover-rehearsal.sh" \ | |
| edge "${COMPLETION_LEGACY_ROOT}" "${COMPLETION_REHEARSAL_ROOT}" \ | |
| "${COMPLETION_SQLITE_PLAN_DIGEST}" "${COMPLETION_DIRECTORY_PLAN_DIGEST}" \ | |
| "${COMPLETION_LEGACY_CONTAINER}" "${COMPLETION_TARGET_CONTAINER}" \ | |
| --capture-after-write "${COMPLETION_CAPTURE_ROOT}" | |
| grep -q '"status":"reconciliation_captured"' \ | |
| "${COMPLETION_REHEARSAL_ROOT}/reconciliation-capture-summary.json" | |
| install -d -m 0700 \ | |
| "${COMPLETION_DECISION_ROOT}" \ | |
| "${COMPLETION_REVIEW_DECISION_ROOT}" \ | |
| "${COMPLETION_AUTOMATION_DECISION_ROOT}" \ | |
| "${COMPLETION_SECRET_CONFIG_DECISION_ROOT}" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| prepare edge "${COMPLETION_REHEARSAL_ROOT}" \ | |
| "${COMPLETION_CAPTURE_ROOT}" "${COMPLETION_FLOW_ROOT}" \ | |
| Asia/Shanghai | |
| node scripts/ql3-local-alpha-reconciliation-decision-fixture.cjs \ | |
| --mode=completion-review \ | |
| --reconciliation-root="${COMPLETION_FLOW_ROOT}" \ | |
| --output="${COMPLETION_REVIEW_DECISION_ROOT}/review.ndjson" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| review edge "${COMPLETION_REHEARSAL_ROOT}" \ | |
| "${COMPLETION_CAPTURE_ROOT}" "${COMPLETION_FLOW_ROOT}" \ | |
| "${COMPLETION_REVIEW_DECISION_ROOT}/review.ndjson" | |
| node scripts/ql3-local-alpha-reconciliation-decision-fixture.cjs \ | |
| --mode=automation \ | |
| --reconciliation-root="${COMPLETION_FLOW_ROOT}" \ | |
| --output="${COMPLETION_AUTOMATION_DECISION_ROOT}/automation.ndjson" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| apply-plan edge "${COMPLETION_REHEARSAL_ROOT}" \ | |
| "${COMPLETION_CAPTURE_ROOT}" "${COMPLETION_FLOW_ROOT}" \ | |
| "${COMPLETION_AUTOMATION_DECISION_ROOT}/automation.ndjson" \ | |
| "${COMPLETION_REVIEW_DECISION_ROOT}/review.ndjson" \ | |
| "${COMPLETION_LEGACY_ROOT}" | |
| grep -q '"status":"secret_config_decision_required"' \ | |
| "${COMPLETION_FLOW_ROOT}/summary.json" | |
| grep -q '"automaticCandidateDecision":"not_authorized"' \ | |
| "${COMPLETION_FLOW_ROOT}/summary.json" | |
| grep -q '"state":"reconciliation_run_history_preserved"' \ | |
| "${COMPLETION_FLOW_ROOT}/results/run-history-verify.result.json" | |
| node scripts/ql3-local-alpha-reconciliation-decision-fixture.cjs \ | |
| --mode=secret-config \ | |
| --reconciliation-root="${COMPLETION_FLOW_ROOT}" \ | |
| --output="${COMPLETION_SECRET_CONFIG_DECISION_ROOT}/secret-config.ndjson" | |
| sh "${BUNDLE_ROOT}/reconciliation-rehearsal.sh" \ | |
| complete edge "${COMPLETION_REHEARSAL_ROOT}" \ | |
| "${COMPLETION_CAPTURE_ROOT}" "${COMPLETION_FLOW_ROOT}" \ | |
| "${COMPLETION_SECRET_CONFIG_DECISION_ROOT}/secret-config.ndjson" \ | |
| "${COMPLETION_REVIEW_DECISION_ROOT}/review.ndjson" \ | |
| "${COMPLETION_LEGACY_ROOT}" | |
| grep -q '"status":"reconciliation_completed"' \ | |
| "${COMPLETION_FLOW_ROOT}/summary.json" | |
| grep -q '"adapterCount":3' \ | |
| "${COMPLETION_FLOW_ROOT}/summary.json" | |
| grep -q '"targetRestart":"not_authorized"' \ | |
| "${COMPLETION_FLOW_ROOT}/summary.json" | |
| grep -q '"legacyRestart":"not_authorized"' \ | |
| "${COMPLETION_FLOW_ROOT}/summary.json" | |
| grep -q '"status":"verified"' \ | |
| "${COMPLETION_FLOW_ROOT}/results/completion-verify.result.json" | |
| docker rm "${COMPLETION_TARGET_CONTAINER}" "${COMPLETION_LEGACY_CONTAINER}" | |
| test ! -e "${COMPLETION_LEGACY_ROOT}/db/database.sqlite-wal" | |
| test ! -e "${COMPLETION_LEGACY_ROOT}/db/database.sqlite-journal" | |
| trap - EXIT | |
| - name: Upload the tested native Local Alpha trial kit | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && (inputs.alpha_artifact_scope == 'local' || inputs.alpha_artifact_scope == 'all') | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-local-${{ inputs.local_alpha_variant }}-${{ matrix.image_arch }} | |
| path: ${{ runner.temp }}/ql3-alpha-local-${{ inputs.local_alpha_variant }}-${{ matrix.image_arch }} | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 0 | |
| overwrite: false | |
| include-hidden-files: false | |
| 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 | |
| - name: Package the tested native Cluster Alpha image | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && (inputs.alpha_artifact_scope == 'cluster' || inputs.alpha_artifact_scope == 'all') | |
| env: | |
| IMAGE: ${{ matrix.repository }}:ci-${{ matrix.image_arch }} | |
| BUNDLE_ROOT: ${{ runner.temp }}/ql3-alpha-${{ matrix.image }}-${{ matrix.image_arch }} | |
| run: | | |
| set -euo pipefail | |
| umask 077 | |
| node scripts/ql3-cluster-alpha-bundle.cjs \ | |
| --mode=record-verification \ | |
| --role=${{ matrix.image }} \ | |
| --architecture=${{ matrix.image_arch }} \ | |
| --image="${IMAGE}" \ | |
| --source-revision=${{ github.sha }} \ | |
| --repository=${{ github.repository }} \ | |
| --workflow-ref="${{ github.workflow_ref }}" \ | |
| --workflow-sha=${{ github.workflow_sha }} \ | |
| --event=${{ github.event_name }} \ | |
| --job=cluster-image \ | |
| --run-id=${{ github.run_id }} \ | |
| --run-attempt=${{ github.run_attempt }} \ | |
| --output="${RUNNER_TEMP}/ql3-cluster-${{ matrix.image }}-${{ matrix.image_arch }}-verification.json" | |
| node scripts/ql3-cluster-alpha-bundle.cjs \ | |
| --mode=create \ | |
| --role=${{ matrix.image }} \ | |
| --architecture=${{ matrix.image_arch }} \ | |
| --image="${IMAGE}" \ | |
| --source-revision=${{ github.sha }} \ | |
| --sbom="${RUNNER_TEMP}/ql3-cluster-${{ matrix.image }}.cdx.json" \ | |
| --verification-evidence="${RUNNER_TEMP}/ql3-cluster-${{ matrix.image }}-${{ matrix.image_arch }}-verification.json" \ | |
| --readme=docs/operations/ql3-alpha-candidate.md \ | |
| --output="${BUNDLE_ROOT}" | |
| node scripts/ql3-cluster-alpha-bundle.cjs \ | |
| --mode=offline-audit \ | |
| --bundle="${BUNDLE_ROOT}" | |
| - name: Upload the tested native Cluster Alpha image | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && (inputs.alpha_artifact_scope == 'cluster' || inputs.alpha_artifact_scope == 'all') | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-${{ matrix.image }}-${{ matrix.image_arch }} | |
| path: ${{ runner.temp }}/ql3-alpha-${{ matrix.image }}-${{ matrix.image_arch }} | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 0 | |
| overwrite: false | |
| include-hidden-files: false | |
| 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: local-operator | |
| dockerfile: deploy/containers/ql3-local-operator/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-secret-binding-mounted-provider-kubernetes-live: | |
| name: Secret binding approval and mounted provider rotation live contract | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 40 | |
| 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: Preload digest-bound K3s and PostgreSQL fixtures | |
| run: | | |
| docker pull rancher/k3s@sha256:71abd3a56f57884c62732e0e0d87606052cb5f8555b7db7e8e33c04570b8175c | |
| docker tag \ | |
| rancher/k3s@sha256:71abd3a56f57884c62732e0e0d87606052cb5f8555b7db7e8e33c04570b8175c \ | |
| rancher/k3s:v1.34.3-k3s1 | |
| docker pull postgres@sha256:1961f96e6029a02c3812d7cb329a3b03a3ac2bb067058dec17b0f5596aca9296 | |
| docker tag \ | |
| postgres@sha256:1961f96e6029a02c3812d7cb329a3b03a3ac2bb067058dec17b0f5596aca9296 \ | |
| postgres:18.4-bookworm | |
| - 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: Verify the backward-compatible offline evidence contract | |
| run: node --test test/back/ql3PluginPackageSecretBindingKubernetesLiveAudit.test.cjs | |
| - name: Prove approval, two-replica rotation and missing projection fail-close | |
| env: | |
| QL3_PLUGIN_PACKAGE_SECRET_BINDING_KUBERNETES_LIVE: '1' | |
| QL3_KUBECTL_BIN: ${{ github.workspace }}/kubectl | |
| QL3_SECRET_BINDING_LIVE_REPORT: ${{ runner.temp }}/ql3-secret-binding/report.json | |
| run: | | |
| umask 077 | |
| mkdir -m 0700 "$(dirname "${QL3_SECRET_BINDING_LIVE_REPORT}")" | |
| pnpm test:plugin-package-secret-binding-kubernetes-live:ql3 \ | |
| "--report=${QL3_SECRET_BINDING_LIVE_REPORT}" | |
| - name: Re-audit content-free evidence and isolated cleanup | |
| env: | |
| QL3_SECRET_BINDING_LIVE_REPORT: ${{ runner.temp }}/ql3-secret-binding/report.json | |
| run: | | |
| test "$(stat -c '%a' "${QL3_SECRET_BINDING_LIVE_REPORT}")" = '600' | |
| pnpm audit:plugin-package-secret-binding-kubernetes-live:ql3 \ | |
| "--report=${QL3_SECRET_BINDING_LIVE_REPORT}" | |
| sha256sum "${QL3_SECRET_BINDING_LIVE_REPORT}" | |
| test -z "$(docker ps -aq --filter name=ql3-secret-binding-live-)" | |
| - name: Upload audited content-free mounted provider evidence | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-secret-binding-provider-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: ${{ runner.temp }}/ql3-secret-binding/report.json | |
| if-no-files-found: error | |
| retention-days: 14 | |
| compression-level: 9 | |
| overwrite: false | |
| include-hidden-files: false | |
| cluster-vault-kv-worker-secret-live: | |
| name: Vault KV Worker Secret direct custody (${{ matrix.arch }}) | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: ubuntu-24.04 | |
| arch: x64 | |
| kubernetesArch: amd64 | |
| - runner: ubuntu-24.04-arm | |
| arch: arm64 | |
| kubernetesArch: 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: Install verified kubectl v1.34.3 | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| curl --fail --location --silent --show-error \ | |
| --output "${RUNNER_TEMP}/kubectl" \ | |
| "https://dl.k8s.io/release/v1.34.3/bin/linux/${{ matrix.kubernetesArch }}/kubectl" | |
| curl --fail --location --silent --show-error \ | |
| --output "${RUNNER_TEMP}/kubectl.sha256" \ | |
| "https://dl.k8s.io/release/v1.34.3/bin/linux/${{ matrix.kubernetesArch }}/kubectl.sha256" | |
| test "$(cat "${RUNNER_TEMP}/kubectl.sha256")" = \ | |
| "$(sha256sum "${RUNNER_TEMP}/kubectl" | cut -d ' ' -f 1)" | |
| chmod 0755 "${RUNNER_TEMP}/kubectl" | |
| - name: Build the direct custody adapter | |
| run: >- | |
| pnpm -r --workspace-concurrency=1 | |
| --filter @qinglong/cluster-control... | |
| run build | |
| - name: Verify the fail-closed adapter and evidence contracts | |
| run: | | |
| node --test \ | |
| packages/ql3-cluster-control/test/vaultKvSecretProvider.test.cjs \ | |
| packages/ql3-cluster-control/test/vaultKvWorkerIngressConfig.test.cjs \ | |
| test/back/ql3VaultKvWorkerSecretLiveAudit.test.cjs \ | |
| test/back/ql3VaultKvWorkerSecretDeploymentAudit.test.cjs | |
| pnpm audit:vault-kv-worker-secret-deployment:ql3 | |
| "${RUNNER_TEMP}/kubectl" kustomize \ | |
| deploy/kubernetes/ql3-cluster/vault-kv-worker-secret \ | |
| >/dev/null | |
| - name: Preload the digest-pinned native Vault image | |
| run: docker pull docker.io/hashicorp/vault@sha256:4e33b126a59c0c333b76fb4e894722462659a6bec7c48c9ee8cea56fccfd2569 | |
| - name: Prove TLS, least privilege, value and token rotation, seal and recovery | |
| env: | |
| QL3_RUN_VAULT_KV_WORKER_SECRET_LIVE: 'true' | |
| QL3_VAULT_KV_WORKER_SECRET_REPORT: ${{ runner.temp }}/ql3-vault-kv-worker-secret/report.json | |
| run: | | |
| umask 077 | |
| mkdir -m 0700 "$(dirname "${QL3_VAULT_KV_WORKER_SECRET_REPORT}")" | |
| node scripts/ql3-vault-kv-worker-secret-live-contract.cjs >/dev/null | |
| - name: Re-audit content-free direct custody evidence and cleanup | |
| env: | |
| QL3_VAULT_KV_WORKER_SECRET_REPORT: ${{ runner.temp }}/ql3-vault-kv-worker-secret/report.json | |
| run: | | |
| test "$(stat -c '%a' "${QL3_VAULT_KV_WORKER_SECRET_REPORT}")" = '600' | |
| pnpm audit:vault-kv-worker-secret-live:ql3 \ | |
| "--report=${QL3_VAULT_KV_WORKER_SECRET_REPORT}" | |
| sha256sum "${QL3_VAULT_KV_WORKER_SECRET_REPORT}" | |
| test -z "$(docker ps -aq --filter name=ql3-vault-kv-worker-)" | |
| - name: Upload audited content-free Vault KV custody evidence | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-vault-kv-worker-secret-${{ matrix.arch }}-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: ${{ runner.temp }}/ql3-vault-kv-worker-secret/report.json | |
| if-no-files-found: error | |
| retention-days: 14 | |
| compression-level: 9 | |
| overwrite: false | |
| include-hidden-files: false | |
| 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 | |
| cluster-alpha-milestone: | |
| name: Finalize the Cluster Alpha integration milestone | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && (inputs.alpha_artifact_scope == 'cluster' || inputs.alpha_artifact_scope == 'all') | |
| needs: | |
| - backend | |
| - service-manager-bridge | |
| - linux-resource-envelopes | |
| - linux-resource-release-evidence | |
| - supply-chain | |
| - local-image | |
| - cluster-image | |
| - cluster-console-capacity-release-evidence | |
| - image-oci | |
| - worker-runtime | |
| - local-profiles | |
| - cluster-postgres | |
| - cluster-postgres-ha | |
| - cluster-cloudnativepg-live | |
| - cluster-provider-credential-test-kubernetes-live | |
| - cluster-secret-binding-mounted-provider-kubernetes-live | |
| - cluster-vault-kv-worker-secret-live | |
| - cluster-plugin-package-kubernetes-live | |
| - cluster-plugin-package-recovery-e2e | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| - 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: Download the exact control amd64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-control-amd64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/control-amd64 | |
| - name: Download the exact control arm64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-control-arm64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/control-arm64 | |
| - name: Download the exact control-ai amd64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-control-ai-amd64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/control-ai-amd64 | |
| - name: Download the exact control-ai arm64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-control-ai-arm64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/control-ai-arm64 | |
| - name: Download the exact admin amd64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-admin-amd64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/admin-amd64 | |
| - name: Download the exact admin arm64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-admin-arm64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/admin-arm64 | |
| - name: Download the exact worker amd64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-worker-amd64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/worker-amd64 | |
| - name: Download the exact worker arm64 Cluster Alpha bundle | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-worker-arm64 | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone-input/worker-arm64 | |
| - name: Close all native Cluster bundles into one milestone index | |
| run: | | |
| set -euo pipefail | |
| umask 077 | |
| node scripts/ql3-cluster-alpha-milestone.cjs \ | |
| --mode=finalize \ | |
| --control-amd64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/control-amd64" \ | |
| --control-arm64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/control-arm64" \ | |
| --control-ai-amd64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/control-ai-amd64" \ | |
| --control-ai-arm64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/control-ai-arm64" \ | |
| --admin-amd64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/admin-amd64" \ | |
| --admin-arm64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/admin-arm64" \ | |
| --worker-amd64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/worker-amd64" \ | |
| --worker-arm64-bundle="${RUNNER_TEMP}/ql3-alpha-cluster-milestone-input/worker-arm64" \ | |
| --source-revision=${{ github.sha }} \ | |
| --repository=${{ github.repository }} \ | |
| --workflow-ref="${{ github.workflow_ref }}" \ | |
| --workflow-sha=${{ github.workflow_sha }} \ | |
| --event=${{ github.event_name }} \ | |
| --run-id=${{ github.run_id }} \ | |
| --run-attempt=${{ github.run_attempt }} \ | |
| --readme=docs/operations/ql3-cluster-alpha-milestone.md \ | |
| --output="${RUNNER_TEMP}/ql3-alpha-cluster-milestone" | |
| node scripts/ql3-cluster-alpha-milestone.cjs \ | |
| --mode=audit \ | |
| --milestone="${RUNNER_TEMP}/ql3-alpha-cluster-milestone" | |
| - name: Upload the closed Cluster Alpha milestone index | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-cluster-milestone | |
| path: ${{ runner.temp }}/ql3-alpha-cluster-milestone | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 0 | |
| overwrite: false | |
| include-hidden-files: false | |
| local-alpha-milestone: | |
| name: Finalize the Local Alpha milestone | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && (inputs.alpha_artifact_scope == 'local' || inputs.alpha_artifact_scope == 'all') | |
| needs: | |
| - backend | |
| - legacy-panel-compatibility | |
| - service-manager-bridge | |
| - linux-resource-envelopes | |
| - linux-resource-release-evidence | |
| - supply-chain | |
| - local-image | |
| - cluster-image | |
| - cluster-console-capacity-release-evidence | |
| - image-oci | |
| - worker-runtime | |
| - local-profiles | |
| - cluster-postgres | |
| - cluster-postgres-ha | |
| - cluster-cloudnativepg-live | |
| - cluster-provider-credential-test-kubernetes-live | |
| - cluster-secret-binding-mounted-provider-kubernetes-live | |
| - cluster-vault-kv-worker-secret-live | |
| - cluster-plugin-package-kubernetes-live | |
| - cluster-plugin-package-recovery-e2e | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| - 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: Download the exact amd64 Local Alpha trial kit | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-local-${{ inputs.local_alpha_variant }}-amd64 | |
| path: ${{ runner.temp }}/ql3-alpha-local-milestone-input/amd64 | |
| - name: Download the exact arm64 Local Alpha trial kit | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-local-${{ inputs.local_alpha_variant }}-arm64 | |
| path: ${{ runner.temp }}/ql3-alpha-local-milestone-input/arm64 | |
| - name: Close both native trial kits into one milestone index | |
| run: | | |
| set -euo pipefail | |
| umask 077 | |
| node scripts/ql3-local-alpha-milestone.cjs \ | |
| --mode=finalize \ | |
| --amd64-bundle="${RUNNER_TEMP}/ql3-alpha-local-milestone-input/amd64" \ | |
| --arm64-bundle="${RUNNER_TEMP}/ql3-alpha-local-milestone-input/arm64" \ | |
| --variant=${{ inputs.local_alpha_variant }} \ | |
| --source-revision=${{ github.sha }} \ | |
| --repository=${{ github.repository }} \ | |
| --workflow-ref="${{ github.workflow_ref }}" \ | |
| --workflow-sha=${{ github.workflow_sha }} \ | |
| --event=${{ github.event_name }} \ | |
| --run-id=${{ github.run_id }} \ | |
| --run-attempt=${{ github.run_attempt }} \ | |
| --readme=docs/operations/ql3-local-alpha-milestone.md \ | |
| --output="${RUNNER_TEMP}/ql3-alpha-local-milestone" | |
| node scripts/ql3-local-alpha-milestone.cjs \ | |
| --mode=audit \ | |
| --milestone="${RUNNER_TEMP}/ql3-alpha-local-milestone" | |
| - name: Upload the closed Local Alpha milestone index | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-local-${{ inputs.local_alpha_variant }}-milestone | |
| path: ${{ runner.temp }}/ql3-alpha-local-milestone | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 0 | |
| overwrite: false | |
| include-hidden-files: false | |
| alpha-stage-index: | |
| name: Finalize the cross-profile Alpha stage index | |
| if: github.event_name == 'workflow_dispatch' && inputs.produce_alpha_artifacts && inputs.alpha_artifact_scope == 'all' | |
| needs: | |
| - local-alpha-milestone | |
| - cluster-alpha-milestone | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| - 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: Download the closed Local Alpha milestone | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-local-${{ inputs.local_alpha_variant }}-milestone | |
| path: ${{ runner.temp }}/ql3-alpha-stage-input/local | |
| - name: Download the closed Cluster Alpha milestone | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-cluster-milestone | |
| path: ${{ runner.temp }}/ql3-alpha-stage-input/cluster | |
| - name: Close both deployment classes into one stage index | |
| run: | | |
| set -euo pipefail | |
| umask 077 | |
| node scripts/ql3-alpha-stage-index.cjs \ | |
| --mode=finalize \ | |
| --local-milestone="${RUNNER_TEMP}/ql3-alpha-stage-input/local" \ | |
| --cluster-milestone="${RUNNER_TEMP}/ql3-alpha-stage-input/cluster" \ | |
| --source-revision=${{ github.sha }} \ | |
| --repository=${{ github.repository }} \ | |
| --workflow-ref="${{ github.workflow_ref }}" \ | |
| --workflow-sha=${{ github.workflow_sha }} \ | |
| --event=${{ github.event_name }} \ | |
| --run-id=${{ github.run_id }} \ | |
| --run-attempt=${{ github.run_attempt }} \ | |
| --readme=docs/operations/ql3-alpha-stage-index.md \ | |
| --output="${RUNNER_TEMP}/ql3-alpha-stage-index" | |
| node scripts/ql3-alpha-stage-index.cjs \ | |
| --mode=audit \ | |
| --stage="${RUNNER_TEMP}/ql3-alpha-stage-index" \ | |
| --local-milestone="${RUNNER_TEMP}/ql3-alpha-stage-input/local" \ | |
| --cluster-milestone="${RUNNER_TEMP}/ql3-alpha-stage-input/cluster" | |
| - name: Upload the closed cross-profile Alpha stage index | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ql3-alpha-${{ github.sha }}-stage-index | |
| path: ${{ runner.temp }}/ql3-alpha-stage-index | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 0 | |
| overwrite: false | |
| include-hidden-files: false |