mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
3b46dab148
Co-authored-by: JJ Kasper <jj@jjsweb.site>
928 lines
37 KiB
YAML
928 lines
37 KiB
YAML
name: Tests
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
- stable
|
|
tags:
|
|
- "!*"
|
|
pull_request:
|
|
types:
|
|
- opened
|
|
- reopened
|
|
- synchronize
|
|
|
|
concurrency:
|
|
# Unique group for this workflow and branch
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/stable' }}
|
|
|
|
jobs:
|
|
# Phase 0: Update PR comment to show tests are running
|
|
pr-comment-start:
|
|
name: Create PR Comment
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
timeout-minutes: 2
|
|
|
|
steps:
|
|
- name: Find existing test comment
|
|
uses: peter-evans/find-comment@v3
|
|
id: find-comment
|
|
with:
|
|
issue-number: ${{ github.event.pull_request.number }}
|
|
comment-author: 'github-actions[bot]'
|
|
body-includes: '<!-- e2e-test-results -->'
|
|
|
|
- name: Get existing comment body
|
|
if: steps.find-comment.outputs.comment-id != ''
|
|
id: get-comment
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const comment = await github.rest.issues.getComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
comment_id: ${{ steps.find-comment.outputs.comment-id }}
|
|
});
|
|
const body = comment.data.body;
|
|
// Check if there are actual results (tables)
|
|
if (body.includes('|') && body.includes('Passed')) {
|
|
// Extract results section (everything after header)
|
|
let resultsSection = body
|
|
.replace(/<!-- e2e-test-results -->\n## 🧪 E2E Test Results\n\n> ⚠️ \*\*Results below are stale\*\*[^\n]*\n\n/g, '')
|
|
.replace(/<!-- e2e-test-results -->\n## 🧪 E2E Test Results\n\n/g, '')
|
|
.replace(/⏳ \*\*Tests are running\.\.\.\*\*\n\n---\n_Started at:[^_]*_\n\n---\n\n/g, '')
|
|
.replace(/⏳ \*\*Tests are running\.\.\.\*\*\n\n---\n_Started at:[^_]*_/g, '')
|
|
.trim();
|
|
if (resultsSection && resultsSection.includes('|')) {
|
|
core.setOutput('has-results', 'true');
|
|
core.setOutput('previous-results', resultsSection);
|
|
} else {
|
|
core.setOutput('has-results', 'false');
|
|
}
|
|
} else {
|
|
core.setOutput('has-results', 'false');
|
|
}
|
|
|
|
- name: Create new test comment
|
|
if: steps.find-comment.outputs.comment-id == ''
|
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
with:
|
|
header: e2e-test-results
|
|
message: |
|
|
<!-- e2e-test-results -->
|
|
## 🧪 E2E Test Results
|
|
|
|
⏳ **Tests are running...**
|
|
|
|
This comment will be updated with the results when the tests complete.
|
|
|
|
---
|
|
_Started at: ${{ github.event.pull_request.updated_at }}_
|
|
|
|
- name: Update existing test comment with stale warning
|
|
if: steps.find-comment.outputs.comment-id != '' && steps.get-comment.outputs.has-results == 'true'
|
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
with:
|
|
header: e2e-test-results
|
|
message: |
|
|
<!-- e2e-test-results -->
|
|
## 🧪 E2E Test Results
|
|
|
|
> ⚠️ **Results below are stale** and not from the latest commit. This comment will be updated when CI completes on the latest run.
|
|
|
|
⏳ **Tests are running...**
|
|
|
|
---
|
|
_Started at: ${{ github.event.pull_request.updated_at }}_
|
|
|
|
---
|
|
|
|
${{ steps.get-comment.outputs.previous-results }}
|
|
|
|
- name: Update existing test comment without results
|
|
if: steps.find-comment.outputs.comment-id != '' && steps.get-comment.outputs.has-results != 'true'
|
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
with:
|
|
header: e2e-test-results
|
|
message: |
|
|
<!-- e2e-test-results -->
|
|
## 🧪 E2E Test Results
|
|
|
|
⏳ **Tests are running...**
|
|
|
|
This comment will be updated with the results when the tests complete.
|
|
|
|
---
|
|
_Started at: ${{ github.event.pull_request.updated_at }}_
|
|
|
|
unit:
|
|
name: Unit Tests (${{ matrix.os }})
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 30
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, windows-latest]
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
setup-rust: 'true'
|
|
install-args: '--ignore-scripts'
|
|
build-packages: 'false'
|
|
|
|
- name: Run Unit Tests
|
|
run: pnpm test --filter='!./docs' --filter='!./workbench/vitest'
|
|
|
|
build-error-messages:
|
|
name: Node.js Module Build Errors Test
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v5
|
|
|
|
- name: Setup Node.js 22.x
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22.x
|
|
cache: "pnpm"
|
|
|
|
- name: Install Dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Run Initial Build
|
|
run: pnpm turbo run build --filter='!./workbench/*'
|
|
|
|
- name: Generate Workflows Registry
|
|
run: node workbench/scripts/generate-workflows-registry.js workbench/nextjs-turbopack/workflows workbench/nextjs-turbopack/_workflows.ts
|
|
|
|
- name: Run Build Error Tests
|
|
run: pnpm vitest run packages/core/e2e/build-errors.test.ts
|
|
env:
|
|
APP_NAME: "nextjs-turbopack"
|
|
|
|
vitest-plugin:
|
|
name: Vitest Plugin Tests
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
setup-rust: 'true'
|
|
install-args: '--ignore-scripts'
|
|
build-packages: 'false'
|
|
|
|
- name: Build packages
|
|
run: pnpm turbo run build --filter='!./workbench/*'
|
|
|
|
- name: Run Vitest Plugin Tests
|
|
run: pnpm test
|
|
working-directory: workbench/vitest
|
|
|
|
e2e-vercel-prod:
|
|
name: E2E Vercel Prod Tests (${{ matrix.app.name }})
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
permissions:
|
|
id-token: write
|
|
contents: read
|
|
deployments: read
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
app:
|
|
- name: "example"
|
|
project-id: "prj_xWq20Dd860HHAfzMjK2Mb6TPVxMa"
|
|
project-slug: "example-workflow"
|
|
- name: "nextjs-turbopack"
|
|
project-id: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
|
|
project-slug: "example-nextjs-workflow-turbopack"
|
|
- name: "nextjs-webpack"
|
|
project-id: "prj_avRPBF3eWjh6iDNQgmhH4VOg27h0"
|
|
project-slug: "example-nextjs-workflow-webpack"
|
|
- name: "nitro"
|
|
project-id: "prj_e7DZirYdLrQKXNrlxg7KmA6ABx8r"
|
|
project-slug: "workbench-nitro-workflow"
|
|
- name: "vite"
|
|
project-id: "prj_uLIcNZNDmETulAvj5h0IcDHi5432"
|
|
project-slug: "workbench-vite-workflow"
|
|
- name: "nuxt"
|
|
project-id: "prj_oTgiz3SGX2fpZuM6E0P38Ts8de6d"
|
|
project-slug: "workbench-nuxt-workflow"
|
|
- name: "sveltekit"
|
|
project-id: "prj_MqnBLm71ceXGSnm3Fs8i8gBnI23G"
|
|
project-slug: "workbench-sveltekit-workflow"
|
|
- name: "hono"
|
|
project-id: "prj_p0GIEsfl53L7IwVbosPvi9rPSOYW"
|
|
project-slug: "workbench-hono-workflow"
|
|
- name: "express"
|
|
project-id: "prj_cCZjpBy92VRbKHHbarDMhOHtkuIr"
|
|
project-slug: "workbench-express-workflow"
|
|
- name: "fastify"
|
|
project-id: "prj_5Yap0VDQ633v998iqQ3L3aQ25Cck"
|
|
project-slug: "workbench-fastify-workflow"
|
|
- name: "astro"
|
|
project-id: "prj_YDAXj3K8LM0hgejuIMhioz2yLgTI"
|
|
project-slug: "workbench-astro-workflow"
|
|
# TODO: re-enable once a Vercel project is connected for this branch.
|
|
# The local-dev/local-prod/local-postgres matrices already cover
|
|
# tanstack-start via scripts/create-test-matrix.mjs.
|
|
# - name: "tanstack-start"
|
|
# project-id: "prj_643jeVugTMq5ivsOFQHcbLG1qcnu"
|
|
# project-slug: "workbench-tanstack-start-workflow"
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
WORKFLOW_PUBLIC_MANIFEST: '1'
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
build-packages: 'false'
|
|
|
|
- name: Build CLI
|
|
run: pnpm turbo run build --filter='@workflow/cli'
|
|
|
|
- name: Waiting for the Vercel deployment
|
|
id: waitForDeployment
|
|
uses: ./.github/actions/wait-for-vercel-project
|
|
with:
|
|
team-id: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
|
|
project-id: ${{ matrix.app.project-id }}
|
|
vercel-token: ${{ secrets.VERCEL_LABS_TOKEN }}
|
|
timeout: 1000
|
|
check-interval: 15
|
|
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
|
|
|
|
- name: Run E2E Tests
|
|
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-vercel-prod-${{ matrix.app.name }}.json
|
|
env:
|
|
NODE_OPTIONS: "--enable-source-maps"
|
|
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
|
|
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
|
|
APP_NAME: ${{ matrix.app.name }}
|
|
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
|
|
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
|
|
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
|
|
WORKFLOW_VERCEL_PROJECT: ${{ matrix.app.project-id }}
|
|
WORKFLOW_VERCEL_PROJECT_SLUG: ${{ matrix.app.project-slug }}
|
|
# The trusted-sources OIDC token (x-vercel-trusted-oidc-idp-token)
|
|
# is minted on demand inside `scripts/trusted-sources-headers.mjs`
|
|
# via the runner's ACTIONS_ID_TOKEN_REQUEST_URL/_TOKEN env vars
|
|
# (exposed when `permissions: id-token: write` is set on the job),
|
|
# and re-minted before its 5-minute expiry. This lets long e2e
|
|
# suites keep a fresh token throughout the run instead of using
|
|
# a single pre-minted token that would expire mid-suite.
|
|
# See: https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/trusted-sources
|
|
# Point PRs at the protected workflow-server preview; unset on main
|
|
# so production runs hit the public vercel-workflow.com URL.
|
|
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}
|
|
|
|
- name: Generate E2E summary
|
|
if: always()
|
|
run: node .github/scripts/aggregate-e2e-results.js . --job-name "E2E Vercel Prod (${{ matrix.app.name }})" >> $GITHUB_STEP_SUMMARY || true
|
|
|
|
- name: Upload E2E results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: e2e-results-vercel-prod-${{ matrix.app.name }}
|
|
path: |
|
|
e2e-vercel-prod-${{ matrix.app.name }}.json
|
|
e2e-metadata-${{ matrix.app.name }}-vercel.json
|
|
e2e-failures-${{ matrix.app.name }}-vercel.json
|
|
e2e-diagnostics-${{ matrix.app.name }}-vercel.json
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
getTestMatrix:
|
|
name: Get Test Matrix
|
|
runs-on: ubuntu-latest
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
install-dependencies: 'false'
|
|
build-packages: 'false'
|
|
|
|
- id: set-matrix
|
|
run: echo "matrix=$(node ./scripts/create-test-matrix.mjs)" >> $GITHUB_OUTPUT
|
|
|
|
e2e-local-dev:
|
|
name: E2E Local Dev Tests (${{ matrix.app.name }} - ${{ matrix.app.canary && 'canary' || 'stable' }})
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
needs: getTestMatrix
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{fromJson(needs.getTestMatrix.outputs.matrix)}}
|
|
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
WORKFLOW_PUBLIC_MANIFEST: '1'
|
|
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
install-dependencies: 'false'
|
|
build-packages: 'false'
|
|
|
|
- name: Setup canary
|
|
if: ${{ matrix.app.canary }}
|
|
run: |
|
|
cat packages/next/package.json | jq '.dependencies.next|="canary"' > packages/next/package.json.new && mv packages/next/package.json.new packages/next/package.json
|
|
cat workbench/${{ matrix.app.name }}/package.json | jq '.dependencies.next|="canary"' > workbench/${{ matrix.app.name }}/package.json.new && mv workbench/${{ matrix.app.name }}/package.json.new workbench/${{ matrix.app.name }}/package.json
|
|
pnpm install --no-frozen-lockfile
|
|
|
|
- name: Install Dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Run Initial Build
|
|
run: pnpm turbo run build --filter='!./workbench/*'
|
|
|
|
- name: Prepare workbench path
|
|
id: prepare-workbench
|
|
uses: ./.github/actions/prepare-workbench-path
|
|
with:
|
|
app-name: ${{ matrix.app.name }}
|
|
|
|
- name: Run E2E Tests
|
|
run: |
|
|
export DEV_SERVER_LOG_PATH="$GITHUB_WORKSPACE/dev-server-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.log"
|
|
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
|
|
echo "starting tests in 10 seconds" && sleep 10
|
|
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
|
|
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.json
|
|
env:
|
|
NODE_OPTIONS: "--enable-source-maps"
|
|
APP_NAME: ${{ matrix.app.name }}
|
|
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
|
|
DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '5173' || (matrix.app.name == 'astro' && '4321' || '3000') }}"
|
|
DEV_TEST_CONFIG: ${{ toJSON(matrix.app) }}
|
|
NEXT_CANARY: ${{ matrix.app.canary && '1' || '' }}
|
|
|
|
- name: Generate E2E summary
|
|
if: always()
|
|
run: node .github/scripts/aggregate-e2e-results.js . --job-name "E2E Local Dev (${{ matrix.app.name }})" >> $GITHUB_STEP_SUMMARY || true
|
|
|
|
- name: Upload E2E results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: e2e-results-local-dev-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}
|
|
path: e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.json
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
e2e-local-prod:
|
|
name: E2E Local Prod Tests (${{ matrix.app.name }} - ${{ matrix.app.canary && 'canary' || 'stable' }})
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
needs: getTestMatrix
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{fromJson(needs.getTestMatrix.outputs.matrix)}}
|
|
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
WORKFLOW_PUBLIC_MANIFEST: '1'
|
|
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
install-dependencies: 'false'
|
|
build-packages: 'false'
|
|
|
|
- name: Setup canary
|
|
if: ${{ matrix.app.canary }}
|
|
run: |
|
|
cat packages/next/package.json | jq '.dependencies.next|="canary"' > packages/next/package.json.new && mv packages/next/package.json.new packages/next/package.json
|
|
cat workbench/${{ matrix.app.name }}/package.json | jq '.dependencies.next|="canary"' > workbench/${{ matrix.app.name }}/package.json.new && mv workbench/${{ matrix.app.name }}/package.json.new workbench/${{ matrix.app.name }}/package.json
|
|
pnpm install --no-frozen-lockfile
|
|
|
|
- name: Install Dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Run Initial Build
|
|
run: pnpm turbo run build --filter='!./workbench/*'
|
|
|
|
- name: Prepare workbench path
|
|
id: prepare-workbench
|
|
uses: ./.github/actions/prepare-workbench-path
|
|
with:
|
|
app-name: ${{ matrix.app.name }}
|
|
|
|
- name: Run Build Tests
|
|
run: pnpm vitest run packages/core/e2e/local-build.test.ts
|
|
env:
|
|
APP_NAME: ${{ matrix.app.name }}
|
|
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
|
|
|
|
- name: Run E2E Tests
|
|
run: |
|
|
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.log"
|
|
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
|
|
echo "starting tests in 10 seconds" && sleep 10
|
|
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.json
|
|
env:
|
|
NODE_OPTIONS: "--enable-source-maps"
|
|
APP_NAME: ${{ matrix.app.name }}
|
|
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
|
|
DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || (matrix.app.name == 'astro' && '4321' || '3000') }}"
|
|
NEXT_CANARY: ${{ matrix.app.canary && '1' || '' }}
|
|
|
|
- name: Generate E2E summary
|
|
if: always()
|
|
run: node .github/scripts/aggregate-e2e-results.js . --job-name "E2E Local Prod (${{ matrix.app.name }})" >> $GITHUB_STEP_SUMMARY || true
|
|
|
|
- name: Upload E2E results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: e2e-results-local-prod-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}
|
|
path: e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.json
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
e2e-local-postgres:
|
|
name: E2E Local Postgres Tests (${{ matrix.app.name }} - ${{ matrix.app.canary && 'canary' || 'stable' }})
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
needs: getTestMatrix
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{fromJson(needs.getTestMatrix.outputs.matrix)}}
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:18-alpine
|
|
env:
|
|
POSTGRES_USER: world
|
|
POSTGRES_PASSWORD: world
|
|
POSTGRES_DB: world
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 10s
|
|
--health-timeout 5s
|
|
--health-retries 5
|
|
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
WORKFLOW_PUBLIC_MANIFEST: '1'
|
|
WORKFLOW_TARGET_WORLD: "@workflow/world-postgres"
|
|
WORKFLOW_POSTGRES_URL: "postgres://world:world@localhost:5432/world"
|
|
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
install-dependencies: 'false'
|
|
build-packages: 'false'
|
|
|
|
- name: Setup canary
|
|
if: ${{ matrix.app.canary }}
|
|
run: |
|
|
cat packages/next/package.json | jq '.dependencies.next|="canary"' > packages/next/package.json.new && mv packages/next/package.json.new packages/next/package.json
|
|
cat workbench/${{ matrix.app.name }}/package.json | jq '.dependencies.next|="canary"' > workbench/${{ matrix.app.name }}/package.json.new && mv workbench/${{ matrix.app.name }}/package.json.new workbench/${{ matrix.app.name }}/package.json
|
|
pnpm install --no-frozen-lockfile
|
|
|
|
- name: Install Dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Run Initial Build
|
|
run: pnpm turbo run build --filter='!./workbench/*'
|
|
|
|
- name: Setup PostgreSQL Database
|
|
run: ./packages/world-postgres/bin/setup.js
|
|
|
|
- name: Prepare workbench path
|
|
id: prepare-workbench
|
|
uses: ./.github/actions/prepare-workbench-path
|
|
with:
|
|
app-name: ${{ matrix.app.name }}
|
|
|
|
- name: Run Build Tests
|
|
run: pnpm vitest run packages/core/e2e/local-build.test.ts
|
|
env:
|
|
APP_NAME: ${{ matrix.app.name }}
|
|
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
|
|
|
|
- name: Run E2E Tests
|
|
run: |
|
|
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.log"
|
|
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
|
|
echo "starting tests in 10 seconds" && sleep 10
|
|
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.json
|
|
env:
|
|
NODE_OPTIONS: "--enable-source-maps"
|
|
APP_NAME: ${{ matrix.app.name }}
|
|
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
|
|
DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || (matrix.app.name == 'astro' && '4321' || '3000') }}"
|
|
NEXT_CANARY: ${{ matrix.app.canary && '1' || '' }}
|
|
|
|
- name: Generate E2E summary
|
|
if: always()
|
|
run: node .github/scripts/aggregate-e2e-results.js . --job-name "E2E Local Postgres (${{ matrix.app.name }})" >> $GITHUB_STEP_SUMMARY || true
|
|
|
|
- name: Upload E2E results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: e2e-results-local-postgres-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}
|
|
path: e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.canary && 'canary' || 'stable' }}.json
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
e2e-windows:
|
|
name: E2E Windows Tests
|
|
runs-on: windows-latest
|
|
timeout-minutes: 30
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
WORKFLOW_PUBLIC_MANIFEST: '1'
|
|
|
|
steps:
|
|
- name: Checkout Repo
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup Rust
|
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
target: wasm32-unknown-unknown
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v5
|
|
|
|
- name: Setup Node.js 22.x
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22.x
|
|
cache: "pnpm"
|
|
|
|
- name: Install Dependencies
|
|
run: pnpm install --frozen-lockfile --ignore-scripts
|
|
|
|
- name: Run Initial Build
|
|
run: pnpm turbo run build --filter='!./workbench/*' --filter='!./docs'
|
|
|
|
- name: Run E2E Tests (Next.js)
|
|
run: |
|
|
cd workbench/nextjs-turbopack
|
|
$logFile = "$env:GITHUB_WORKSPACE/nextjs-server.log"
|
|
$env:DEV_SERVER_LOG_PATH = $logFile
|
|
$job = Start-Job -ScriptBlock { Set-Location $using:PWD; pnpm dev *>&1 | Tee-Object -FilePath $using:logFile }
|
|
Start-Sleep -Seconds 15
|
|
cd ../..
|
|
pnpm vitest run packages/core/e2e/dev.test.ts
|
|
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
|
|
Stop-Job $job
|
|
shell: powershell
|
|
env:
|
|
NODE_OPTIONS: "--enable-source-maps"
|
|
APP_NAME: "nextjs-turbopack"
|
|
DEPLOYMENT_URL: "http://localhost:3000"
|
|
DEV_TEST_CONFIG: '{"generatedStepPath":"app/.well-known/workflow/v1/step/route.js","generatedWorkflowPath":"app/.well-known/workflow/v1/flow/route.js","apiFilePath":"app/api/chat/route.ts","apiFileImportPath":"../../..","port":3000,"supportsDeferredStepCopies":false}'
|
|
|
|
- name: Print Next.js server logs
|
|
if: always()
|
|
shell: powershell
|
|
run: |
|
|
$logFile = "$env:GITHUB_WORKSPACE/nextjs-server.log"
|
|
if (Test-Path $logFile) {
|
|
Write-Host "=== Next.js Server Logs ===" -ForegroundColor Cyan
|
|
Get-Content $logFile
|
|
Write-Host "=== End of Server Logs ===" -ForegroundColor Cyan
|
|
} else {
|
|
Write-Host "No server log file found at $logFile" -ForegroundColor Yellow
|
|
}
|
|
|
|
- name: Generate E2E summary
|
|
if: always()
|
|
shell: bash
|
|
run: node .github/scripts/aggregate-e2e-results.js . --job-name "E2E Windows (nextjs-turbopack)" >> $GITHUB_STEP_SUMMARY || true
|
|
|
|
- name: Upload E2E results
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: e2e-results-windows-nextjs-turbopack
|
|
path: e2e-windows-nextjs-turbopack.json
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
- name: Upload Next.js server logs
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: nextjs-server-logs-windows
|
|
path: nextjs-server.log
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|
|
|
|
# Community World E2E Tests (dynamically generated from worlds-manifest.json)
|
|
# Non-blocking (continue-on-error in the reusable workflow; surfaced
|
|
# as a warning in the summary). The community world adapters need an in-process
|
|
# queue worker, which the reusable workflow starts via a CI-only instrumentation
|
|
# kickstart. A fast-fail preflight there guards against the 30-minute hang that
|
|
# occurred when that kickstart was missing (see e2e-community-world.yml).
|
|
getCommunityWorldsMatrix:
|
|
name: Get Community Worlds Matrix
|
|
runs-on: ubuntu-latest
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Setup environment
|
|
uses: ./.github/actions/setup-workflow-dev
|
|
with:
|
|
install-dependencies: 'false'
|
|
build-packages: 'false'
|
|
|
|
- id: set-matrix
|
|
run: echo "matrix=$(node ./scripts/create-community-worlds-matrix.mjs)" >> $GITHUB_OUTPUT
|
|
|
|
e2e-community:
|
|
name: E2E Community World (${{ matrix.world.name }})
|
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
needs: getCommunityWorldsMatrix
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{ fromJson(needs.getCommunityWorldsMatrix.outputs.matrix) }}
|
|
uses: ./.github/workflows/e2e-community-world.yml
|
|
with:
|
|
world-id: ${{ matrix.world.id }}
|
|
world-name: ${{ matrix.world.name }}
|
|
world-package: ${{ matrix.world.package }}
|
|
service-type: ${{ matrix.world.service-type }}
|
|
env-vars: ${{ matrix.world.env-vars }}
|
|
setup-command: ${{ matrix.world.setup-command }}
|
|
secrets: inherit
|
|
|
|
# Final job: Aggregate all E2E results and update PR comment
|
|
summary:
|
|
name: E2E Summary
|
|
runs-on: ubuntu-latest
|
|
needs: [e2e-vercel-prod, e2e-local-dev, e2e-local-prod, e2e-local-postgres, e2e-windows, e2e-community]
|
|
if: always() && !cancelled()
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Download all E2E artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: e2e-results-*
|
|
path: e2e-results
|
|
merge-multiple: true
|
|
|
|
- name: List downloaded files
|
|
run: find e2e-results -type f -name "*.json" | sort || echo "No files found"
|
|
|
|
- name: Aggregate E2E results
|
|
id: aggregate
|
|
run: |
|
|
node .github/scripts/aggregate-e2e-results.js e2e-results --mode aggregate --run-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | tee e2e-summary.md >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Check E2E job statuses
|
|
id: check-status
|
|
run: |
|
|
VERCEL_STATUS="${{ needs.e2e-vercel-prod.result }}"
|
|
LOCAL_DEV_STATUS="${{ needs.e2e-local-dev.result }}"
|
|
LOCAL_PROD_STATUS="${{ needs.e2e-local-prod.result }}"
|
|
POSTGRES_STATUS="${{ needs.e2e-local-postgres.result }}"
|
|
WINDOWS_STATUS="${{ needs.e2e-windows.result }}"
|
|
COMMUNITY_STATUS="${{ needs.e2e-community.result }}"
|
|
|
|
echo "vercel=$VERCEL_STATUS" >> $GITHUB_OUTPUT
|
|
echo "local-dev=$LOCAL_DEV_STATUS" >> $GITHUB_OUTPUT
|
|
echo "local-prod=$LOCAL_PROD_STATUS" >> $GITHUB_OUTPUT
|
|
echo "postgres=$POSTGRES_STATUS" >> $GITHUB_OUTPUT
|
|
echo "windows=$WINDOWS_STATUS" >> $GITHUB_OUTPUT
|
|
echo "community=$COMMUNITY_STATUS" >> $GITHUB_OUTPUT
|
|
|
|
# Community world failures are warnings, not errors
|
|
if [[ "$VERCEL_STATUS" == "failure" || "$LOCAL_DEV_STATUS" == "failure" || "$LOCAL_PROD_STATUS" == "failure" || "$POSTGRES_STATUS" == "failure" || "$WINDOWS_STATUS" == "failure" ]]; then
|
|
echo "has_failures=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "has_failures=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
if [[ "$COMMUNITY_STATUS" == "failure" ]]; then
|
|
echo "has_warnings=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "has_warnings=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Update PR comment with results
|
|
if: github.event_name == 'pull_request'
|
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
with:
|
|
header: e2e-test-results
|
|
path: e2e-summary.md
|
|
|
|
- name: Append failure notice to PR comment
|
|
if: github.event_name == 'pull_request' && steps.check-status.outputs.has_failures == 'true'
|
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
with:
|
|
header: e2e-test-results
|
|
append: true
|
|
message: |
|
|
|
|
---
|
|
❌ **Some E2E test jobs failed:**
|
|
- Vercel Prod: ${{ needs.e2e-vercel-prod.result }}
|
|
- Local Dev: ${{ needs.e2e-local-dev.result }}
|
|
- Local Prod: ${{ needs.e2e-local-prod.result }}
|
|
- Local Postgres: ${{ needs.e2e-local-postgres.result }}
|
|
- Windows: ${{ needs.e2e-windows.result }}
|
|
|
|
Check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.
|
|
|
|
- name: Append community warning to PR comment
|
|
if: github.event_name == 'pull_request' && steps.check-status.outputs.has_warnings == 'true'
|
|
uses: marocchino/sticky-pull-request-comment@v2
|
|
with:
|
|
header: e2e-test-results
|
|
append: true
|
|
message: |
|
|
|
|
---
|
|
⚠️ **Community world tests failed** (non-blocking):
|
|
- Community Worlds: ${{ needs.e2e-community.result }}
|
|
|
|
Check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.
|
|
|
|
# Final required check: passes only when unit + all E2E jobs succeed.
|
|
# Community worlds are intentionally excluded — they are temporarily disabled
|
|
# (see getCommunityWorldsMatrix above).
|
|
e2e-required-check:
|
|
name: E2E Required Check
|
|
runs-on: ubuntu-latest
|
|
needs: [unit, e2e-vercel-prod, e2e-local-dev, e2e-local-prod, e2e-local-postgres, e2e-windows]
|
|
if: always()
|
|
timeout-minutes: 5
|
|
|
|
steps:
|
|
- name: Validate E2E job statuses
|
|
env:
|
|
UNIT_STATUS: ${{ needs.unit.result }}
|
|
VERCEL_STATUS: ${{ needs.e2e-vercel-prod.result }}
|
|
LOCAL_DEV_STATUS: ${{ needs.e2e-local-dev.result }}
|
|
LOCAL_PROD_STATUS: ${{ needs.e2e-local-prod.result }}
|
|
POSTGRES_STATUS: ${{ needs.e2e-local-postgres.result }}
|
|
WINDOWS_STATUS: ${{ needs.e2e-windows.result }}
|
|
HAS_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'workflow-server-test') }}
|
|
run: |
|
|
FAILED_JOBS=()
|
|
|
|
# When workflow-server-test label is present, only check vercel-prod (others should be skipped)
|
|
# Otherwise, all jobs must succeed
|
|
if [[ "$HAS_LABEL" == "true" ]]; then
|
|
echo "workflow-server-test label detected - only checking required jobs"
|
|
[[ "$VERCEL_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-vercel-prod ($VERCEL_STATUS)")
|
|
|
|
# Verify other jobs were actually skipped
|
|
[[ "$UNIT_STATUS" == "skipped" ]] || echo "Warning: unit was not skipped ($UNIT_STATUS)"
|
|
[[ "$LOCAL_DEV_STATUS" == "skipped" ]] || echo "Warning: e2e-local-dev was not skipped ($LOCAL_DEV_STATUS)"
|
|
[[ "$LOCAL_PROD_STATUS" == "skipped" ]] || echo "Warning: e2e-local-prod was not skipped ($LOCAL_PROD_STATUS)"
|
|
[[ "$POSTGRES_STATUS" == "skipped" ]] || echo "Warning: e2e-local-postgres was not skipped ($POSTGRES_STATUS)"
|
|
[[ "$WINDOWS_STATUS" == "skipped" ]] || echo "Warning: e2e-windows was not skipped ($WINDOWS_STATUS)"
|
|
else
|
|
echo "Standard PR - checking all jobs"
|
|
[[ "$UNIT_STATUS" == "success" ]] || FAILED_JOBS+=("unit ($UNIT_STATUS)")
|
|
[[ "$VERCEL_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-vercel-prod ($VERCEL_STATUS)")
|
|
[[ "$LOCAL_DEV_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-local-dev ($LOCAL_DEV_STATUS)")
|
|
[[ "$LOCAL_PROD_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-local-prod ($LOCAL_PROD_STATUS)")
|
|
[[ "$POSTGRES_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-local-postgres ($POSTGRES_STATUS)")
|
|
[[ "$WINDOWS_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-windows ($WINDOWS_STATUS)")
|
|
fi
|
|
|
|
if [[ ${#FAILED_JOBS[@]} -gt 0 ]]; then
|
|
echo "The following E2E jobs did not succeed:"
|
|
printf '%s\n' "${FAILED_JOBS[@]}"
|
|
exit 1
|
|
fi
|
|
|
|
echo "All required E2E jobs succeeded."
|
|
|
|
# Publish E2E results to GitHub Pages (main branch only)
|
|
publish-results:
|
|
name: Publish E2E Results
|
|
runs-on: ubuntu-latest
|
|
needs: [summary]
|
|
# Use always() because summary uses always() - without it this job gets skipped when any E2E test fails
|
|
if: always() && !cancelled() && github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
timeout-minutes: 5
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
|
|
- name: Download all E2E artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: e2e-results-*
|
|
path: e2e-results
|
|
merge-multiple: true
|
|
|
|
- name: Generate docs data
|
|
run: |
|
|
mkdir -p docs-data
|
|
node .github/scripts/generate-docs-data.js \
|
|
--type e2e \
|
|
--results-dir e2e-results \
|
|
--output docs-data/e2e-results.json \
|
|
--commit "${{ github.sha }}" \
|
|
--branch "${{ github.ref_name }}"
|
|
|
|
- name: Deploy to GitHub Pages
|
|
uses: peaceiris/actions-gh-pages@v4
|
|
with:
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
publish_dir: ./docs-data
|
|
destination_dir: ci
|
|
keep_files: true
|