Files
ai-contract-platform/.github/workflows/provider-promotion.yml
T
yuqei ff6ea2877e
Build and Test Quality Gate / Contract Web typecheck and build (pull_request) Successful in 3m58s
Build and Test Quality Gate / Python tests and Contract API migration (pull_request) Failing after 29m6s
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Successful in 15m55s
feat: close provider promotion and support access gates
2026-08-11 15:21:14 +08:00

200 lines
7.8 KiB
YAML

name: Protected Provider Promotion
on:
workflow_dispatch:
inputs:
phase:
description: "Collect technical evidence or finalize a legally signed package"
required: true
type: choice
options:
- collect
- finalize
shared_staging_api_base:
description: "Authenticated shared-staging AI Platform URL"
required: true
default: "https://staging.example.invalid/ai"
tenant_id:
description: "Acceptance tenant"
required: true
default: "default"
technical_run_id:
description: "Collect workflow run id (required for finalize)"
required: false
default: ""
legal_evidence_host:
description: "Controlled shared evidence hostname (required for finalize)"
required: false
default: ""
receipt_key_id:
description: "Trusted receipt key id (required for finalize)"
required: false
default: ""
target_release_id:
description: "Immutable target release id (required for finalize)"
required: false
default: ""
target_image_digest:
description: "Immutable target AI Platform image digest (required for finalize)"
required: false
default: ""
permissions:
contents: read
actions: read
concurrency:
group: provider-promotion-${{ inputs.tenant_id }}
cancel-in-progress: false
jobs:
collect:
if: inputs.phase == 'collect'
runs-on: [self-hosted, yuqei-smoke]
environment: shared-staging-provider-promotion
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
with:
clean: true
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install locked AI Platform package
shell: bash
run: python -m pip install ./yuqei-ai-platform/services/ai-platform-api
- name: Collect deterministic legal review package
shell: bash
env:
SERVICE_KEY: ${{ secrets.AI_PLATFORM_SERVICE_KEY }}
SHARED_STAGING_API_BASE: ${{ inputs.shared_staging_api_base }}
TENANT_ID: ${{ inputs.tenant_id }}
run: |
set -euo pipefail
umask 077
test -n "$SERVICE_KEY"
mkdir -p .promotion-private promotion-output
printf '%s' "$SERVICE_KEY" > .promotion-private/service-key
python yuqei-infra/scripts/run-ai-provider-acceptance.py \
--mode collect \
--base-url "$SHARED_STAGING_API_BASE" \
--tenant-id "$TENANT_ID" \
--service-key-file .promotion-private/service-key \
--artifact-dir promotion-output \
--review-package-file promotion-output/provider-technical-review-package.json
- name: Upload allowlisted review artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: provider-technical-review
if-no-files-found: error
retention-days: 14
path: |
promotion-output/provider-technical-review-package.json
promotion-output/ai-provider-acceptance-summary.json
promotion-output/ai-provider-acceptance-summary.md
- name: Remove private inputs
if: always()
shell: bash
run: rm -f .promotion-private/service-key
finalize:
if: inputs.phase == 'finalize'
runs-on: [self-hosted, yuqei-smoke]
environment: shared-staging-provider-promotion
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
clean: true
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Validate protected inputs
shell: bash
env:
TECHNICAL_RUN_ID: ${{ inputs.technical_run_id }}
LEGAL_EVIDENCE_HOST: ${{ inputs.legal_evidence_host }}
RECEIPT_KEY_ID: ${{ inputs.receipt_key_id }}
TARGET_RELEASE_ID: ${{ inputs.target_release_id }}
TARGET_IMAGE_DIGEST: ${{ inputs.target_image_digest }}
run: |
set -euo pipefail
test -n "$TECHNICAL_RUN_ID"
test -n "$LEGAL_EVIDENCE_HOST"
test -n "$RECEIPT_KEY_ID"
test -n "$TARGET_RELEASE_ID"
test -n "$TARGET_IMAGE_DIGEST"
- name: Download exact technical package
uses: actions/download-artifact@v4
with:
name: provider-technical-review
run-id: ${{ inputs.technical_run_id }}
github-token: ${{ github.token }}
path: technical-input
- name: Install locked AI Platform package
shell: bash
run: python -m pip install ./yuqei-ai-platform/services/ai-platform-api
- name: Finalize signed promotion without rerunning Provider
shell: bash
env:
SERVICE_KEY: ${{ secrets.AI_PLATFORM_SERVICE_KEY }}
LEGAL_APPROVAL_B64: ${{ secrets.PROVIDER_LEGAL_APPROVAL_JSON_B64 }}
LEGAL_KEYRING_B64: ${{ secrets.PROVIDER_LEGAL_KEYRING_JSON_B64 }}
RECEIPT_PRIVATE_KEY_B64: ${{ secrets.PROVIDER_RECEIPT_PRIVATE_KEY_PEM_B64 }}
SHARED_STAGING_API_BASE: ${{ inputs.shared_staging_api_base }}
TENANT_ID: ${{ inputs.tenant_id }}
LEGAL_EVIDENCE_HOST: ${{ inputs.legal_evidence_host }}
RECEIPT_KEY_ID: ${{ inputs.receipt_key_id }}
TARGET_RELEASE_ID: ${{ inputs.target_release_id }}
TARGET_IMAGE_DIGEST: ${{ inputs.target_image_digest }}
run: |
set -euo pipefail
umask 077
test -n "$SERVICE_KEY"
test -n "$LEGAL_APPROVAL_B64"
test -n "$LEGAL_KEYRING_B64"
test -n "$RECEIPT_PRIVATE_KEY_B64"
mkdir -p .promotion-private promotion-output
printf '%s' "$SERVICE_KEY" > .promotion-private/service-key
printf '%s' "$LEGAL_APPROVAL_B64" | base64 --decode > .promotion-private/legal-approval.json
printf '%s' "$LEGAL_KEYRING_B64" | base64 --decode > .promotion-private/legal-keyring.json
printf '%s' "$RECEIPT_PRIVATE_KEY_B64" | base64 --decode > .promotion-private/receipt-private-key.pem
python yuqei-infra/scripts/run-ai-provider-acceptance.py \
--mode finalize \
--base-url "$SHARED_STAGING_API_BASE" \
--tenant-id "$TENANT_ID" \
--service-key-file .promotion-private/service-key \
--artifact-dir promotion-output \
--review-package-file technical-input/provider-technical-review-package.json \
--legal-approval-file .promotion-private/legal-approval.json \
--legal-keyring-file .promotion-private/legal-keyring.json \
--legal-evidence-allowed-host "$LEGAL_EVIDENCE_HOST" \
--receipt-private-key-file .promotion-private/receipt-private-key.pem \
--receipt-key-id "$RECEIPT_KEY_ID" \
--target-release-id "$TARGET_RELEASE_ID" \
--target-image-digest "$TARGET_IMAGE_DIGEST" \
--receipt-output promotion-output/provider-acceptance-receipt.json \
--promotion-bundle-output promotion-output/provider-promotion-bundle.json
- name: Upload allowlisted promotion artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: provider-promotion-bundle
if-no-files-found: error
retention-days: 30
path: |
promotion-output/provider-acceptance-receipt.json
promotion-output/provider-promotion-bundle.json
promotion-output/ai-provider-acceptance-summary.json
promotion-output/ai-provider-acceptance-summary.md
- name: Remove private inputs
if: always()
shell: bash
run: |
rm -f \
.promotion-private/service-key \
.promotion-private/legal-approval.json \
.promotion-private/legal-keyring.json \
.promotion-private/receipt-private-key.pem