Files
yyj 7c35863f62 ci: add model E2E acceptance for community PRs and PyPI releases (#2195)
* ci: add isolated model E2E acceptance for PR wheels and PyPI

* ci: recheck GPU occupancy before each model starts

* ci: disable inherited container healthchecks during acceptance

* ci: wait for free GPUs before native import validation

* docs: record qj5090 acceptance infrastructure validation

* ci: add immutable candidate and public PyPI release acceptance gates
2026-09-11 00:50:36 +08:00

184 lines
6.3 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Model E2E acceptance (PR / PyPI)
# Separate from release workflows: this workflow cannot upload packages.
# Only maintainers dispatch reviewed SHAs; never execute pull_request_target code.
on:
workflow_dispatch:
inputs:
mode:
description: '验收对象:pr 为候选 wheels,pypi 为公开安装'
type: choice
options: [pypi, pr]
default: pypi
version:
description: 'PyPI 的 ktransformers 版本;latest 按用户默认安装'
type: string
default: latest
pr_repository:
description: 'PR 所属四仓之一'
type: choice
options: [kvcache-ai/ktransformers, kvcache-ai/sglang, kvcache-ai/transformers, kvcache-ai/accelerate]
default: kvcache-ai/ktransformers
pr_number:
description: 'PR 编号(pr 模式必填)'
type: string
default: ''
pr_sha:
description: '已审阅批准的完整 PR head SHA(pr 模式必填)'
type: string
default: ''
build_run_id:
description: '可信 main 构建流程的 run ID,包含 model-e2e-candidate artifact'
type: string
default: ''
workflow_call:
inputs:
mode:
type: string
default: pypi
version:
type: string
default: latest
pr_repository:
type: string
default: kvcache-ai/ktransformers
pr_number:
type: string
default: ''
pr_sha:
type: string
default: ''
build_run_id:
type: string
default: ''
permissions:
contents: read
# Deliberately no shared Actions concurrency group: GitHub may replace an
# existing pending run. The runner queue + host-wide flock preserve waiting jobs.
jobs:
prepare:
if: github.repository == 'kvcache-ai/ktransformers' && github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Freeze the approved test identity (no PR code execution)
env:
GH_TOKEN: ${{ github.token }}
ENABLED: ${{ vars.KT_MODEL_E2E_ENABLED }}
MODE: ${{ inputs.mode }}
VERSION: ${{ inputs.version }}
PR_REPOSITORY: ${{ inputs.pr_repository }}
PR_NUMBER: ${{ inputs.pr_number }}
PR_SHA: ${{ inputs.pr_sha }}
BUILD_RUN_ID: ${{ inputs.build_run_id }}
BUILD_WORKFLOW: ${{ vars.KT_MODEL_E2E_BUILD_WORKFLOW }}
run: |
python .github/model-e2e/prepare.py
mkdir input
mv request.json input/request.json
- name: Download final candidates from the approved build
if: inputs.mode == 'pr'
uses: actions/download-artifact@v4
with:
name: model-e2e-candidate
path: input/candidate
run-id: ${{ inputs.build_run_id }}
github-token: ${{ github.token }}
- name: Check wheel hashes and source lock
if: inputs.mode == 'pr'
run: |
python - <<'PY'
import json, sys
sys.path.insert(0, '.github/model-e2e')
from contracts import verify_candidate
verify_candidate('input/candidate', json.load(open('input/request.json')))
PY
- uses: actions/upload-artifact@v4
with:
name: model-e2e-input-${{ github.run_id }}-${{ github.run_attempt }}
path: input/
if-no-files-found: error
retention-days: 7
gpu:
needs: prepare
# An administrator must provision the no-credentials runner account and
# protected environment before assigning this new label. Existing release
# runners must NOT receive the label without the documented isolation audit.
environment: qj5090-model-e2e
runs-on: [self-hosted, linux, x64, gpu, kt-model-e2e]
timeout-minutes: 1100
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
path: harness
- uses: actions/download-artifact@v4
with:
name: model-e2e-input-${{ github.run_id }}-${{ github.run_attempt }}
path: input
- name: Queue, install fresh packages, and run the three model tests
env:
HOST_CONFIG: ${{ vars.KT_MODEL_E2E_HOST_CONFIG || '/etc/ktransformers/model-e2e/qj5090.json' }}
CONTROLLER_PYTHON: ${{ vars.KT_MODEL_E2E_PYTHON || 'python3.12' }}
run: |
set -euo pipefail
test "$("$CONTROLLER_PYTHON" -c 'import sys; print(sys.version_info >= (3, 11))')" = True
test_root="$(mktemp -d "$RUNNER_TEMP/kt-model-e2e.XXXXXXXX")"
echo "E2E_OUTPUT=$test_root/output" >> "$GITHUB_ENV"
"$CONTROLLER_PYTHON" harness/.github/model-e2e/host.py \
--config "$HOST_CONFIG" --input "$GITHUB_WORKSPACE/input" \
--output "$test_root/output"
- name: Retain evidence only (not model weights, adapters, or the venv)
if: always() && env.E2E_OUTPUT != ''
uses: actions/upload-artifact@v4
with:
name: model-e2e-evidence-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ env.E2E_OUTPUT }}/evidence/
if-no-files-found: warn
retention-days: 30
report:
if: always()
needs: [prepare, gpu]
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
actions: read
statuses: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/download-artifact@v4
if: needs.prepare.result == 'success'
with:
name: model-e2e-input-${{ github.run_id }}-${{ github.run_attempt }}
path: input
- uses: actions/download-artifact@v4
if: needs.gpu.result == 'success' || needs.gpu.result == 'failure'
continue-on-error: true
with:
name: model-e2e-evidence-${{ github.run_id }}-${{ github.run_attempt }}
path: evidence
- name: Summarize and report to the approved KT PR SHA
env:
GH_TOKEN: ${{ github.token }}
GPU_JOB_RESULT: ${{ needs.gpu.result }}
run: python3 .github/model-e2e/report.py