name: 'E2E Tests' on: # E2E is slow and currently flaky, so it is NOT in the merge queue (gating the # serial queue on it would stall every merge). It runs post-merge on `main`, # plus a nightly full regression and on-demand. Promote it to `merge_group` + # required once a stable subset is carved out. push: branches: - 'main' - 'feat/e2e/**' schedule: - cron: '0 4 * * *' # nightly full regression (~04:00 UTC), guaranteed signal workflow_dispatch: concurrency: # Scope the group by event so back-to-back pushes to `main` cancel superseded # runs (no pile-up when PRs merge in quick succession), without ever cancelling # the nightly schedule or a manual dispatch — they share the ref but not the # event. feat/e2e/** pushes still cancel their own superseded runs. group: |- ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref_name }} # Cancel superseded pushes (main included now — only the latest tree matters, # and it covers every merged change). The nightly run always finishes, so a # busy merge window that keeps cancelling the push run still gets a signal. cancel-in-progress: |- ${{ github.event_name == 'push' }} jobs: e2e-test-linux: name: 'E2E Test (Linux) - ${{ matrix.sandbox }}' runs-on: 'ubuntu-latest' # Skip on fork PRs: forks have no access to repository secrets # (OPENAI_*, DOCKERHUB_*), so the matrix would fail unconditionally # and show misleading red status. Same-repo PRs run normally. if: |- ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} strategy: matrix: sandbox: - 'sandbox:none' - 'sandbox:docker' node-version: - '22.x' steps: - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 - name: 'Set up Node.js ${{ matrix.node-version }}' uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 with: node-version: '${{ matrix.node-version }}' cache: 'npm' cache-dependency-path: 'package-lock.json' registry-url: 'https://registry.npmjs.org/' - name: 'Configure npm for rate limiting' run: |- npm config set fetch-retry-mintimeout 20000 npm config set fetch-retry-maxtimeout 120000 npm config set fetch-retries 5 npm config set fetch-timeout 300000 - name: 'Install dependencies' run: |- npm ci --prefer-offline --no-audit --progress=false - name: 'Build project' run: |- npm run build - name: 'Bundle CLI for E2E tests' run: |- npm run bundle - name: 'Set up Docker' if: |- ${{ matrix.sandbox == 'sandbox:docker' }} uses: 'docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5' # ratchet:docker/setup-buildx-action@v4 - name: 'Set up Podman' if: |- ${{ matrix.sandbox == 'sandbox:podman' }} uses: 'redhat-actions/podman-login@4934294ad0449894bcd1e9f191899d7292469603' # ratchet:redhat-actions/podman-login@v1 with: registry: 'docker.io' username: '${{ secrets.DOCKERHUB_USERNAME }}' password: '${{ secrets.DOCKERHUB_TOKEN }}' - name: 'Run E2E tests' env: OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}' OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}' OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}' KEEP_OUTPUT: 'true' VERBOSE: 'true' run: |- if [[ "${{ matrix.sandbox }}" == "sandbox:docker" ]]; then npm run test:integration:sandbox:docker else npm run test:integration:sandbox:none fi e2e-test-macos: name: 'E2E Test - macOS' runs-on: 'macos-latest' # Skip on fork PRs (no secrets) — see e2e-test-linux above. if: |- ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} steps: - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 - name: 'Set up Node.js' uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 with: node-version-file: '.nvmrc' cache: 'npm' cache-dependency-path: 'package-lock.json' registry-url: 'https://registry.npmjs.org/' - name: 'Configure npm for rate limiting' run: |- npm config set fetch-retry-mintimeout 20000 npm config set fetch-retry-maxtimeout 120000 npm config set fetch-retries 5 npm config set fetch-timeout 300000 - name: 'Install dependencies' run: |- npm ci --prefer-offline --no-audit --progress=false - name: 'Build project' run: |- npm run build - name: 'Bundle CLI for E2E tests' run: |- npm run bundle - name: 'Run E2E tests' env: OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}' OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}' OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}' run: 'npm run test:e2e' web-shell-browser-regression: name: 'web-shell Browser Regression' runs-on: 'ubuntu-latest' if: |- ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} steps: - name: 'Checkout' uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2 - name: 'Set up Node.js' uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0 with: node-version-file: '.nvmrc' cache: 'npm' cache-dependency-path: 'package-lock.json' registry-url: 'https://registry.npmjs.org/' - name: 'Configure npm for rate limiting' run: |- npm config set fetch-retry-mintimeout 20000 npm config set fetch-retry-maxtimeout 120000 npm config set fetch-retries 5 npm config set fetch-timeout 300000 - name: 'Install dependencies' run: |- npm ci --prefer-offline --no-audit --progress=false - name: 'Install Playwright Chromium' run: 'npx playwright install --with-deps chromium' - name: 'Run web-shell browser regression' run: 'npm run test:e2e --workspace=packages/web-shell' - name: 'Upload web-shell Playwright artifacts' if: '${{ always() }}' uses: 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' # v7.0.1 with: name: 'web-shell-browser-regression' path: |- packages/web-shell/client/e2e/test-results packages/web-shell/client/e2e/playwright-report if-no-files-found: 'ignore'