mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
2011 lines
92 KiB
YAML
2011 lines
92 KiB
YAML
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
|
|
|
|
service-manager-bridge:
|
|
name: systemd/OpenRC dual-authority bridge
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: '8.3.1'
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: '24.18.0'
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
- name: Install workspace dependencies without lifecycle scripts
|
|
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
- name: Build the complete local test fixture workspace
|
|
run: pnpm run build:packages:ql3
|
|
- name: Build and test Local Owner product closure
|
|
run: pnpm --filter @qinglong/local-owner-cli test
|
|
- name: Audit root bridge static import closure
|
|
run: pnpm audit:service-manager-bridge-imports:ql3
|
|
- name: Run real systemd/OpenRC root and non-root bridge gate
|
|
run: pnpm test:service-manager-bridge:ql3
|
|
|
|
linux-resource-envelopes:
|
|
name: Linux resource envelopes (${{ matrix.arch }}, Node 24)
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: ubuntu-24.04
|
|
arch: x64
|
|
- runner: ubuntu-24.04-arm
|
|
arch: arm64
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: '8.3.1'
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: '24.18.0'
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
- name: Verify native runner architecture
|
|
run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)"
|
|
- name: Install workspace dependencies without lifecycle scripts
|
|
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
- name: Initialize the public test environment
|
|
run: |
|
|
cp .env.example .env
|
|
mkdir -p data/db
|
|
- name: Rebuild the reviewed native SQLite binding
|
|
run: pnpm rebuild @whyour/sqlite3
|
|
- name: Build benchmark targets once
|
|
run: |
|
|
pnpm build:back
|
|
pnpm run build:packages:ql3
|
|
- name: Test fail-closed Linux resource gate
|
|
run: node --test test/back/ql3LinuxResourceGate.test.cjs test/back/ql3LinuxResourceWorkflow.test.cjs test/back/ql3LegacyShadowResourceRollback.test.cjs test/back/ql3PhysicalEdgeEvidence.test.cjs test/back/ql3PhysicalEdgeIdleSampler.test.cjs test/back/ql3PhysicalEdgeFaultProbe.test.cjs test/back/ql3PhysicalEdgeTaskScale.test.cjs
|
|
- name: Prepare architecture evidence directory
|
|
run: mkdir -p "${RUNNER_TEMP}/ql3-linux-resource-evidence"
|
|
- name: Stress router headroom without claiming a supported minimum
|
|
run: >-
|
|
docker run --rm --read-only
|
|
--security-opt no-new-privileges
|
|
--user 65532:65532
|
|
--memory=128m
|
|
--memory-swap=128m
|
|
--cpus=0.5
|
|
--pids-limit=64
|
|
--tmpfs /tmp:rw,nosuid,nodev,noexec,mode=1777,size=64m
|
|
--volume "${{ github.workspace }}:/workspace:ro"
|
|
--workdir /workspace
|
|
node:24.18.0-bookworm-slim
|
|
node scripts/ql3-linux-resource-gate.cjs
|
|
--tier=router-stress-ci
|
|
--expected-arch=${{ matrix.arch }}
|
|
--json
|
|
> "${RUNNER_TEMP}/ql3-linux-resource-evidence/router-stress-ci.json"
|
|
- name: Enforce the emulated edge release envelope
|
|
run: >-
|
|
docker run --rm --read-only
|
|
--security-opt no-new-privileges
|
|
--user 65532:65532
|
|
--memory=256m
|
|
--memory-swap=256m
|
|
--cpus=1
|
|
--pids-limit=128
|
|
--tmpfs /tmp:rw,nosuid,nodev,noexec,mode=1777,size=64m
|
|
--volume "${{ github.workspace }}:/workspace:ro"
|
|
--workdir /workspace
|
|
node:24.18.0-bookworm-slim
|
|
node scripts/ql3-linux-resource-gate.cjs
|
|
--tier=edge-release-ci
|
|
--expected-arch=${{ matrix.arch }}
|
|
--json
|
|
> "${RUNNER_TEMP}/ql3-linux-resource-evidence/edge-release-ci.json"
|
|
- name: Enforce the cluster control-plane guard independently
|
|
run: >-
|
|
docker run --rm --read-only
|
|
--security-opt no-new-privileges
|
|
--user 65532:65532
|
|
--memory=512m
|
|
--memory-swap=512m
|
|
--cpus=2
|
|
--pids-limit=256
|
|
--tmpfs /tmp:rw,nosuid,nodev,noexec,mode=1777,size=32m
|
|
--volume "${{ github.workspace }}:/workspace:ro"
|
|
--workdir /workspace
|
|
node:24.18.0-bookworm-slim
|
|
node scripts/ql3-linux-resource-gate.cjs
|
|
--tier=cluster-control-ci
|
|
--expected-arch=${{ matrix.arch }}
|
|
--json
|
|
> "${RUNNER_TEMP}/ql3-linux-resource-evidence/cluster-control-ci.json"
|
|
- name: Bind native resource evidence to this workflow run
|
|
env:
|
|
SOURCE_REPOSITORY: ${{ github.repository }}
|
|
SOURCE_REVISION: ${{ github.sha }}
|
|
SOURCE_WORKFLOW: ${{ github.workflow }}
|
|
SOURCE_RUN_ID: ${{ github.run_id }}
|
|
SOURCE_RUN_ATTEMPT: ${{ github.run_attempt }}
|
|
run: >-
|
|
node scripts/ql3-linux-resource-release-evidence.cjs
|
|
--mode=bundle
|
|
--repository="${SOURCE_REPOSITORY}"
|
|
--revision="${SOURCE_REVISION}"
|
|
--workflow="${SOURCE_WORKFLOW}"
|
|
--run-id="${SOURCE_RUN_ID}"
|
|
--run-attempt="${SOURCE_RUN_ATTEMPT}"
|
|
--architecture="${{ matrix.arch }}"
|
|
--router-stress-ci="${RUNNER_TEMP}/ql3-linux-resource-evidence/router-stress-ci.json"
|
|
--edge-release-ci="${RUNNER_TEMP}/ql3-linux-resource-evidence/edge-release-ci.json"
|
|
--cluster-control-ci="${RUNNER_TEMP}/ql3-linux-resource-evidence/cluster-control-ci.json"
|
|
--output="${RUNNER_TEMP}/ql3-linux-resource-evidence/${{ matrix.arch }}.json"
|
|
- name: Upload native resource evidence
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: ql3-linux-resource-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.arch }}
|
|
path: ${{ runner.temp }}/ql3-linux-resource-evidence/${{ matrix.arch }}.json
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
compression-level: 0
|
|
overwrite: false
|
|
include-hidden-files: false
|
|
|
|
linux-resource-release-evidence:
|
|
name: Cross-architecture Linux resource release evidence
|
|
needs: linux-resource-envelopes
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- name: Download native x64 resource evidence
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: ql3-linux-resource-${{ github.run_id }}-${{ github.run_attempt }}-x64
|
|
path: ${{ runner.temp }}/ql3-linux-resource-evidence/x64
|
|
- name: Download native arm64 resource evidence
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: ql3-linux-resource-${{ github.run_id }}-${{ github.run_attempt }}-arm64
|
|
path: ${{ runner.temp }}/ql3-linux-resource-evidence/arm64
|
|
- name: Merge source-bound cross-architecture evidence
|
|
env:
|
|
SOURCE_REPOSITORY: ${{ github.repository }}
|
|
SOURCE_REVISION: ${{ github.sha }}
|
|
SOURCE_WORKFLOW: ${{ github.workflow }}
|
|
SOURCE_RUN_ID: ${{ github.run_id }}
|
|
SOURCE_RUN_ATTEMPT: ${{ github.run_attempt }}
|
|
run: >-
|
|
node scripts/ql3-linux-resource-release-evidence.cjs
|
|
--mode=merge
|
|
--repository="${SOURCE_REPOSITORY}"
|
|
--revision="${SOURCE_REVISION}"
|
|
--workflow="${SOURCE_WORKFLOW}"
|
|
--run-id="${SOURCE_RUN_ID}"
|
|
--run-attempt="${SOURCE_RUN_ATTEMPT}"
|
|
--x64="${RUNNER_TEMP}/ql3-linux-resource-evidence/x64/x64.json"
|
|
--arm64="${RUNNER_TEMP}/ql3-linux-resource-evidence/arm64/arm64.json"
|
|
--output="${RUNNER_TEMP}/ql3-linux-resource-evidence/cross-architecture.json"
|
|
- name: Upload cross-architecture resource release evidence
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: ql3-linux-resource-release-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: ${{ runner.temp }}/ql3-linux-resource-evidence/cross-architecture.json
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
compression-level: 0
|
|
overwrite: false
|
|
include-hidden-files: false
|
|
|
|
supply-chain:
|
|
name: Profile supply-chain gate
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: '8.3.1'
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: '24.18.0'
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
- name: Install workspace dependencies without lifecycle scripts
|
|
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
- name: Test bounded fail-closed audit classifier
|
|
run: >-
|
|
node --test
|
|
test/back/ql3ProfileVulnerabilityAudit.test.cjs
|
|
test/back/ql3ClusterImageSbom.test.cjs
|
|
test/back/ql3ClusterImageReleaseAudit.test.cjs
|
|
test/back/ql3ReleaseCandidateContract.test.cjs
|
|
test/back/ql3PrivateReleaseEvidenceReceiptContract.test.cjs
|
|
test/back/ql3ReleaseSetContract.test.cjs
|
|
test/back/ql3ReleaseCatalogContract.test.cjs
|
|
test/back/ql3ReleaseDeploymentReadinessContract.test.cjs
|
|
test/back/ql3ReleaseTagFinalizer.test.cjs
|
|
test/back/ql3ReleasePublicationClosureContract.test.cjs
|
|
test/back/ql3ReleaseCatalogConsumptionCeremony.test.cjs
|
|
test/back/ql3DeploymentLockContract.test.cjs
|
|
test/back/ql3ImageOsVulnerabilityPolicy.test.cjs
|
|
test/back/ql3LocalImageAudit.test.cjs
|
|
test/back/ql3VersionTransition.test.cjs
|
|
- name: Audit cluster image release contract
|
|
run: |
|
|
pnpm audit:release-version:ql3
|
|
pnpm audit:image-release:ql3
|
|
pnpm audit:deployment-lock-surfaces:ql3
|
|
pnpm audit:local-image:ql3
|
|
- name: Reject high or critical advisories in 3.0 Profile importers
|
|
run: pnpm audit:profiles:ql3
|
|
|
|
local-image:
|
|
name: Local application image (${{ matrix.image_arch }})
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: ubuntu-24.04
|
|
node_arch: x64
|
|
image_arch: amd64
|
|
- runner: ubuntu-24.04-arm
|
|
node_arch: arm64
|
|
image_arch: arm64
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: '8.3.1'
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version: '24.18.0'
|
|
cache: pnpm
|
|
cache-dependency-path: pnpm-lock.yaml
|
|
- name: Verify native runner architecture
|
|
run: node -e "if (process.arch !== '${{ matrix.node_arch }}') throw new Error('unexpected architecture ' + process.arch)"
|
|
- name: Install workspace dependencies without lifecycle scripts
|
|
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
- name: Audit immutable local image inputs
|
|
run: |
|
|
node --test test/back/ql3LocalImageAudit.test.cjs
|
|
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
|
|
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"}}' "${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: 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
|
|
- 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
|
|
- 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 }}
|
|
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"
|
|
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
|
|
- 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
|