mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
name: QingLong 3.0 Approval Management Kubernetes live evidence
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ql3-approval-management-live-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
approval-management-live:
|
||||
name: Three-node Approval, OIDC, mTLS and CloudNativePG
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
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: Require enough ephemeral disk for three isolated K3s nodes
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
available_kib="$(df -Pk / | awk 'NR == 2 { print $4 }')"
|
||||
minimum_kib="$((25 * 1024 * 1024))"
|
||||
if (( available_kib < minimum_kib )); then
|
||||
echo "Approval live gate requires at least 25 GiB free; found ${available_kib} KiB" >&2
|
||||
exit 1
|
||||
fi
|
||||
docker volume ls --quiet --filter dangling=true | sort > \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.before"
|
||||
docker system df
|
||||
|
||||
- name: Install workspace dependencies without lifecycle scripts
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Install verified kubectl v1.32.8
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/kubectl" \
|
||||
https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/kubectl.sha256" \
|
||||
https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl.sha256
|
||||
test "$(cat "${RUNNER_TEMP}/kubectl.sha256")" = \
|
||||
"$(sha256sum "${RUNNER_TEMP}/kubectl" | cut -d ' ' -f 1)"
|
||||
chmod 0755 "${RUNNER_TEMP}/kubectl"
|
||||
|
||||
- name: Fetch checksum-locked CloudNativePG operator manifest
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/cloudnative-pg.yaml" \
|
||||
https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.30.0/cnpg-1.30.0.yaml
|
||||
echo "f8bede43fe4ee0d478c2355b204a36876b2ae4faac60f2a9452280b293da3b88 ${RUNNER_TEMP}/cloudnative-pg.yaml" | sha256sum --check
|
||||
|
||||
- name: Recheck static Approval and evidence contracts
|
||||
run: |
|
||||
pnpm audit:cluster-deployment:ql3
|
||||
node --test \
|
||||
test/back/ql3ApprovalManagementKubernetesLiveContract.test.cjs \
|
||||
test/back/ql3ApprovalManagementKubernetesLiveAudit.test.cjs
|
||||
pnpm --filter @qinglong/runtime-core build
|
||||
|
||||
- name: Preload the digest-bound K3s distribution
|
||||
run: docker pull rancher/k3s:v1.34.3-k3s1
|
||||
|
||||
- name: Prove Approval over three nodes, mTLS, OIDC and CloudNativePG
|
||||
env:
|
||||
QL3_APPROVAL_MANAGEMENT_KUBERNETES_LIVE: '1'
|
||||
QL3_KUBECTL_BIN: ${{ runner.temp }}/kubectl
|
||||
QL3_CNPG_OPERATOR_MANIFEST_FILE: ${{ runner.temp }}/cloudnative-pg.yaml
|
||||
QL3_APPROVAL_REPORT: ${{ runner.temp }}/ql3-approval/report.json
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
mkdir -m 0700 "$(dirname "${QL3_APPROVAL_REPORT}")"
|
||||
node scripts/ql3-approval-management-kubernetes-live-contract.cjs \
|
||||
"--report=${QL3_APPROVAL_REPORT}"
|
||||
|
||||
- name: Re-audit private report and isolated cleanup
|
||||
env:
|
||||
QL3_APPROVAL_REPORT: ${{ runner.temp }}/ql3-approval/report.json
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(stat -c '%a' "${QL3_APPROVAL_REPORT}")" = '600'
|
||||
pnpm audit:approval-management-kubernetes-live:ql3 \
|
||||
"--report=${QL3_APPROVAL_REPORT}"
|
||||
sha256sum "${QL3_APPROVAL_REPORT}"
|
||||
test -z "$(docker ps -aq --filter name=ql3-approval-live-)"
|
||||
test -z "$(docker network ls -q --filter name=ql3-approval-live-)"
|
||||
docker volume ls --quiet --filter dangling=true | sort > \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.after"
|
||||
diff --unified \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.before" \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.after"
|
||||
docker system df
|
||||
|
||||
- name: Upload audited content-free Approval evidence
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ql3-approval-management-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/ql3-approval/report.json
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
compression-level: 9
|
||||
overwrite: false
|
||||
include-hidden-files: false
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,134 @@
|
||||
name: QingLong 3.0 CloudNativePG DR live evidence
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ql3-cloudnativepg-dr-live-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
cloudnativepg-dr-live:
|
||||
name: Barman WAL, latest and PITR recovery
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 120
|
||||
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: Require enough ephemeral disk for four isolated K3s nodes
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
available_kib="$(df -Pk / | awk 'NR == 2 { print $4 }')"
|
||||
minimum_kib="$((35 * 1024 * 1024))"
|
||||
if (( available_kib < minimum_kib )); then
|
||||
echo "CloudNativePG DR live gate requires at least 35 GiB free; found ${available_kib} KiB" >&2
|
||||
exit 1
|
||||
fi
|
||||
docker volume ls --quiet --filter dangling=true | sort > \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.before"
|
||||
docker system df
|
||||
|
||||
- name: Install workspace dependencies without lifecycle scripts
|
||||
run: pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Install verified kubectl v1.32.8
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/kubectl" \
|
||||
https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/kubectl.sha256" \
|
||||
https://dl.k8s.io/release/v1.32.8/bin/linux/amd64/kubectl.sha256
|
||||
test "$(cat "${RUNNER_TEMP}/kubectl.sha256")" = \
|
||||
"$(sha256sum "${RUNNER_TEMP}/kubectl" | cut -d ' ' -f 1)"
|
||||
chmod 0755 "${RUNNER_TEMP}/kubectl"
|
||||
|
||||
- name: Fetch checksum-locked operator manifests
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/cert-manager.yaml" \
|
||||
https://github.com/cert-manager/cert-manager/releases/download/v1.20.3/cert-manager.yaml
|
||||
echo "7ee74ba06845213e96d8ceaff3d20dd51e682765c1418eddda4e8780ba082261 ${RUNNER_TEMP}/cert-manager.yaml" | sha256sum --check
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/barman-cloud.yaml" \
|
||||
https://github.com/cloudnative-pg/plugin-barman-cloud/releases/download/v0.13.0/manifest.yaml
|
||||
echo "d2e71e7b06822448f1a421f05781846cfdb9cc621e7ef32eef5e20c5133213b0 ${RUNNER_TEMP}/barman-cloud.yaml" | sha256sum --check
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/cloudnative-pg.yaml" \
|
||||
https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.30.0/cnpg-1.30.0.yaml
|
||||
echo "f8bede43fe4ee0d478c2355b204a36876b2ae4faac60f2a9452280b293da3b88 ${RUNNER_TEMP}/cloudnative-pg.yaml" | sha256sum --check
|
||||
|
||||
- name: Recheck the static backup and supply-chain contracts
|
||||
run: |
|
||||
pnpm audit:cloudnativepg-backup:ql3
|
||||
pnpm audit:barman-cloud-supply-chain:ql3
|
||||
pnpm audit:cert-manager-selection:ql3
|
||||
node --test \
|
||||
test/back/ql3CloudNativePgBarmanLiveContract.test.cjs \
|
||||
test/back/ql3CloudNativePgDrEvidenceAudit.test.cjs \
|
||||
test/back/ql3CloudNativePgBarmanWorkflow.test.cjs
|
||||
|
||||
- name: Prove TLS object storage, continuous WAL, latest restore and PITR
|
||||
env:
|
||||
QL3_CLOUDNATIVEPG_BARMAN_LIVE: '1'
|
||||
QL3_SOURCE_REVISION: ${{ github.sha }}
|
||||
QL3_KUBECTL_BIN: ${{ runner.temp }}/kubectl
|
||||
QL3_CERT_MANAGER_MANIFEST_FILE: ${{ runner.temp }}/cert-manager.yaml
|
||||
QL3_BARMAN_MANIFEST_FILE: ${{ runner.temp }}/barman-cloud.yaml
|
||||
QL3_CNPG_OPERATOR_MANIFEST_FILE: ${{ runner.temp }}/cloudnative-pg.yaml
|
||||
QL3_DR_REPORT: ${{ runner.temp }}/ql3-cloudnativepg-dr/report.json
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
mkdir -m 0700 "$(dirname "${QL3_DR_REPORT}")"
|
||||
pnpm test:cloudnativepg-barman-live:ql3 -- \
|
||||
"--report=${QL3_DR_REPORT}"
|
||||
|
||||
- name: Re-audit the exact private report and isolated cleanup
|
||||
env:
|
||||
QL3_DR_REPORT: ${{ runner.temp }}/ql3-cloudnativepg-dr/report.json
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(stat -c '%a' "${QL3_DR_REPORT}")" = '600'
|
||||
pnpm audit:cloudnativepg-dr-evidence:ql3 -- \
|
||||
"--report=${QL3_DR_REPORT}"
|
||||
sha256sum "${QL3_DR_REPORT}"
|
||||
test -z "$(docker ps -aq --filter name=ql3-barman-dr-)"
|
||||
test -z "$(docker network ls -q --filter name=ql3-barman-dr-)"
|
||||
test -z "$(docker ps -aq --filter label=io.qinglong.ql3.live=cloudnativepg-barman-disaster-recovery)"
|
||||
test -z "$(docker network ls -q --filter label=io.qinglong.ql3.live=cloudnativepg-barman-disaster-recovery)"
|
||||
docker volume ls --quiet --filter dangling=true | sort > \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.after"
|
||||
diff --unified \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.before" \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.after"
|
||||
docker system df
|
||||
|
||||
- name: Upload the audited content-free DR evidence
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ql3-cloudnativepg-dr-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/ql3-cloudnativepg-dr/report.json
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
compression-level: 9
|
||||
overwrite: false
|
||||
include-hidden-files: false
|
||||
@@ -0,0 +1,532 @@
|
||||
name: 'QingLong 3.0 Image Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Exact QingLong 3 SemVer tag without the v prefix
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ql3-image-release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
worker-management-release-evidence:
|
||||
name: Audit private Worker management release evidence
|
||||
runs-on: [self-hosted, linux, ql3-release-evidence-ephemeral]
|
||||
environment: ql3-production-release-evidence
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
with:
|
||||
node-version: '24.18.0'
|
||||
|
||||
- name: Re-audit commit-scoped private production evidence
|
||||
env:
|
||||
RELEASE_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
if [[ ! "${GITHUB_SHA}" =~ ^[0-9a-f]{40,64}$ ]]; then
|
||||
echo "release source commit is malformed" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${RELEASE_VERSION}" =~ ^3\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
|
||||
echo "release version must be QingLong 3 SemVer" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${GITHUB_REF_TYPE}" != "tag" || "${GITHUB_REF_NAME}" != "v${RELEASE_VERSION}" ]]; then
|
||||
echo "workflow dispatch must target the exact protected release tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
evidence_dir="/run/qinglong3-release-evidence/${GITHUB_SHA}"
|
||||
node scripts/ql3-worker-credential-management-release-gate.cjs \
|
||||
--report="${evidence_dir}/worker-management-release-evidence.json" \
|
||||
--ceremony-report="${evidence_dir}/worker-management-ceremony.json" \
|
||||
--durable-audit-report="${evidence_dir}/worker-management-durable-audit.json" \
|
||||
--pki-rotation-report="${evidence_dir}/worker-management-pki-rotation-v2.json" \
|
||||
--ca-rollover-report="${evidence_dir}/worker-management-ca-rollover.json" \
|
||||
--source-commit="${GITHUB_SHA}" \
|
||||
--release-version="${RELEASE_VERSION}"
|
||||
|
||||
cluster-dr-release-evidence:
|
||||
name: Audit private CloudNativePG disaster-recovery evidence
|
||||
runs-on: [self-hosted, linux, ql3-release-evidence-ephemeral]
|
||||
environment: ql3-production-release-evidence
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
with:
|
||||
node-version: '24.18.0'
|
||||
|
||||
- name: Re-audit commit-scoped private disaster-recovery evidence
|
||||
env:
|
||||
RELEASE_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
if [[ ! "${GITHUB_SHA}" =~ ^[0-9a-f]{40,64}$ ]]; then
|
||||
echo "release source commit is malformed" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${RELEASE_VERSION}" =~ ^3\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
|
||||
echo "release version must be QingLong 3 SemVer" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${GITHUB_REF_TYPE}" != "tag" || "${GITHUB_REF_NAME}" != "v${RELEASE_VERSION}" ]]; then
|
||||
echo "workflow dispatch must target the exact protected release tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
evidence_dir="/run/qinglong3-release-evidence/${GITHUB_SHA}"
|
||||
report="${evidence_dir}/cloudnativepg-dr-evidence.json"
|
||||
node scripts/ql3-cloudnativepg-backup-audit.cjs
|
||||
node scripts/ql3-barman-cloud-supply-chain-audit.cjs
|
||||
node scripts/ql3-cert-manager-selection-audit.cjs
|
||||
node scripts/ql3-cloudnativepg-dr-release-gate.cjs \
|
||||
--report="${report}" \
|
||||
--source-commit="${GITHUB_SHA}" \
|
||||
--release-version="${RELEASE_VERSION}"
|
||||
|
||||
os-vulnerability:
|
||||
name: Scan ${{ matrix.image }} OS packages on ${{ matrix.image_arch }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- image: control
|
||||
runner: ubuntu-24.04
|
||||
node_arch: x64
|
||||
image_arch: amd64
|
||||
dockerfile: deploy/containers/ql3-cluster-control/Dockerfile
|
||||
target: runtime
|
||||
- image: control
|
||||
runner: ubuntu-24.04-arm
|
||||
node_arch: arm64
|
||||
image_arch: arm64
|
||||
dockerfile: deploy/containers/ql3-cluster-control/Dockerfile
|
||||
target: runtime
|
||||
- image: control-ai
|
||||
runner: ubuntu-24.04
|
||||
node_arch: x64
|
||||
image_arch: amd64
|
||||
dockerfile: deploy/containers/ql3-cluster-control/Dockerfile
|
||||
target: runtime-ai
|
||||
- image: control-ai
|
||||
runner: ubuntu-24.04-arm
|
||||
node_arch: arm64
|
||||
image_arch: arm64
|
||||
dockerfile: deploy/containers/ql3-cluster-control/Dockerfile
|
||||
target: runtime-ai
|
||||
- image: admin
|
||||
runner: ubuntu-24.04
|
||||
node_arch: x64
|
||||
image_arch: amd64
|
||||
dockerfile: deploy/containers/ql3-cluster-admin/Dockerfile
|
||||
target: runtime
|
||||
- image: admin
|
||||
runner: ubuntu-24.04-arm
|
||||
node_arch: arm64
|
||||
image_arch: arm64
|
||||
dockerfile: deploy/containers/ql3-cluster-admin/Dockerfile
|
||||
target: runtime
|
||||
- image: local
|
||||
runner: ubuntu-24.04
|
||||
node_arch: x64
|
||||
image_arch: amd64
|
||||
dockerfile: deploy/containers/ql3-local-application/Dockerfile
|
||||
target: runtime
|
||||
- image: local
|
||||
runner: ubuntu-24.04-arm
|
||||
node_arch: arm64
|
||||
image_arch: arm64
|
||||
dockerfile: deploy/containers/ql3-local-application/Dockerfile
|
||||
target: runtime
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
with:
|
||||
node-version: '24.18.0'
|
||||
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
|
||||
|
||||
- name: Verify native scanner architecture
|
||||
run: node -e "if (process.arch !== '${{ matrix.node_arch }}') throw new Error('unexpected architecture ' + process.arch)"
|
||||
|
||||
- name: Audit the bounded exception lifecycle
|
||||
run: node scripts/ql3-image-os-vulnerability-policy.cjs
|
||||
|
||||
- name: Build the exact native OCI release artifact once
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "${RUNNER_TEMP}/ql3-native"
|
||||
docker buildx build \
|
||||
--platform linux/${{ matrix.image_arch }} \
|
||||
--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-native/image.oci.tar" \
|
||||
.
|
||||
|
||||
- name: Materialize image-scoped Trivy 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 OS vulnerabilities
|
||||
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
||||
with:
|
||||
version: 'v0.70.0'
|
||||
input: ${{ runner.temp }}/ql3-native/image.oci.tar
|
||||
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: Bind the successful native scan to the OCI digest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir "${RUNNER_TEMP}/ql3-native/layout"
|
||||
tar -xf "${RUNNER_TEMP}/ql3-native/image.oci.tar" \
|
||||
-C "${RUNNER_TEMP}/ql3-native/layout"
|
||||
node scripts/ql3-image-release-bundle.cjs \
|
||||
--mode=record-native \
|
||||
--image=${{ matrix.image }} \
|
||||
--platform=linux/${{ matrix.image_arch }} \
|
||||
--layout=${{ runner.temp }}/ql3-native/layout \
|
||||
--expected-revision=${{ github.sha }} \
|
||||
--evidence=${{ runner.temp }}/ql3-native/evidence.json
|
||||
rm "${RUNNER_TEMP}/ql3-native/image.oci.tar"
|
||||
|
||||
- name: Upload the scanned immutable native OCI artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ql3-release-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.image }}-${{ matrix.image_arch }}
|
||||
path: ${{ runner.temp }}/ql3-native
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
compression-level: 0
|
||||
overwrite: false
|
||||
include-hidden-files: false
|
||||
|
||||
publish:
|
||||
name: Publish immutable multi-architecture ${{ matrix.image }} image
|
||||
needs:
|
||||
- worker-management-release-evidence
|
||||
- cluster-dr-release-evidence
|
||||
- os-vulnerability
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- image: control
|
||||
repository: qinglong3-cluster-control
|
||||
runtime_root: deploy/containers/ql3-cluster-control/runtime-dependencies
|
||||
- image: control-ai
|
||||
repository: qinglong3-cluster-control-ai
|
||||
runtime_root: deploy/containers/ql3-cluster-control/runtime-dependencies
|
||||
- image: admin
|
||||
repository: qinglong3-cluster-admin
|
||||
runtime_root: deploy/containers/ql3-cluster-admin/runtime-dependencies
|
||||
- image: local
|
||||
repository: qinglong3-local-application
|
||||
runtime_root: deploy/containers/ql3-local-application/runtime-dependencies
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
||||
with:
|
||||
node-version: '24.18.0'
|
||||
|
||||
- name: Resolve and validate release identity
|
||||
id: identity
|
||||
env:
|
||||
DISPATCH_VERSION: ${{ inputs.version }}
|
||||
IMAGE_REPOSITORY: ${{ matrix.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
owner="${GITHUB_REPOSITORY_OWNER,,}"
|
||||
image="ghcr.io/${owner}/${IMAGE_REPOSITORY}"
|
||||
version="${DISPATCH_VERSION}"
|
||||
if [[ ! "${version}" =~ ^3\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
|
||||
echo "release version must be QingLong 3 SemVer" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${GITHUB_REF_TYPE}" != "tag" || "${GITHUB_REF_NAME}" != "v${version}" ]]; then
|
||||
echo "release source must be the exact requested tag" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "image=${image}" >> "${GITHUB_OUTPUT}"
|
||||
echo "version=${version}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Generate and audit the reviewed application SBOM
|
||||
run: >-
|
||||
node scripts/ql3-cluster-image-sbom.cjs
|
||||
--image=${{ matrix.image }}
|
||||
--output=${{ runner.temp }}/${{ matrix.repository }}.cdx.json
|
||||
|
||||
- name: Reject high or critical production dependency advisories
|
||||
run: >-
|
||||
npm audit
|
||||
--omit=dev
|
||||
--audit-level=high
|
||||
--prefix=${{ matrix.runtime_root }}
|
||||
|
||||
- name: Download the exact scanned amd64 OCI artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: ql3-release-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.image }}-amd64
|
||||
path: ${{ runner.temp }}/native/amd64
|
||||
|
||||
- name: Download the exact scanned arm64 OCI artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: ql3-release-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.image }}-arm64
|
||||
path: ${{ runner.temp }}/native/arm64
|
||||
|
||||
- name: Merge and re-audit the scanned OCI digest without rebuilding
|
||||
id: bundle
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/ql3-image-release-bundle.cjs \
|
||||
--mode=merge \
|
||||
--image=${{ matrix.image }} \
|
||||
--amd64-layout=${RUNNER_TEMP}/native/amd64/layout \
|
||||
--amd64-evidence=${RUNNER_TEMP}/native/amd64/evidence.json \
|
||||
--arm64-layout=${RUNNER_TEMP}/native/arm64/layout \
|
||||
--arm64-evidence=${RUNNER_TEMP}/native/arm64/evidence.json \
|
||||
--expected-revision=${GITHUB_SHA} \
|
||||
--output=${RUNNER_TEMP}/${{ matrix.repository }}-release-layout \
|
||||
--predicate=${RUNNER_TEMP}/${{ matrix.repository }}-os-vulnerability.json \
|
||||
--report=${RUNNER_TEMP}/${{ matrix.repository }}-bundle.json
|
||||
digest="$(node -e 'const fs=require("node:fs");const value=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!/^sha256:[0-9a-f]{64}$/.test(value.rootIndexDigest))process.exit(1);process.stdout.write(value.rootIndexDigest)' "${RUNNER_TEMP}/${{ matrix.repository }}-bundle.json")"
|
||||
echo "digest=${digest}" >> "${GITHUB_OUTPUT}"
|
||||
tar -C "${RUNNER_TEMP}/${{ matrix.repository }}-release-layout" \
|
||||
-cf "${RUNNER_TEMP}/${{ matrix.repository }}-release.oci.tar" .
|
||||
|
||||
- name: Install the exact checksum-pinned OCI copier
|
||||
env:
|
||||
REGCTL: ${{ runner.temp }}/regctl
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl --fail --location --proto '=https' --tlsv1.2 \
|
||||
--output "${REGCTL}" \
|
||||
https://github.com/regclient/regclient/releases/download/v0.11.5/regctl-linux-amd64
|
||||
echo "c93aa7638749f5aaac1a8e01787321889c78f0101809bb2880343478d0ba0467 ${REGCTL}" | sha256sum --check --strict
|
||||
chmod 0755 "${REGCTL}"
|
||||
"${REGCTL}" version
|
||||
|
||||
- uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Import the scanned OCI graph by digest without a public tag
|
||||
id: push
|
||||
env:
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.bundle.outputs.digest }}
|
||||
ARCHIVE: ${{ runner.temp }}/${{ matrix.repository }}-release.oci.tar
|
||||
REGCTL: ${{ runner.temp }}/regctl
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ ! "${DIGEST}" =~ ^sha256:[0-9a-f]{64}$ ]]; then
|
||||
echo "bundle did not return an immutable SHA-256 digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
"${REGCTL}" image import "${IMAGE}@${DIGEST}" "${ARCHIVE}"
|
||||
remote_digest="$("${REGCTL}" image digest "${IMAGE}@${DIGEST}")"
|
||||
if [[ "${remote_digest}" != "${DIGEST}" ]]; then
|
||||
echo "registry changed the scanned OCI digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "digest=${DIGEST}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
|
||||
- name: Keylessly sign the immutable manifest digest
|
||||
env:
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: cosign sign --yes "${IMAGE}@${DIGEST}"
|
||||
|
||||
- name: Attest SLSA build provenance
|
||||
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4
|
||||
with:
|
||||
subject-name: ${{ steps.identity.outputs.image }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest the reviewed CycloneDX application SBOM
|
||||
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4
|
||||
with:
|
||||
subject-name: ${{ steps.identity.outputs.image }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
sbom-path: ${{ runner.temp }}/${{ matrix.repository }}.cdx.json
|
||||
push-to-registry: true
|
||||
|
||||
- name: Attest the digest-bound OS vulnerability evidence
|
||||
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4
|
||||
with:
|
||||
subject-name: ${{ steps.identity.outputs.image }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
predicate-type: https://qinglong.dev/attestations/image-os-vulnerability/v1
|
||||
predicate-path: ${{ runner.temp }}/${{ matrix.repository }}-os-vulnerability.json
|
||||
push-to-registry: true
|
||||
|
||||
- name: Verify the published manifest and attestation bindings
|
||||
env:
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
manifest="${RUNNER_TEMP}/${{ matrix.repository }}-index.json"
|
||||
docker buildx imagetools inspect --raw "${IMAGE}@${DIGEST}" > "${manifest}"
|
||||
node scripts/ql3-cluster-remote-manifest-audit.cjs \
|
||||
--manifest="${manifest}" \
|
||||
--expected-image="${IMAGE}" \
|
||||
--expected-digest="${DIGEST}"
|
||||
|
||||
- name: Verify local rollout compatibility against the pushed digest
|
||||
if: matrix.image == 'local'
|
||||
env:
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile --ignore-scripts
|
||||
pnpm --filter @qinglong/local-owner-cli check
|
||||
docker pull "${IMAGE}@${DIGEST}"
|
||||
docker_executable="$(realpath "$(command -v docker)")"
|
||||
docker_socket="$(realpath /var/run/docker.sock)"
|
||||
node scripts/ql3-local-compose-rollout-live-contract.cjs \
|
||||
--image="${IMAGE}@${DIGEST}" \
|
||||
--docker-executable="${docker_executable}" \
|
||||
--docker-socket="${docker_socket}" \
|
||||
--profile=edge
|
||||
node scripts/ql3-local-compose-rollout-live-contract.cjs \
|
||||
--image="${IMAGE}@${DIGEST}" \
|
||||
--docker-executable="${docker_executable}" \
|
||||
--docker-socket="${docker_socket}" \
|
||||
--profile=standalone
|
||||
|
||||
- name: Verify the keyless signature identity
|
||||
env:
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
certificate_identity="https://github.com/${GITHUB_REPOSITORY}/.github/workflows/ql3-image-release.yml@${GITHUB_REF}"
|
||||
cosign verify \
|
||||
--certificate-identity "${certificate_identity}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||
"${IMAGE}@${DIGEST}"
|
||||
|
||||
- name: Verify GitHub SLSA provenance from the published OCI registry
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh attestation verify "oci://${IMAGE}@${DIGEST}" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/ql3-image-release.yml" \
|
||||
--source-digest "${GITHUB_SHA}" \
|
||||
--source-ref "${GITHUB_REF}" \
|
||||
--deny-self-hosted-runners \
|
||||
--bundle-from-oci
|
||||
|
||||
- name: Verify the reviewed CycloneDX attestation from the OCI registry
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh attestation verify "oci://${IMAGE}@${DIGEST}" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/ql3-image-release.yml" \
|
||||
--source-digest "${GITHUB_SHA}" \
|
||||
--source-ref "${GITHUB_REF}" \
|
||||
--predicate-type "https://cyclonedx.org/bom" \
|
||||
--deny-self-hosted-runners \
|
||||
--bundle-from-oci
|
||||
|
||||
- name: Verify the OS vulnerability evidence from the OCI registry
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh attestation verify "oci://${IMAGE}@${DIGEST}" \
|
||||
--repo "${GITHUB_REPOSITORY}" \
|
||||
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/ql3-image-release.yml" \
|
||||
--source-digest "${GITHUB_SHA}" \
|
||||
--source-ref "${GITHUB_REF}" \
|
||||
--predicate-type "https://qinglong.dev/attestations/image-os-vulnerability/v1" \
|
||||
--deny-self-hosted-runners \
|
||||
--bundle-from-oci
|
||||
|
||||
- name: Promote only the verified digest to immutable release tags
|
||||
env:
|
||||
IMAGE: ${{ steps.identity.outputs.image }}
|
||||
VERSION: ${{ steps.identity.outputs.version }}
|
||||
DIGEST: ${{ steps.push.outputs.digest }}
|
||||
REGCTL: ${{ runner.temp }}/regctl
|
||||
run: |
|
||||
set -euo pipefail
|
||||
"${REGCTL}" image copy "${IMAGE}@${DIGEST}" "${IMAGE}:${VERSION}"
|
||||
"${REGCTL}" image copy "${IMAGE}@${DIGEST}" "${IMAGE}:sha-${GITHUB_SHA}"
|
||||
if [[ "$("${REGCTL}" image digest "${IMAGE}:${VERSION}")" != "${DIGEST}" ]]; then
|
||||
echo "release version tag does not resolve to the verified digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$("${REGCTL}" image digest "${IMAGE}:sha-${GITHUB_SHA}")" != "${DIGEST}" ]]; then
|
||||
echo "release source tag does not resolve to the verified digest" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,122 @@
|
||||
name: QingLong 3.0 Worker Kubernetes rollout live evidence
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ql3-worker-kubernetes-rollout-live-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
worker-kubernetes-rollout-live:
|
||||
name: Production Worker, Recreate, mTLS, PVC and Session drain
|
||||
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: Require isolated fixture capacity
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
available_kib="$(df -Pk / | awk 'NR == 2 { print $4 }')"
|
||||
minimum_kib="$((15 * 1024 * 1024))"
|
||||
if (( available_kib < minimum_kib )); then
|
||||
echo "Worker rollout live gate requires at least 15 GiB free; found ${available_kib} KiB" >&2
|
||||
exit 1
|
||||
fi
|
||||
docker volume ls --quiet --filter dangling=true | sort > \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.before"
|
||||
docker system df
|
||||
|
||||
- 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/amd64/kubectl
|
||||
curl --fail --location --silent --show-error \
|
||||
--output "${RUNNER_TEMP}/kubectl.sha256" \
|
||||
https://dl.k8s.io/release/v1.34.3/bin/linux/amd64/kubectl.sha256
|
||||
test "$(cat "${RUNNER_TEMP}/kubectl.sha256")" = \
|
||||
"$(sha256sum "${RUNNER_TEMP}/kubectl" | cut -d ' ' -f 1)"
|
||||
chmod 0755 "${RUNNER_TEMP}/kubectl"
|
||||
|
||||
- name: Recheck Worker runtime and evidence contracts
|
||||
run: |
|
||||
pnpm --filter @qinglong/worker-runtime test
|
||||
node --test test/back/ql3WorkerKubernetesRolloutLiveAudit.test.cjs
|
||||
pnpm audit:worker-deployment:ql3
|
||||
|
||||
- name: Preload digest-bound fixture images
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
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: Prove production Worker Kubernetes lifecycle
|
||||
env:
|
||||
QL3_WORKER_KUBERNETES_ROLLOUT_LIVE: '1'
|
||||
QL3_KUBECTL_BIN: ${{ runner.temp }}/kubectl
|
||||
QL3_WORKER_KUBERNETES_REPORT: ${{ runner.temp }}/ql3-worker/report.json
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
mkdir -m 0700 "$(dirname "${QL3_WORKER_KUBERNETES_REPORT}")"
|
||||
pnpm test:worker-kubernetes-rollout-live:ql3 \
|
||||
"--report=${QL3_WORKER_KUBERNETES_REPORT}"
|
||||
|
||||
- name: Re-audit private report and isolated cleanup
|
||||
env:
|
||||
QL3_WORKER_KUBERNETES_REPORT: ${{ runner.temp }}/ql3-worker/report.json
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(stat -c '%a' "${QL3_WORKER_KUBERNETES_REPORT}")" = '600'
|
||||
pnpm audit:worker-kubernetes-rollout-live:ql3 \
|
||||
"--report=${QL3_WORKER_KUBERNETES_REPORT}"
|
||||
sha256sum "${QL3_WORKER_KUBERNETES_REPORT}"
|
||||
test -z "$(docker ps -aq --filter name=ql3-worker-rollout-live-)"
|
||||
test -z "$(docker ps -aq --filter name=ql3-worker-rollout-postgres-live-)"
|
||||
docker volume ls --quiet --filter dangling=true | sort > \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.after"
|
||||
diff --unified \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.before" \
|
||||
"${RUNNER_TEMP}/ql3-dangling-volumes.after"
|
||||
docker system df
|
||||
|
||||
- name: Upload audited content-free Worker evidence
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ql3-worker-kubernetes-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: ${{ runner.temp }}/ql3-worker/report.json
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
compression-level: 9
|
||||
overwrite: false
|
||||
include-hidden-files: false
|
||||
Reference in New Issue
Block a user