diff --git a/.gitea/workflows/ci-base-image.yml b/.gitea/workflows/ci-base-image.yml new file mode 100644 index 000000000..fe3c443e9 --- /dev/null +++ b/.gitea/workflows/ci-base-image.yml @@ -0,0 +1,105 @@ +name: CI Base Image Build + +on: + push: + branches: + - develop + - main + paths: + - 'requirements-base.txt' + - 'requirements-dev.txt' + - 'infra/docker/ci.Dockerfile' + workflow_dispatch: + inputs: + reason: + description: "触发原因" + required: false + default: "手动触发 - ci-base 镜像重建" + +concurrency: + group: ci-base-image-build + cancel-in-progress: false + +jobs: + build-ci-base: + name: Build CI Base Image + runs-on: runtime-builder + timeout-minutes: 60 + steps: + - name: Checkout code + shell: sh + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + curl -sH "Authorization: token $GITHUB_TOKEN" \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" \ + | bash + + - name: Docker login to Gitea Registry + shell: sh + env: + GITEA_REGISTRY_USER: xiaoxia + GITEA_REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + set -eu + for i in 1 2 3; do + echo "=== Docker login 尝试 $i/3 ===" + if docker login git.xiaoxiajianji.com -u "${GITEA_REGISTRY_USER}" -p "${GITEA_REGISTRY_TOKEN}"; then + echo "✅ Docker login successful" + break + fi + echo "❌ Docker login 失败(尝试 $i/3),5s 后重试..." + sleep 5 + done + + - name: Build and push CI base image + shell: sh + run: | + set -eu + IMAGE="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas/ci-base" + VERSION_TAG="deps-$(date +%Y%m%d-%H%M)-${GITHUB_SHA::8}" + + echo "=== Building CI base image (tags: latest, ${VERSION_TAG}) ===" + docker build --progress=plain \ + -f infra/docker/ci.Dockerfile \ + -t "${IMAGE}:latest" \ + -t "${IMAGE}:${VERSION_TAG}" \ + . + echo "✅ Image built successfully" + + echo "=== Pushing ${VERSION_TAG} ===" + docker push "${IMAGE}:${VERSION_TAG}" + echo "=== Pushing latest ===" + docker push "${IMAGE}:latest" + echo "✅ Pushed to Gitea Registry" + + - name: Verify image + shell: sh + run: | + set -eu + IMAGE="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas/ci-base:latest" + echo "=== Verifying pinned deps in fresh image ===" + docker run --rm "${IMAGE}" /opt/xiaoxia-ci-venv/bin/python -c \ + "import httpcore, h2, numpy, httpx; print('VERSIONS:', httpcore.__version__, h2.__version__, numpy.__version__, httpx.__version__)" + + - name: Notify result + if: always() + continue-on-error: true + shell: sh + env: + CI_NOTIFY_WEBHOOK: ${{ secrets.CI_NOTIFY_WEBHOOK }} + run: | + set +e + if [ "${{ job.status }}" = "success" ]; then + NOTIFY_MODE=success JOB_NAME="CI Base Image Build" python3 scripts/ci_notify.py + else + NOTIFY_MODE=failure JOB_NAME="CI Base Image Build" python3 scripts/ci_notify.py + fi + + - name: Cleanup + if: always() + shell: sh + run: | + IMAGE="git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas/ci-base" + docker rmi "${IMAGE}:latest" 2>/dev/null || true + echo "Cleanup done" diff --git a/tests/unit/test_mediakit_smart_clips.py b/tests/unit/test_mediakit_smart_clips.py index 5178fe36d..e06283548 100644 --- a/tests/unit/test_mediakit_smart_clips.py +++ b/tests/unit/test_mediakit_smart_clips.py @@ -446,8 +446,12 @@ class TestFromAssetsByTemplateSegments: ) clips_data = _get_clips_data(mock_plan_svc) - assert 3.0 <= clips_data[0]["duration"] <= 5.0 - assert 4.0 <= clips_data[1]["duration"] <= 8.0 + # PR #1614 转场补偿:2 个片段时每 clip 时长 +(2-1)*0.5/2=0.25s + # (xfade 重叠在渲染时扣除,故 clip 时长 = segment 随机时长 + 补偿), + # 断言上界需计入补偿与一位小数舍入余量 + comp = (2 - 1) * 0.5 / 2 + assert 3.0 + comp - 0.1 <= clips_data[0]["duration"] <= 5.0 + comp + 0.1 + assert 4.0 + comp - 0.1 <= clips_data[1]["duration"] <= 8.0 + comp + 0.1 def test_assets_balanced_assignment(self): """素材按使用次数贪心分配(使用少的优先),保证均衡使用。"""