Files
ai-contract-platform/.github/workflows/build-test-quality-gate.yml
T

226 lines
9.7 KiB
YAML

name: Build and Test Quality Gate
on:
pull_request:
push:
branches:
- main
- master
workflow_dispatch:
permissions:
contents: read
jobs:
web:
name: Contract Web typecheck and build
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- name: Install workspace dependencies
run: |
npm install -g npm@10.9.7
npm ci
- name: Typecheck Contract Web
run: npm run typecheck --workspace @yuqei/contract-web
- name: Test Contract Web critical routing and policy guards
run: npm test --workspace @yuqei/contract-web
- name: Build Contract Web
run: npm run build --workspace @yuqei/contract-web
kms-gateway:
name: Isolated Alibaba and Tencent KMS gateway
runs-on: ubuntu-latest
timeout-minutes: 20
env:
PIP_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PYTHONPATH: ${{ github.workspace }}/yuqei-contract-platform/services/kms-gateway/src
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install pinned KMS build toolchain
run: |
python -m pip install --only-binary=:all: --require-hashes \
--requirement yuqei-infra/requirements/kms-gateway-build-tools-py312.txt
- name: Build reproducible Alibaba compatibility wheels
run: |
mkdir -p "$RUNNER_TEMP/kms-fork-build" "$RUNNER_TEMP/kms-fork-wheels"
python yuqei-contract-platform/services/kms-gateway/vendor/alibaba-kms-modern/build_forks.py \
--work-dir "$RUNNER_TEMP/kms-fork-build" \
--wheel-dir "$RUNNER_TEMP/kms-fork-wheels" \
> "$RUNNER_TEMP/kms-fork-manifest.json"
python -m json.tool "$RUNNER_TEMP/kms-fork-manifest.json" > /dev/null
- name: Install hash-locked KMS dependency closure
run: |
mkdir -p "$RUNNER_TEMP/kms-runtime-inputs"
python -m pip download --no-build-isolation --no-deps --require-hashes \
--dest "$RUNNER_TEMP/kms-runtime-inputs" \
--requirement yuqei-infra/requirements/kms-gateway-runtime-linux-x86_64-py312.txt
python -m pip install --no-build-isolation --require-hashes \
--no-index --find-links "$RUNNER_TEMP/kms-runtime-inputs" \
--requirement yuqei-infra/requirements/kms-gateway-runtime-linux-x86_64-py312.txt
python -m pip install --no-deps "$RUNNER_TEMP/kms-fork-wheels"/*.whl
python -m pip install --only-binary=:all: --require-hashes \
--requirement yuqei-infra/requirements/kms-gateway-test-py312.txt
- name: Verify both official cloud SDKs and gateway contracts
working-directory: yuqei-contract-platform/services/kms-gateway
run: |
KMS_SITE_PACKAGES="$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')"
python vendor/verify_dependency_closure.py \
--project pyproject.toml \
--path "$KMS_SITE_PACKAGES" \
--forbid pyOpenSSL
python -c "import importlib.metadata as m, importlib.util; from alibabacloud_kms_kms20160120.client import Client as AliClient; from openapi_util.protobuf import api_pb2; from tencentcloud.kms.v20190118.kms_client import KmsClient as TencentClient; assert AliClient and TencentClient; assert m.version('alibabacloud-kms-python-sdk') == '1.1.3+yuqei.1'; assert m.version('alibabacloud-dkms-gcs') == '1.0.2+yuqei.2'; assert m.version('protobuf') == '6.33.6'; assert m.version('cryptography') == '50.0.2'; assert importlib.util.find_spec('OpenSSL') is None; request = api_pb2.EncryptRequest(KeyId='ci', Plaintext=b'payload'); assert api_pb2.EncryptRequest.FromString(request.SerializeToString()) == request"
python -m pip check
python -m pytest -q
- name: Audit the installed KMS environment
run: |
python -m venv "$RUNNER_TEMP/kms-audit-tool"
"$RUNNER_TEMP/kms-audit-tool/bin/python" -m pip install \
--only-binary=:all: --require-hashes \
--requirement yuqei-infra/requirements/kms-gateway-audit-tools-linux-x86_64-py312.txt
KMS_SITE_PACKAGES="$(python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')"
KMS_AUDIT_SITE_PACKAGES="$("$RUNNER_TEMP/kms-audit-tool/bin/python" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')"
"$RUNNER_TEMP/kms-audit-tool/bin/python" -m pip_audit \
--strict --vulnerability-service osv --progress-spinner off \
--path "$KMS_SITE_PACKAGES" \
--format json --output "$RUNNER_TEMP/kms-pip-audit.json"
"$RUNNER_TEMP/kms-audit-tool/bin/python" -m pip_audit \
--strict --vulnerability-service osv --progress-spinner off \
--path "$KMS_AUDIT_SITE_PACKAGES" \
--format json --output "$RUNNER_TEMP/kms-audit-tool-pip-audit.json"
- name: Upload KMS dependency evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: kms-gateway-dependency-evidence
path: |
${{ runner.temp }}/kms-fork-manifest.json
${{ runner.temp }}/kms-pip-audit.json
${{ runner.temp }}/kms-audit-tool-pip-audit.json
if-no-files-found: warn
python:
name: Python tests and Contract API migration
runs-on: ubuntu-latest
timeout-minutes: 25
env:
PIP_INDEX_URL: https://pypi.tuna.tsinghua.edu.cn/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
CONTRACT_API_ENVIRONMENT: test
CONTRACT_API_DATABASE_URL: "sqlite+pysqlite:///:memory:"
CONTRACT_API_DATABASE_AUTO_CREATE: "true"
CONTRACT_API_SEED_DEMO_DATA: "false"
AI_PLATFORM_ENVIRONMENT: test
AI_PLATFORM_DATABASE_URL: "sqlite+pysqlite:///:memory:"
AI_PLATFORM_DATABASE_AUTO_CREATE: "true"
AI_PLATFORM_REPOSITORY_BACKEND: sqlalchemy
YUQEI_WORKER_ENVIRONMENT: test
YUQEI_WORKER_DATABASE_URL: "sqlite+pysqlite:///:memory:"
YUQEI_WORKER_DATABASE_AUTO_CREATE: "true"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install Python projects and test runner
run: |
python -m pip install --upgrade pip
python -m pip install pytest \
-e yuqei-contract-platform/services/contract-api \
-e yuqei-ai-platform/services/ai-platform-api \
-e yuqei-ai-platform/services/worker \
-e yuqei-ai-sdk-python
- name: Test Contract API
env:
TMPDIR: /dev/shm/yuqei-contract-tests
YUQEI_TEST_CLEAN_TMP_PATHS: "true"
run: |
mkdir -p "$TMPDIR"
python -m pytest yuqei-contract-platform/services/contract-api/tests -q
- name: Test P0 critical launch journeys
env:
TMPDIR: /dev/shm/yuqei-p0-tests
YUQEI_TEST_CLEAN_TMP_PATHS: "true"
run: |
mkdir -p "$TMPDIR"
python -m pytest \
yuqei-contract-platform/services/contract-api/tests/test_contract_templates.py \
yuqei-contract-platform/services/contract-api/tests/test_contract_delivery_quality_gate.py \
yuqei-contract-platform/services/contract-api/tests/test_tenant_isolation.py \
yuqei-contract-platform/services/contract-api/tests/test_public_policy_promotion.py \
yuqei-contract-platform/services/contract-api/tests/test_p0_launch_critical_journeys.py \
yuqei-ai-platform/services/ai-platform-api/tests/test_public_ai_disclosure.py \
-q
- name: Test AI Platform API
run: python -m pytest yuqei-ai-platform/services/ai-platform-api/tests -q
- name: Test AI worker
run: python -m pytest yuqei-ai-platform/services/worker/tests -q
- name: Test Python SDK
run: python -m pytest yuqei-ai-sdk-python/tests -q
- name: Test production release tools
run: python -m pytest yuqei-infra/tests -q
- name: Verify Contract API empty-database migrations
working-directory: yuqei-contract-platform/services/contract-api
env:
CONTRACT_API_DATABASE_URL: sqlite+pysqlite:////tmp/yuqei-contract-api-ci.sqlite3
CONTRACT_API_DATABASE_AUTO_CREATE: "false"
run: |
expected_head="$(python -m alembic heads | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$(printf '%s\n' "$expected_head" | sed '/^$/d' | wc -l)" -eq 1
python -m alembic upgrade head
python -m alembic downgrade 20260710_0030
python -m alembic upgrade head
actual_head="$(python -m alembic current | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$actual_head" = "$expected_head"
- name: Verify AI Platform API empty-database migrations
working-directory: yuqei-ai-platform/services/ai-platform-api
env:
AI_PLATFORM_DATABASE_URL: sqlite+pysqlite:////tmp/yuqei-ai-platform-api-ci.sqlite3
AI_PLATFORM_DATABASE_AUTO_CREATE: "false"
AI_PLATFORM_REPOSITORY_BACKEND: sqlalchemy
run: |
expected_head="$(python -m alembic heads | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$(printf '%s\n' "$expected_head" | sed '/^$/d' | wc -l)" -eq 1
python -m alembic upgrade head
actual_head="$(python -m alembic current | sed -nE 's/^([^ ]+) \(head\)$/\1/p')"
test "$actual_head" = "$expected_head"