Skip to content

quality gate

quality gate #585

Workflow file for this run

name: quality gate
on:
schedule:
# Daily 02:00 Asia/Shanghai.
- cron: "0 18 * * *"
workflow_dispatch:
inputs:
target_branch:
description: Target branch
type: string
default: latest
scope:
description: Quality gate scope
type: choice
default: ci
options:
- ci
- repo
- repo-tooling
- repo-frontend
- repo-frontend-pr
- repo-frontend-react-doctor
- repo-backend
- repo-backend-static
- repo-backend-fmt
- repo-backend-image-llm-vision
- repo-backend-official-i18n-seed
- repo-backend-clippy-core-libs
- repo-backend-clippy-runtime-storage
- repo-backend-clippy-apps
- repo-backend-test-core-libs
- repo-backend-test-runtime-storage-fast
- repo-backend-test-storage-postgres-1-of-4
- repo-backend-test-storage-postgres-2-of-4
- repo-backend-test-storage-postgres-3-of-4
- repo-backend-test-storage-postgres-4-of-4
- repo-backend-test-control-plane
- repo-backend-test-api-server-1-of-4
- repo-backend-test-api-server-2-of-4
- repo-backend-test-api-server-3-of-4
- repo-backend-test-api-server-4-of-4
- repo-backend-test-plugin-runner
- repo-backend-check-core-libs
- repo-backend-check-runtime-storage
- repo-backend-check-apps
- backend
- backend-consistency
- backend-consistency-control-runtime
- backend-consistency-storage
- backend-consistency-api
- state-protocols
- coverage
- coverage-frontend
- coverage-backend
- coverage-backend-control-plane
- coverage-backend-orchestration-runtime
- coverage-backend-plugin-runner
- coverage-backend-storage-postgres
- coverage-backend-api-server
- container-images
report_type:
description: Report type
type: choice
default: ci
options:
- ci
- cd
environment:
description: Optional environment label
type: string
required: false
react_doctor_base:
description: Protected baseline SHA for a manual React Doctor candidate run
type: string
required: false
permissions:
contents: read
actions: read
issues: write
concurrency:
group: quality-gate-${{ github.event_name }}-${{ github.event_name == 'workflow_dispatch' && inputs.target_branch || 'latest' }}
cancel-in-progress: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
QUALITY_GATE_TARGET_BRANCH: ${{ github.event_name == 'workflow_dispatch' && inputs.target_branch || 'latest' }}
QUALITY_GATE_SCOPE: ${{ github.event_name == 'workflow_dispatch' && inputs.scope || 'ci' }}
QUALITY_GATE_REPORT_TYPE: ${{ github.event_name == 'workflow_dispatch' && inputs.report_type || 'ci' }}
QUALITY_GATE_SCHEDULED_ENVIRONMENT: nightly-latest
jobs:
resolve-quality-gate-target:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
target_sha: ${{ steps.target.outputs.sha }}
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- id: target
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
foundation-contract-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
needs: resolve-quality-gate-target
uses: ./.github/workflows/foundation-contracts.yml
with:
target_ref: ${{ needs.resolve-quality-gate-target.outputs.target_sha }}
base_ref: ''
foundation: all
lane: project-health
single-scope-gate:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.scope != 'ci' && inputs.scope != 'container-images' }}
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
fetch-depth: 0
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: pnpm/action-setup@v5
with:
version: 11.5.0
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- run: pnpm --dir web install --frozen-lockfile
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
if: ${{ startsWith(inputs.scope, 'repo-backend-test-api-server-') || startsWith(inputs.scope, 'repo-backend-test-storage-postgres-') || inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || inputs.scope == 'coverage-backend-api-server' }}
- name: Resolve release-seeded Rust cache architecture
id: release_seed_arch
if: ${{ inputs.scope == 'repo' || inputs.scope == 'backend' || startsWith(inputs.scope, 'backend-consistency') || inputs.scope == 'state-protocols' || inputs.scope == 'repo-backend' || startsWith(inputs.scope, 'repo-backend-') || inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-') }}
shell: bash
run: |
set -euo pipefail
case "${RUNNER_ARCH:-X64}" in
ARM64) cache_arch=arm64 ;;
*) cache_arch=amd64 ;;
esac
echo "cache_arch=$cache_arch" >> "$GITHUB_OUTPUT"
- name: Restore release-seeded api-server Rust cache
id: release_seed_api
uses: actions/cache/restore@v5
with:
path: |
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-registry
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-git
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/target
key: rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock') }}-
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-
- name: Import release-seeded api-server Rust cache
if: ${{ steps.release_seed_api.outputs.cache-hit != '' }}
shell: bash
env:
RUST_RELEASE_SEEDED_CACHE_ARCH: ${{ steps.release_seed_arch.outputs.cache_arch }}
CARGO_TARGET_DIR: tmp/quality-gate-cache/rust-single-${{ (inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-')) && 'coverage' || 'backend' }}/${{ inputs.scope }}/target
run: |
set -euo pipefail
cargo_home="${CARGO_HOME:-$HOME/.cargo}"
mkdir -p "$cargo_home/registry" "$cargo_home/git" "$CARGO_TARGET_DIR"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-registry/" "$cargo_home/registry/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-git/" "$cargo_home/git/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/target/" "$CARGO_TARGET_DIR/"
- name: Restore Rust single-scope quality gate cache
if: ${{ inputs.scope == 'repo' || inputs.scope == 'backend' || startsWith(inputs.scope, 'backend-consistency') || inputs.scope == 'state-protocols' || inputs.scope == 'repo-backend' || startsWith(inputs.scope, 'repo-backend-') || inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-') }}
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tmp/quality-gate-cache/rust-single-${{ (inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-')) && 'coverage' || 'backend' }}/${{ inputs.scope }}/target
key: rust-quality-gate-v2-${{ (inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-')) && 'coverage' || 'backend' }}-${{ runner.os }}-${{ inputs.scope }}-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-quality-gate-v2-${{ (inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-')) && 'coverage' || 'backend' }}-${{ runner.os }}-${{ inputs.scope }}-${{ hashFiles('api/Cargo.lock') }}-
rust-quality-gate-v2-${{ (inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-')) && 'coverage' || 'backend' }}-${{ runner.os }}-${{ inputs.scope }}-
- uses: ./.github/actions/quality-gate
env:
CARGO_TARGET_DIR: ../tmp/quality-gate-cache/rust-single-${{ (inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-')) && 'coverage' || 'backend' }}/${{ inputs.scope }}/target
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
REACT_DOCTOR_DIFF_BASE: ${{ inputs.react_doctor_base }}
REACT_DOCTOR_BASE_SOURCE: workflow_dispatch.react_doctor_base
REACT_DOCTOR_CANDIDATE_SOURCE: quality-gate-target-sha
with:
scope: ${{ env.QUALITY_GATE_SCOPE }}
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "true"
start_postgres: ${{ inputs.scope == 'repo' || inputs.scope == 'backend' || startsWith(inputs.scope, 'backend-consistency') || inputs.scope == 'state-protocols' || inputs.scope == 'repo-backend' || startsWith(inputs.scope, 'repo-backend-test-') || inputs.scope == 'coverage' || inputs.scope == 'coverage-backend' || startsWith(inputs.scope, 'coverage-backend-') }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-artifacts
path: tmp/test-governance
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-summary
if-no-files-found: error
path: |
tmp/test-governance/quality-gate-report.json
tmp/test-governance/quality-gate-report.md
tmp/test-governance/quality-gate.latest.log
repo-tooling-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
fetch-depth: 0
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: pnpm/action-setup@v5
with:
version: 11.5.0
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- run: pnpm --dir web install --frozen-lockfile
- uses: dtolnay/rust-toolchain@stable
- uses: ./.github/actions/quality-gate
env:
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: repo-tooling
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
start_postgres: "false"
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-repo-tooling
path: tmp/test-governance
repo-frontend-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: pnpm/action-setup@v5
with:
version: 11.5.0
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- run: pnpm --dir web install --frozen-lockfile
- uses: ./.github/actions/quality-gate
env:
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: repo-frontend
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
start_postgres: "false"
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-repo-frontend
path: tmp/test-governance
repo-frontend-react-doctor-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
fetch-depth: 0
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- name: Resolve React Doctor base and candidate
env:
GH_TOKEN: ${{ github.token }}
REACT_DOCTOR_MANUAL_BASE: ${{ inputs.react_doctor_base }}
run: |
set -euo pipefail
candidate_sha="${QUALITY_GATE_TARGET_SHA}"
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
base_sha="${REACT_DOCTOR_DIFF_BASE:-${REACT_DOCTOR_MANUAL_BASE:-}}"
if [[ -z "${base_sha}" ]]; then
echo "React Doctor manual runs require inputs.react_doctor_base (protected baseline SHA)" >&2
exit 1
fi
base_source="workflow_dispatch.react_doctor_base"
else
previous_successful_run="$(
gh api --method GET \
-H "Accept: application/vnd.github+json" \
-f "branch=${QUALITY_GATE_TARGET_BRANCH}" \
-f "status=success" \
-f "per_page=50" \
"repos/${GITHUB_REPOSITORY}/actions/workflows/quality-gate.yml/runs" \
| jq -r --arg candidate "${candidate_sha}" --arg run_id "${GITHUB_RUN_ID}" '
[
.workflow_runs[]
| select((.id | tostring) != $run_id)
| select(.head_sha != $candidate)
| [.head_sha, (.id | tostring)]
]
| .[0]
| if . then @tsv else empty end
'
)"
if [[ -z "${previous_successful_run}" ]]; then
echo "React Doctor scheduled run has no previous successful quality-gate candidate baseline" >&2
exit 1
fi
base_sha="${previous_successful_run%%$'\t'*}"
previous_run_id="${previous_successful_run#*$'\t'}"
base_source="previous-successful-quality-gate-run:${previous_run_id}"
fi
git rev-parse --verify "${base_sha}^{commit}" >/dev/null
git rev-parse --verify "${candidate_sha}^{commit}" >/dev/null
{
echo "REACT_DOCTOR_DIFF_BASE=${base_sha}"
echo "REACT_DOCTOR_CANDIDATE_SHA=${candidate_sha}"
echo "REACT_DOCTOR_BASE_SOURCE=${base_source}"
echo "REACT_DOCTOR_CANDIDATE_SOURCE=quality-gate-target-sha"
} >> "$GITHUB_ENV"
- uses: pnpm/action-setup@v5
with:
version: 11.5.0
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- run: pnpm --dir web install --frozen-lockfile
- uses: ./.github/actions/quality-gate
env:
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: repo-frontend-react-doctor
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
start_postgres: "false"
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-repo-frontend-react-doctor
path: tmp/test-governance
repo-backend-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
scope:
- repo-backend-static
- repo-backend-fmt
- repo-backend-image-llm-vision
- repo-backend-official-i18n-seed
- repo-backend-clippy-core-libs
- repo-backend-clippy-runtime-storage
- repo-backend-clippy-apps
- repo-backend-test-core-libs
- repo-backend-test-runtime-storage-fast
- repo-backend-test-storage-postgres-1-of-4
- repo-backend-test-storage-postgres-2-of-4
- repo-backend-test-storage-postgres-3-of-4
- repo-backend-test-storage-postgres-4-of-4
- repo-backend-test-control-plane
- repo-backend-test-api-server-1-of-4
- repo-backend-test-api-server-2-of-4
- repo-backend-test-api-server-3-of-4
- repo-backend-test-api-server-4-of-4
- repo-backend-test-plugin-runner
- repo-backend-check-core-libs
- repo-backend-check-runtime-storage
- repo-backend-check-apps
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@nextest
if: ${{ startsWith(matrix.scope, 'repo-backend-test-api-server-') || startsWith(matrix.scope, 'repo-backend-test-storage-postgres-') }}
- name: Resolve release-seeded Rust cache architecture
id: release_seed_arch
if: ${{ matrix.scope != 'repo-backend-static' && matrix.scope != 'repo-backend-fmt' }}
shell: bash
run: |
set -euo pipefail
case "${RUNNER_ARCH:-X64}" in
ARM64) cache_arch=arm64 ;;
*) cache_arch=amd64 ;;
esac
echo "cache_arch=$cache_arch" >> "$GITHUB_OUTPUT"
- name: Restore release-seeded api-server Rust cache
id: release_seed_api
uses: actions/cache/restore@v5
with:
path: |
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-registry
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-git
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/target
key: rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock') }}-
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-
- name: Import release-seeded api-server Rust cache
if: ${{ steps.release_seed_api.outputs.cache-hit != '' }}
shell: bash
env:
RUST_RELEASE_SEEDED_CACHE_ARCH: ${{ steps.release_seed_arch.outputs.cache_arch }}
CARGO_TARGET_DIR: tmp/quality-gate-cache/rust-backend/${{ matrix.scope }}/target
run: |
set -euo pipefail
cargo_home="${CARGO_HOME:-$HOME/.cargo}"
mkdir -p "$cargo_home/registry" "$cargo_home/git" "$CARGO_TARGET_DIR"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-registry/" "$cargo_home/registry/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-git/" "$cargo_home/git/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/target/" "$CARGO_TARGET_DIR/"
- name: Restore Rust backend quality gate cache
if: ${{ matrix.scope != 'repo-backend-static' && matrix.scope != 'repo-backend-fmt' }}
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tmp/quality-gate-cache/rust-backend/${{ matrix.scope }}/target
key: rust-quality-gate-backend-v2-${{ runner.os }}-${{ matrix.scope }}-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-quality-gate-backend-v2-${{ runner.os }}-${{ matrix.scope }}-${{ hashFiles('api/Cargo.lock') }}-
rust-quality-gate-backend-v2-${{ runner.os }}-${{ matrix.scope }}-
rust-quality-gate-backend-v2-${{ runner.os }}-
- uses: ./.github/actions/quality-gate
env:
CARGO_TARGET_DIR: ../tmp/quality-gate-cache/rust-backend/${{ matrix.scope }}/target
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: ${{ matrix.scope }}
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
start_postgres: ${{ startsWith(matrix.scope, 'repo-backend-test-') }}
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-${{ matrix.scope }}
path: tmp/test-governance
backend-consistency-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 35
strategy:
fail-fast: false
matrix:
group:
- control-runtime
- storage
- api
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@stable
- name: Resolve release-seeded Rust cache architecture
id: release_seed_arch
shell: bash
run: |
set -euo pipefail
case "${RUNNER_ARCH:-X64}" in
ARM64) cache_arch=arm64 ;;
*) cache_arch=amd64 ;;
esac
echo "cache_arch=$cache_arch" >> "$GITHUB_OUTPUT"
- name: Restore release-seeded api-server Rust cache
id: release_seed_api
uses: actions/cache/restore@v5
with:
path: |
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-registry
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-git
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/target
key: rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock') }}-
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-
- name: Import release-seeded api-server Rust cache
if: ${{ steps.release_seed_api.outputs.cache-hit != '' }}
shell: bash
env:
RUST_RELEASE_SEEDED_CACHE_ARCH: ${{ steps.release_seed_arch.outputs.cache_arch }}
CARGO_TARGET_DIR: tmp/quality-gate-cache/rust-backend-consistency/${{ matrix.group }}/target
run: |
set -euo pipefail
cargo_home="${CARGO_HOME:-$HOME/.cargo}"
mkdir -p "$cargo_home/registry" "$cargo_home/git" "$CARGO_TARGET_DIR"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-registry/" "$cargo_home/registry/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-git/" "$cargo_home/git/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/target/" "$CARGO_TARGET_DIR/"
- name: Restore Rust backend consistency quality gate cache
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tmp/quality-gate-cache/rust-backend-consistency/${{ matrix.group }}/target
key: rust-quality-gate-backend-consistency-v2-${{ runner.os }}-${{ matrix.group }}-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-quality-gate-backend-consistency-v2-${{ runner.os }}-${{ matrix.group }}-${{ hashFiles('api/Cargo.lock') }}-
rust-quality-gate-backend-consistency-v2-${{ runner.os }}-${{ matrix.group }}-
rust-quality-gate-backend-consistency-v2-${{ runner.os }}-
- uses: ./.github/actions/quality-gate
env:
CARGO_TARGET_DIR: ../tmp/quality-gate-cache/rust-backend-consistency/${{ matrix.group }}/target
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: backend-consistency-${{ matrix.group }}
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-backend-consistency-${{ matrix.group }}
path: tmp/test-governance
coverage-frontend-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: pnpm/action-setup@v5
with:
version: 11.5.0
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- run: pnpm --dir web install --frozen-lockfile
- uses: ./.github/actions/quality-gate
env:
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: coverage-frontend
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
start_postgres: "false"
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-coverage-frontend
path: tmp/test-governance
coverage-backend-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
needs: resolve-quality-gate-target
runs-on: ubuntu-latest
timeout-minutes: 55
strategy:
fail-fast: false
matrix:
scope:
- coverage-backend-control-plane
- coverage-backend-orchestration-runtime
- coverage-backend-plugin-runner
- coverage-backend-storage-postgres
steps:
- uses: actions/checkout@v5
with:
ref: ${{ needs.resolve-quality-gate-target.outputs.target_sha }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=${{ needs.resolve-quality-gate-target.outputs.target_sha }}" >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- name: Resolve release-seeded Rust cache architecture
id: release_seed_arch
shell: bash
run: |
set -euo pipefail
case "${RUNNER_ARCH:-X64}" in
ARM64) cache_arch=arm64 ;;
*) cache_arch=amd64 ;;
esac
echo "cache_arch=$cache_arch" >> "$GITHUB_OUTPUT"
- name: Restore release-seeded api-server Rust cache
id: release_seed_api
uses: actions/cache/restore@v5
with:
path: |
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-registry
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/cargo-git
tmp/container-cache/api-server/${{ steps.release_seed_arch.outputs.cache_arch }}/target
key: rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-${{ hashFiles('api/Cargo.lock') }}-
rust-release-seeded-api-server-v2-${{ runner.os }}-${{ steps.release_seed_arch.outputs.cache_arch }}-rust-1-slim-bookworm-release-
- name: Import release-seeded api-server Rust cache
if: ${{ steps.release_seed_api.outputs.cache-hit != '' }}
shell: bash
env:
RUST_RELEASE_SEEDED_CACHE_ARCH: ${{ steps.release_seed_arch.outputs.cache_arch }}
CARGO_TARGET_DIR: tmp/quality-gate-cache/rust-coverage/${{ matrix.scope }}/target
run: |
set -euo pipefail
cargo_home="${CARGO_HOME:-$HOME/.cargo}"
mkdir -p "$cargo_home/registry" "$cargo_home/git" "$CARGO_TARGET_DIR"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-registry/" "$cargo_home/registry/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/cargo-git/" "$cargo_home/git/"
rsync -a "tmp/container-cache/api-server/$RUST_RELEASE_SEEDED_CACHE_ARCH/target/" "$CARGO_TARGET_DIR/"
- name: Restore Rust coverage quality gate cache
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tmp/quality-gate-cache/rust-coverage/${{ matrix.scope }}/target
key: rust-quality-gate-coverage-v2-${{ runner.os }}-${{ matrix.scope }}-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-quality-gate-coverage-v2-${{ runner.os }}-${{ matrix.scope }}-${{ hashFiles('api/Cargo.lock') }}-
rust-quality-gate-coverage-v2-${{ runner.os }}-${{ matrix.scope }}-
rust-quality-gate-coverage-v2-${{ runner.os }}-
- uses: ./.github/actions/quality-gate
env:
CARGO_TARGET_DIR: ../tmp/quality-gate-cache/rust-coverage/${{ matrix.scope }}/target
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: ${{ matrix.scope }}
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
- name: Record immutable coverage target SHA
if: always()
run: echo "$QUALITY_GATE_TARGET_SHA" > tmp/test-governance/target-sha.txt
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-${{ matrix.scope }}
path: tmp/test-governance
coverage-backend-api-server-sharded:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
needs: resolve-quality-gate-target
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v5
with:
ref: ${{ needs.resolve-quality-gate-target.outputs.target_sha }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=${{ needs.resolve-quality-gate-target.outputs.target_sha }}" >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
- uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tmp/quality-gate-cache/rust-coverage/api-server-shadow/target
key: rust-quality-gate-coverage-v2-${{ runner.os }}-api-server-shadow-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-quality-gate-coverage-v2-${{ runner.os }}-api-server-shadow-${{ hashFiles('api/Cargo.lock') }}-
rust-quality-gate-coverage-v2-${{ runner.os }}-api-server-shadow-
- uses: ./.github/actions/quality-gate
env:
CARGO_TARGET_DIR: ../tmp/quality-gate-cache/rust-coverage/api-server-shadow/target
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
QUALITY_GATE_TARGET_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: coverage-backend-api-server-sharded-${{ matrix.shard }}-of-4
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
publish_issue: "false"
- uses: actions/upload-artifact@v6
if: always()
with:
name: coverage-shadow-api-server-profraw-${{ matrix.shard }}
path: tmp/test-governance/coverage-shadow/api-server/shard-${{ matrix.shard }}
coverage-backend-api-server-sharded-merge:
if: ${{ always() && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci')) }}
needs:
- resolve-quality-gate-target
- coverage-backend-api-server-sharded
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
with:
ref: ${{ needs.resolve-quality-gate-target.outputs.target_sha }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=${{ needs.resolve-quality-gate-target.outputs.target_sha }}" >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
- uses: actions/cache/restore@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tmp/quality-gate-cache/rust-coverage/api-server-shadow/target
key: rust-quality-gate-coverage-v2-${{ runner.os }}-api-server-shadow-${{ hashFiles('api/Cargo.lock', 'api/**/Cargo.toml') }}
restore-keys: |
rust-quality-gate-coverage-v2-${{ runner.os }}-api-server-shadow-${{ hashFiles('api/Cargo.lock') }}-
rust-quality-gate-coverage-v2-${{ runner.os }}-api-server-shadow-
- uses: actions/download-artifact@v6
with:
pattern: coverage-shadow-api-server-profraw-*
path: tmp/test-governance/coverage-shadow/api-server/downloaded
merge-multiple: true
- name: Merge and enforce sharded API coverage
env:
CARGO_TARGET_DIR: ../tmp/quality-gate-cache/rust-coverage/api-server-shadow/target
QUALITY_GATE_TARGET_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
run: node scripts/node/coverage-shadow.js merge api-server 4
- uses: actions/upload-artifact@v6
if: always()
with:
name: coverage-shadow-api-server-equivalence
path: |
tmp/test-governance/coverage-shadow/api-server/api-server-merged.json
tmp/test-governance/coverage-shadow/api-server/equivalence.json
container-images-gate:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && (inputs.scope == 'ci' || inputs.scope == 'container-images')) }}
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
packages: read
issues: write
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: actions/setup-node@v5
with:
node-version: 24
- name: Prepare Trivy report directory
run: mkdir -p tmp/test-governance
- name: Generate web HIGH Trivy warning report
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
env:
TRIVY_USERNAME: ${{ github.actor }}
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
with:
image-ref: ghcr.io/${{ github.repository_owner }}/1flowbase-web:latest
version: v0.70.0
scanners: vuln
severity: HIGH
format: json
output: tmp/test-governance/trivy-web-high.json
exit-code: "0"
- name: Generate web CRITICAL Trivy gate report
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
env:
TRIVY_USERNAME: ${{ github.actor }}
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
with:
image-ref: ghcr.io/${{ github.repository_owner }}/1flowbase-web:latest
version: v0.70.0
scanners: vuln
severity: CRITICAL
format: json
output: tmp/test-governance/trivy-web-critical.json
exit-code: "0"
- name: Generate api-server HIGH Trivy warning report
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
env:
TRIVY_USERNAME: ${{ github.actor }}
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
with:
image-ref: ghcr.io/${{ github.repository_owner }}/1flowbase-api-server:latest
version: v0.70.0
scanners: vuln
severity: HIGH
format: json
output: tmp/test-governance/trivy-api-server-high.json
exit-code: "0"
- name: Generate api-server CRITICAL Trivy gate report
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
env:
TRIVY_USERNAME: ${{ github.actor }}
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
with:
image-ref: ghcr.io/${{ github.repository_owner }}/1flowbase-api-server:latest
version: v0.70.0
scanners: vuln
severity: CRITICAL
format: json
output: tmp/test-governance/trivy-api-server-critical.json
exit-code: "0"
- name: Generate plugin-runner HIGH Trivy warning report
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
env:
TRIVY_USERNAME: ${{ github.actor }}
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
with:
image-ref: ghcr.io/${{ github.repository_owner }}/1flowbase-plugin-runner:latest
version: v0.70.0
scanners: vuln
severity: HIGH
format: json
output: tmp/test-governance/trivy-plugin-runner-high.json
exit-code: "0"
- name: Generate plugin-runner CRITICAL Trivy gate report
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
env:
TRIVY_USERNAME: ${{ github.actor }}
TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
with:
image-ref: ghcr.io/${{ github.repository_owner }}/1flowbase-plugin-runner:latest
version: v0.70.0
scanners: vuln
severity: CRITICAL
format: json
output: tmp/test-governance/trivy-plugin-runner-critical.json
exit-code: "0"
- uses: ./.github/actions/quality-gate
env:
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
with:
scope: container-images
report_type: ${{ env.QUALITY_GATE_REPORT_TYPE }}
environment: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment || 'container-images' }}
publish_issue: "false"
start_postgres: "false"
github_token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-container-images
path: tmp/test-governance
ai-gateway-protocol-conformance:
if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci') }}
uses: ./.github/workflows/ai-gateway-concurrency.yml
with:
target_ref: ${{ github.event_name == 'workflow_dispatch' && inputs.target_branch || 'latest' }}
aggregate:
if: ${{ always() && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.scope == 'ci')) }}
needs:
- repo-tooling-gate
- repo-frontend-gate
- repo-frontend-react-doctor-gate
- repo-backend-gate
- backend-consistency-gate
- coverage-frontend-gate
- coverage-backend-gate
- coverage-backend-api-server-sharded-merge
- container-images-gate
- ai-gateway-protocol-conformance
- foundation-contract-gate
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
- name: Resolve quality gate target SHA
run: echo "QUALITY_GATE_TARGET_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- uses: actions/download-artifact@v6
with:
pattern: test-governance-*
path: tmp/test-governance/parallel
merge-multiple: false
- run: node scripts/node/cli/github-quality-gate-aggregate.js
env:
GITHUB_REF_NAME: ${{ env.QUALITY_GATE_TARGET_BRANCH }}
GITHUB_SHA: ${{ env.QUALITY_GATE_TARGET_SHA }}
INPUT_ARTIFACT_ROOT: tmp/test-governance/parallel
INPUT_REPORT_TYPE: ${{ env.QUALITY_GATE_REPORT_TYPE }}
INPUT_ENVIRONMENT: ${{ github.event_name == 'schedule' && env.QUALITY_GATE_SCHEDULED_ENVIRONMENT || inputs.environment }}
INPUT_EXPECTED_SCOPES: 'repo-tooling,repo-frontend,repo-frontend-react-doctor,repo-backend-static,repo-backend-fmt,repo-backend-image-llm-vision,repo-backend-official-i18n-seed,repo-backend-clippy-core-libs,repo-backend-clippy-runtime-storage,repo-backend-clippy-apps,repo-backend-test-core-libs,repo-backend-test-runtime-storage-fast,repo-backend-test-storage-postgres-1-of-4,repo-backend-test-storage-postgres-2-of-4,repo-backend-test-storage-postgres-3-of-4,repo-backend-test-storage-postgres-4-of-4,repo-backend-test-control-plane,repo-backend-test-api-server-1-of-4,repo-backend-test-api-server-2-of-4,repo-backend-test-api-server-3-of-4,repo-backend-test-api-server-4-of-4,repo-backend-test-plugin-runner,repo-backend-check-core-libs,repo-backend-check-runtime-storage,repo-backend-check-apps,backend-consistency-control-runtime,backend-consistency-storage,backend-consistency-api,coverage-frontend,coverage-backend-control-plane,coverage-backend-orchestration-runtime,coverage-backend-plugin-runner,coverage-backend-storage-postgres,container-images,ai-gateway-protocol-conformance,foundation-contracts'
INPUT_PUBLISH_ISSUE: "true"
INPUT_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-artifact@v6
if: always()
with:
name: test-governance-artifacts
path: tmp/test-governance