feat(desktop): add export-storyboard-image tool #3137
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: Build and Test | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - feature/desktop | |
| - 'claude/**' | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: ['>=22.7.5 <23', '24.x'] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'npm' | |
| - name: Set up Docker | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: TypeScript compilation | |
| run: npx tsc | |
| - name: Lint | |
| run: npm run lint | |
| # Byte-locked engine files shared with agent-to-tableau-desktop. This gate was | |
| # only in scripts/agent-check, so drift rode through eight merged PRs. | |
| - name: Lockstep-core hash gate | |
| run: node scripts/check-lockstep.mjs | |
| - name: Build | |
| run: npm run build | |
| # --dirty: the plain build:desktop CLEANS ./build first, deleting the | |
| # build/index.js the default build just produced — the e2e suites spawn | |
| # `node build/index.js` children, which then die pre-handshake | |
| # ("Connection closed") until server.test.ts happens to rebuild it | |
| # mid-pool. Root-caused 2026-07-15 on the resync PR (#504). | |
| - name: Build desktop variant | |
| run: npm run build:desktop:dirty | |
| - name: Build Docker image | |
| run: npm run build:docker | |
| - name: Build NPM package | |
| run: npm pack | |
| - name: Tests | |
| run: npx vitest run --config ./vitest.config.ts --maxWorkers=2 | |
| # Coverage is informational (fail_below_min is false below). Vitest 3 can exit 1 | |
| # after every test passes when its fixed worker watchdog fires during coverage; | |
| # the required test step above remains authoritative, and the report step still | |
| # fails if this command does not produce the Cobertura artifact. | |
| - name: Generate coverage report | |
| if: matrix.node-version == '>=22.7.5 <23' | |
| continue-on-error: true | |
| run: npm run coverage | |
| - name: E2E Tests | |
| if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' | |
| uses: ./.github/actions/run-e2e-tests | |
| env: | |
| SERVER: ${{ secrets.E2E_TEST_SERVER }} | |
| SITE_NAME: ${{ secrets.E2E_TEST_SITE_NAME }} | |
| AUTH: ${{ secrets.E2E_TEST_AUTH }} | |
| JWT_SUB_CLAIM: ${{ secrets.E2E_TEST_JWT_SUB_CLAIM }} | |
| CONNECTED_APP_CLIENT_ID: ${{ secrets.E2E_TEST_CONNECTED_APP_CLIENT_ID }} | |
| CONNECTED_APP_SECRET_ID: ${{ secrets.E2E_TEST_CONNECTED_APP_SECRET_ID }} | |
| CONNECTED_APP_SECRET_VALUE: ${{ secrets.E2E_TEST_CONNECTED_APP_SECRET_VALUE }} | |
| - name: OAuth Tests (Embedded authz server) | |
| if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' | |
| uses: ./.github/actions/run-oauth-embedded-authz-tests | |
| env: | |
| SERVER: ${{ secrets.E2E_TEST_SERVER }} | |
| SITE_NAME: ${{ secrets.E2E_TEST_SITE_NAME }} | |
| OAUTH_JWE_PRIVATE_KEY: ${{ secrets.OAUTH_TEST_OAUTH_JWE_PRIVATE_KEY }} | |
| OAUTH_JWE_PRIVATE_KEY_PASSPHRASE: ${{ secrets.OAUTH_TEST_OAUTH_JWE_PRIVATE_KEY_PASSPHRASE }} | |
| # Skipped on Node 24: Playwright browser launch stalls indefinitely under that runtime. | |
| # I gave Claude the CI logs and it said: | |
| # Root cause: Node.js 24 ships with an updated undici-backed fetch and changed AbortSignal propagation behaviour. | |
| # Playwright's browser installer pipes the download stream directly into an extraction process (unzip/tar). On Node | |
| # 24, the internal AbortController that Playwright uses to manage the download lifetime fires during the hand-off | |
| # between the completed download stream and the extraction subprocess — canceling the operation even though the bytes | |
| # arrived. | |
| # The result is an error that looks like a network failure but is actually a signal-propagation race in the install | |
| # script between Node 24's new stream/abort semantics and Playwright's child_process usage for extraction. | |
| # Why it appeared to "stall" rather than fail fast: GitHub Actions has a default job timeout of 6 hours. If the cancel | |
| # error was swallowed or the extraction process hung instead of exiting, the job would sit silently until that | |
| # timeout — which matches the "stalls indefinitely" symptom you saw. | |
| # The fix you already applied is correct: pinning to >=22.7.5 <23 sidesteps the Node 24 regression entirely. Once | |
| # Playwright ships a version that handles Node 24's abort semantics correctly you can re-enable it, but there's | |
| # nothing to change on your side right now. | |
| - name: OAuth Tests (Tableau authz server) | |
| if: matrix.node-version == '>=22.7.5 <23' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' | |
| uses: ./.github/actions/run-oauth-tableau-authz-tests | |
| env: | |
| SERVER: ${{ secrets.E2E_TEST_SERVER }} | |
| TEST_USER: ${{ secrets.E2E_TEST_USER }} | |
| TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} | |
| TEST_SITE_NAME: ${{ secrets.E2E_TEST_SITE_NAME }} | |
| - name: Build Claude MCP Bundle | |
| run: | | |
| npm run build:manifest | |
| npm ci --omit=dev | |
| npx -y @anthropic-ai/mcpb pack . tableau-mcp.mcpb | |
| - name: Report code coverage | |
| if: always() && matrix.node-version == '>=22.7.5 <23' | |
| uses: irongut/CodeCoverageSummary@v1.3.0 | |
| with: | |
| filename: coverage/**/cobertura-coverage.xml | |
| badge: true | |
| fail_below_min: false | |
| format: markdown | |
| hide_branch_rate: false | |
| hide_complexity: true | |
| indicators: true | |
| output: both | |
| thresholds: '60 80' | |
| - name: Upload artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: artifacts-node${{ matrix.node-version == '>=22.7.5 <23' && '22.x' || matrix.node-version }} | |
| if-no-files-found: error | |
| path: | | |
| build/ | |
| junit/ | |
| playwright-report/ | |
| tableau-mcp.mcpb |