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"