|
36 | 36 | skills: ${{ steps.filter.outputs.skills }} |
37 | 37 | codex_plugin: ${{ steps.filter.outputs.codex_plugin }} |
38 | 38 | gcp_beginframe: ${{ steps.filter.outputs.gcp_beginframe }} |
| 39 | + studio: ${{ steps.filter.outputs.studio }} |
39 | 40 | steps: |
40 | 41 | # Force git-based change detection instead of the pull_request REST API. |
41 | 42 | # The API path can fail the whole workflow on transient listFiles |
|
77 | 78 | - "scripts/package-codex-plugin.mjs" |
78 | 79 | - "package.json" |
79 | 80 | - ".github/workflows/ci.yml" |
| 81 | + studio: |
| 82 | + - "packages/studio/**" |
| 83 | + - "packages/core/**" |
| 84 | + - "packages/studio-server/**" |
| 85 | + - "bun.lock" |
| 86 | + - ".github/workflows/ci.yml" |
80 | 87 | gcp_beginframe: |
81 | 88 | - "packages/gcp-cloud-run/Dockerfile" |
82 | 89 | - "packages/aws-lambda/scripts/probe-beginframe.ts" |
@@ -496,6 +503,97 @@ jobs: |
496 | 503 |
|
497 | 504 | kill $SERVER_PID 2>/dev/null || true |
498 | 505 |
|
| 506 | + studio-timeline-viewport: |
| 507 | + name: "Studio: timeline viewport gate" |
| 508 | + needs: [changes] |
| 509 | + if: needs.changes.outputs.studio == 'true' |
| 510 | + runs-on: ubuntu-latest |
| 511 | + timeout-minutes: 12 |
| 512 | + steps: |
| 513 | + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 |
| 514 | + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 |
| 515 | + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 |
| 516 | + with: |
| 517 | + node-version: 22 |
| 518 | + - run: bun install --frozen-lockfile |
| 519 | + # Same reason as studio-load-smoke: vite.config.ts is loaded by Node and |
| 520 | + # resolves the workspace packages through their "node" export condition. |
| 521 | + - run: bun run --filter '@hyperframes/{parsers,lint,studio-server}' build |
| 522 | + - run: bun run --cwd packages/core build |
| 523 | + - run: bun run --cwd packages/core build:hyperframes-runtime |
| 524 | + - name: Install the fixture as a Studio project |
| 525 | + # Studio resolves projects from packages/studio/data/projects, which is |
| 526 | + # gitignored. The fixture composition is tracked under tests/e2e, so |
| 527 | + # copy it into place rather than committing a project directory. |
| 528 | + run: | |
| 529 | + mkdir -p packages/studio/data/projects |
| 530 | + cp -R packages/studio/tests/e2e/fixtures/timeline-virtualization \ |
| 531 | + packages/studio/data/projects/timeline-virtualization |
| 532 | + - name: Run both arms of the timeline viewport gate |
| 533 | + run: | |
| 534 | + set -euo pipefail |
| 535 | +
|
| 536 | + # Two servers, because row virtualization is read from import.meta.env |
| 537 | + # at module load: one process cannot serve both builds. |
| 538 | + bun run --cwd packages/studio dev -- --port 5313 --strictPort & |
| 539 | + DEFAULT_PID=$! |
| 540 | + VITE_STUDIO_TIMELINE_ROW_VIRTUALIZATION_ENABLED=1 \ |
| 541 | + bun run --cwd packages/studio dev -- --port 5314 --strictPort & |
| 542 | + VIRTUALIZED_PID=$! |
| 543 | + trap 'kill $DEFAULT_PID $VIRTUALIZED_PID 2>/dev/null || true' EXIT |
| 544 | +
|
| 545 | + for i in $(seq 1 60); do |
| 546 | + if curl -sf http://localhost:5313/ >/dev/null 2>&1 \ |
| 547 | + && curl -sf http://localhost:5314/ >/dev/null 2>&1; then break; fi |
| 548 | + sleep 1 |
| 549 | + done |
| 550 | + if ! curl -sf http://localhost:5313/ >/dev/null 2>&1 \ |
| 551 | + || ! curl -sf http://localhost:5314/ >/dev/null 2>&1; then |
| 552 | + echo "FAIL: studio dev servers did not start" |
| 553 | + exit 1 |
| 554 | + fi |
| 555 | +
|
| 556 | + # The default build first. It is the one users get, and the arm that |
| 557 | + # caught the regression this gate exists for. |
| 558 | + # Capture both statuses so either failure still leaves two evidence files. |
| 559 | + DEFAULT_STATUS=0 |
| 560 | + STUDIO_URL="http://localhost:5313/#project/timeline-virtualization" \ |
| 561 | + TIMELINE_ROW_VIRTUALIZATION=off \ |
| 562 | + TIMELINE_ELEMENT_COUNT=1000 \ |
| 563 | + TIMELINE_TIER=ci \ |
| 564 | + node packages/studio/tests/e2e/timeline-virtualization.mjs \ |
| 565 | + | tee /tmp/timeline-gate-default.json \ |
| 566 | + || DEFAULT_STATUS=$? |
| 567 | +
|
| 568 | + VIRTUALIZED_STATUS=0 |
| 569 | + STUDIO_URL="http://localhost:5314/#project/timeline-virtualization" \ |
| 570 | + TIMELINE_ROW_VIRTUALIZATION=on \ |
| 571 | + TIMELINE_ELEMENT_COUNT=50000 \ |
| 572 | + TIMELINE_TIER=ci \ |
| 573 | + node packages/studio/tests/e2e/timeline-virtualization.mjs \ |
| 574 | + | tee /tmp/timeline-gate-virtualized.json \ |
| 575 | + || VIRTUALIZED_STATUS=$? |
| 576 | +
|
| 577 | + { |
| 578 | + echo "### Timeline viewport gate" |
| 579 | + echo "- Default arm exit: ${DEFAULT_STATUS}" |
| 580 | + echo "- Virtualized arm exit: ${VIRTUALIZED_STATUS}" |
| 581 | + } >> "$GITHUB_STEP_SUMMARY" |
| 582 | +
|
| 583 | + if (( DEFAULT_STATUS != 0 || VIRTUALIZED_STATUS != 0 )); then |
| 584 | + echo "FAIL: default=${DEFAULT_STATUS}, virtualized=${VIRTUALIZED_STATUS}" |
| 585 | + exit 1 |
| 586 | + fi |
| 587 | + - name: Upload gate evidence |
| 588 | + # The gate's whole output is machine-readable evidence, and a red run is |
| 589 | + # exactly when someone needs to read it. Keep it on failure too. |
| 590 | + if: always() |
| 591 | + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 |
| 592 | + with: |
| 593 | + name: timeline-viewport-gate-evidence |
| 594 | + path: /tmp/timeline-gate-*.json |
| 595 | + if-no-files-found: error |
| 596 | + |
499 | 597 | smoke-global-install: |
500 | 598 | name: "Smoke: global install" |
501 | 599 | needs: [changes, build] |
|
0 commit comments