fix(engine): reject non-finite, negative and malformed frame rates #19132
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: regression | |
| permissions: | |
| contents: read | |
| # Suppress hyperframes CLI telemetry from HeyGen's own CI runs. | |
| # External users' CI continues to emit telemetry unless they set this themselves. | |
| env: | |
| HYPERFRAMES_NO_TELEMETRY: "1" | |
| # Graphite can update a branch and its PR base separately for the same head | |
| # SHA. Keep only the newest expensive regression run for each PR/ref. | |
| concurrency: | |
| group: regression-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| on: | |
| pull_request: | |
| # Re-run when Graphite changes only the PR base for the same head SHA. | |
| types: [opened, synchronize, reopened, edited] | |
| push: | |
| branches: | |
| - main | |
| jobs: | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| matrix: ${{ steps.shards.outputs.matrix }} | |
| steps: | |
| # Force git-based change detection instead of the pull_request REST API. | |
| # The API path can fail the whole workflow on transient listFiles | |
| # timeouts before any regression shard even starts. | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4 | |
| id: filter | |
| with: | |
| token: "" | |
| filters: | | |
| code: | |
| - "packages/core/**" | |
| - "packages/producer/**" | |
| - "packages/engine/**" | |
| - "Dockerfile*" | |
| # Bin-pack the shard matrix from recorded per-fixture timings rather than | |
| # a hand-written list. Fails if any fixture on disk is neither scheduled | |
| # nor explicitly excluded, so a new fixture cannot silently never run. | |
| - name: Plan regression shards | |
| id: shards | |
| run: | | |
| echo "matrix=$(node packages/producer/scripts/plan-regression-shards.mjs)" >> "$GITHUB_OUTPUT" | |
| node packages/producer/scripts/plan-regression-shards.mjs --pretty | |
| preflight: | |
| name: Preflight (lint + format) | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: ./.github/actions/preflight | |
| regression-shards: | |
| needs: [changes, preflight] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: true | |
| # Bin-packed at run time from packages/producer/tests/shard-schedule.json. | |
| # To rebalance, refresh the timings in that file — no YAML edit needed. | |
| # To change shard count, set "shardCount" there. | |
| matrix: ${{ fromJSON(needs.changes.outputs.matrix) }} | |
| steps: | |
| - name: Checkout (with LFS) | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| lfs: true | |
| - name: Validate LFS files | |
| run: | | |
| echo "Checking golden baseline MP4s are real files (not LFS pointers)..." | |
| for mp4 in packages/producer/tests/*/output/output.mp4; do | |
| if [ -f "$mp4" ]; then | |
| size=$(stat --format=%s "$mp4") | |
| if [ "$size" -lt 1000 ]; then | |
| echo "ERROR: $mp4 appears to be an LFS pointer ($size bytes)" | |
| exit 1 | |
| fi | |
| echo "OK: $mp4 ($size bytes)" | |
| fi | |
| done | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 | |
| - name: Build test Docker image (cached) | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 | |
| with: | |
| context: . | |
| file: Dockerfile.test | |
| load: true | |
| tags: hyperframes-producer:test | |
| cache-from: type=gha,scope=regression-test-image | |
| # PR matrices can fan out across many stacked branches at once. Let | |
| # them consume the shared cache, but keep a single writer on main so | |
| # concurrent exports cannot exhaust the Actions cache service. | |
| cache-to: ${{ github.event_name == 'push' && 'type=gha,mode=max,scope=regression-test-image' || '' }} | |
| - name: "Run regression shard: ${{ matrix.shard }} (${{ matrix.mode }})" | |
| run: | | |
| echo "Shard: ${{ matrix.shard }}" | |
| echo "Mode: ${{ matrix.mode }}" | |
| echo "Args: ${{ matrix.args }}" | |
| docker run --rm \ | |
| --security-opt seccomp=unconfined \ | |
| --shm-size=4g \ | |
| -v ${{ github.workspace }}/packages/producer/tests:/app/packages/producer/tests \ | |
| hyperframes-producer:test \ | |
| --mode=${{ matrix.mode }} \ | |
| ${{ matrix.args }} | |
| - name: Upload failure artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: regression-failures-${{ matrix.shard }} | |
| path: packages/producer/tests/*/failures/ | |
| if-no-files-found: ignore | |
| # Summary job — matches the required check name in branch protection | |
| regression: | |
| runs-on: ubuntu-latest | |
| needs: [changes, regression-shards] | |
| if: always() | |
| steps: | |
| - name: Check results | |
| run: | | |
| if [ "${{ needs.changes.outputs.code }}" != "true" ]; then | |
| echo "No code changes — skipping regression (auto-pass)" | |
| exit 0 | |
| fi | |
| if [ "${{ needs.regression-shards.result }}" != "success" ]; then | |
| echo "One or more regression shards failed" | |
| exit 1 | |
| fi |