fix(core): Eliminate false-negative 500s in workflow builder tools #98981
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: 'CI: Pull Requests (Build, Test, Lint)' | |
| on: | |
| pull_request: | |
| merge_group: | |
| concurrency: | |
| group: ci-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| install-and-build: | |
| name: Install & Build | |
| runs-on: ${{ vars.RUNNER_PROVIDER == 'github' && 'ubuntu-latest' || 'blacksmith-4vcpu-ubuntu-2204' }} | |
| env: | |
| NODE_OPTIONS: '--max-old-space-size=7168' | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| QA_METRICS_WEBHOOK_URL: ${{ secrets.QA_METRICS_WEBHOOK_URL }} | |
| QA_METRICS_WEBHOOK_USER: ${{ secrets.QA_METRICS_WEBHOOK_USER }} | |
| QA_METRICS_WEBHOOK_PASSWORD: ${{ secrets.QA_METRICS_WEBHOOK_PASSWORD }} | |
| outputs: | |
| ci: ${{ fromJSON(steps.ci-filter.outputs.results).ci == true }} | |
| runtime: ${{ fromJSON(steps.ci-filter.outputs.results).runtime == true }} | |
| unit: ${{ fromJSON(steps.ci-filter.outputs.results).unit == true }} | |
| e2e: ${{ fromJSON(steps.ci-filter.outputs.results).e2e == true }} | |
| dev_server_smoke: ${{ fromJSON(steps.ci-filter.outputs.results)['dev-server-smoke'] == true }} | |
| workflows: ${{ fromJSON(steps.ci-filter.outputs.results).workflows == true }} | |
| workflow_scripts: ${{ fromJSON(steps.ci-filter.outputs.results)['workflow-scripts'] == true }} | |
| db: ${{ fromJSON(steps.ci-filter.outputs.results).db == true }} | |
| performance: ${{ fromJSON(steps.ci-filter.outputs.results).performance == true }} | |
| e2e_performance: ${{ fromJSON(steps.ci-filter.outputs.results)['e2e-performance'] == true }} | |
| instance_ai_workflow_eval: ${{ fromJSON(steps.ci-filter.outputs.results)['instance-ai-workflow-eval'] == true }} | |
| commit_sha: ${{ steps.commit-sha.outputs.sha }} | |
| merge_base: ${{ steps.ci-filter.outputs.merge-base }} | |
| matrix: ${{ steps.generate-matrix.outputs.matrix }} | |
| db_test_matrix: ${{ steps.generate-db-test-matrix.outputs.db_test_matrix }} | |
| skip_tests: ${{ steps.generate-matrix.outputs.skip-tests }} | |
| affected_packages: ${{ steps.affected-packages.outputs.list }} | |
| changed_files: ${{ steps.ci-filter.outputs.changed-files }} | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| with: | |
| # Use merge_group SHA when in merge queue, otherwise PR merge ref | |
| ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || format('refs/pull/{0}/merge', github.event.pull_request.number) }} | |
| - name: Capture commit SHA for cache consistency | |
| id: commit-sha | |
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Check for relevant changes | |
| uses: ./.github/actions/ci-filter | |
| id: ci-filter | |
| with: | |
| mode: filter | |
| filters: | | |
| ci: | |
| ** | |
| !packages/@n8n/task-runner-python/** | |
| !.github/** | |
| !**/*.md | |
| !**/LICENSE | |
| !**/CHANGELOG.md | |
| runtime: | |
| ** | |
| !packages/@n8n/task-runner-python/** | |
| !.github/** | |
| !**/*.md | |
| !**/LICENSE | |
| !**/CHANGELOG.md | |
| !**/*.test.ts | |
| !**/*.spec.ts | |
| !packages/testing/playwright/** | |
| !packages/frontend/@n8n/storybook/** | |
| !.devcontainer/** | |
| !scripts/agent-setup.mjs | |
| !scripts/backend-module/** | |
| !scripts/cloud-session.mjs | |
| !scripts/licenses/** | |
| !scripts/mutation-health/** | |
| !scripts/sync-agent-skill-links.mjs | |
| unit: | |
| ** | |
| !packages/@n8n/task-runner-python/** | |
| !packages/testing/playwright/** | |
| !.github/** | |
| !**/*.md | |
| !**/LICENSE | |
| !**/CHANGELOG.md | |
| !.devcontainer/** | |
| !scripts/agent-setup.mjs | |
| !scripts/backend-module/** | |
| !scripts/cloud-session.mjs | |
| !scripts/licenses/** | |
| !scripts/mutation-health/** | |
| !scripts/sync-agent-skill-links.mjs | |
| e2e: | |
| .github/workflows/test-e2e-*.yml | |
| .github/workflows/prepare-docker-reusable.yml | |
| .github/actions/build-n8n-docker/** | |
| .github/actions/load-n8n-docker/** | |
| packages/testing/playwright/** | |
| packages/testing/containers/** | |
| dev-server-smoke: | |
| packages/frontend/editor-ui/vite.config.mts | |
| pnpm-workspace.yaml | |
| packages/@n8n/*/package.json | |
| packages/testing/playwright/tests/dev-server-smoke/** | |
| packages/testing/playwright/playwright.config.ts | |
| packages/testing/playwright/playwright-projects.ts | |
| packages/testing/playwright/package.json | |
| .github/workflows/test-dev-server-smoke-reusable.yml | |
| workflows: .github/** | |
| workflow-scripts: | |
| .github/scripts/** | |
| scripts/licenses/** | |
| scripts/mutation-health/** | |
| performance: | |
| packages/testing/performance/** | |
| packages/workflow/src/** | |
| packages/@n8n/expression-runtime/src/** | |
| .github/workflows/test-bench-reusable.yml | |
| e2e-performance: | |
| packages/testing/playwright/tests/performance/** | |
| packages/testing/playwright/utils/performance-helper.ts | |
| packages/testing/containers/** | |
| .github/workflows/test-e2e-performance-reusable.yml | |
| instance-ai-workflow-eval: | |
| packages/@n8n/instance-ai/src/** | |
| packages/@n8n/instance-ai/evaluations/** | |
| packages/cli/src/modules/instance-ai/** | |
| packages/core/src/execution-engine/eval-mock-helpers.ts | |
| .github/workflows/test-evals-discovery.yml | |
| db: | |
| packages/cli/src/databases/** | |
| packages/cli/src/modules/*/database/** | |
| packages/cli/src/modules/**/*.entity.ts | |
| packages/cli/src/modules/**/*.repository.ts | |
| packages/cli/test/integration/** | |
| packages/cli/test/migration/** | |
| packages/cli/test/shared/db/** | |
| packages/cli/test/setup-testcontainers.js | |
| packages/cli/test/teardown-testcontainers.js | |
| packages/@n8n/db/** | |
| packages/@n8n/backend-test-utils/** | |
| packages/@n8n/scheduler/src/** | |
| packages/cli/**/__tests__/** | |
| packages/testing/containers/services/postgres.ts | |
| packages/testing/containers/postgres-versions.json | |
| packages/testing/containers/test-containers.ts | |
| .github/workflows/test-db-reusable.yml | |
| .github/scripts/db-test-matrix.mjs | |
| docs/generated/** | |
| .tbls.sqlite.yml | |
| .tbls.postgres.yml | |
| - name: Setup and Build | |
| if: fromJSON(steps.ci-filter.outputs.results).ci || fromJSON(steps.ci-filter.outputs.results).e2e | |
| uses: ./.github/actions/setup-nodejs | |
| with: | |
| build-command: ${{ fromJSON(steps.ci-filter.outputs.results).ci && 'pnpm build' || 'pnpm turbo run build --filter=@n8n/playwright-janitor' }} | |
| - name: Report build critical path | |
| if: fromJSON(steps.ci-filter.outputs.results).ci | |
| run: node scripts/turbo-critical-path.mjs >> "$GITHUB_STEP_SUMMARY" | |
| - name: Run format check | |
| if: fromJSON(steps.ci-filter.outputs.results).ci | |
| run: pnpm format:check | |
| - name: Generate shard matrix | |
| id: generate-matrix | |
| if: fromJSON(steps.ci-filter.outputs.results).ci || fromJSON(steps.ci-filter.outputs.results).e2e | |
| env: | |
| CHANGED_FILES: ${{ steps.ci-filter.outputs.changed-files }} | |
| MERGE_BASE: ${{ steps.ci-filter.outputs.merge-base }} | |
| run: | | |
| FILES_CSV=$(echo "$CHANGED_FILES" | tr '\n' ',' | sed 's/,$//') | |
| MATRIX=$(node packages/testing/playwright/scripts/distribute-tests.mjs --matrix 16 --orchestrate --impact "--files=$FILES_CSV" "--base=$MERGE_BASE") | |
| echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT" | |
| echo "skip-tests=$(node -e "process.stdout.write(JSON.parse(process.argv[1])[0]?.skip === true ? 'true' : 'false')" "$MATRIX")" >> "$GITHUB_OUTPUT" | |
| # Generated here because a reusable workflow cannot resolve its own matrix. | |
| - name: Generate DB test matrix | |
| id: generate-db-test-matrix | |
| if: fromJSON(steps.ci-filter.outputs.results).db | |
| run: echo "db_test_matrix=$(node .github/scripts/db-test-matrix.mjs)" >> "$GITHUB_OUTPUT" | |
| - name: Compute affected packages | |
| id: affected-packages | |
| if: fromJSON(steps.ci-filter.outputs.results).unit | |
| env: | |
| CHANGED_FILES: ${{ steps.ci-filter.outputs.changed-files }} | |
| run: | | |
| PACKAGES=$(node packages/testing/janitor/dist/cli.js affected-packages | tr '\n' ' ' | sed 's/ *$//') | |
| echo "Affected packages: $PACKAGES" | |
| echo "list=$PACKAGES" >> "$GITHUB_OUTPUT" | |
| unit-test: | |
| name: Unit tests | |
| if: needs.install-and-build.outputs.unit == 'true' | |
| uses: ./.github/workflows/test-unit-reusable.yml | |
| needs: install-and-build | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| collectCoverage: ${{ github.event_name != 'merge_group' }} | |
| affectedPackages: ${{ needs.install-and-build.outputs.affected_packages }} | |
| changedFiles: ${{ needs.install-and-build.outputs.changed_files }} | |
| secrets: inherit | |
| typecheck: | |
| name: Typecheck | |
| if: needs.install-and-build.outputs.ci == 'true' | |
| runs-on: ${{ vars.RUNNER_PROVIDER == 'github' && 'ubuntu-latest' || 'blacksmith-4vcpu-ubuntu-2204' }} | |
| needs: install-and-build | |
| env: | |
| NODE_OPTIONS: --max-old-space-size=7168 | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| - name: Setup Node.js | |
| uses: ./.github/actions/setup-nodejs | |
| with: | |
| build-command: pnpm typecheck | |
| lint: | |
| name: Lint | |
| if: needs.install-and-build.outputs.ci == 'true' | |
| uses: ./.github/workflows/test-linting-reusable.yml | |
| needs: install-and-build | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| check-packaging: | |
| name: Check packaging | |
| if: needs.install-and-build.outputs.ci == 'true' | |
| runs-on: ${{ vars.RUNNER_PROVIDER == 'github' && 'ubuntu-latest' || 'blacksmith-4vcpu-ubuntu-2204' }} | |
| needs: install-and-build | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| - name: Setup Node.js | |
| uses: ./.github/actions/setup-nodejs | |
| - name: Check packaging | |
| shell: bash | |
| run: | | |
| pnpm -r pack --dry-run | |
| # Seeds the SHA-keyed Docker image cache once so that downstream e2e jobs | |
| # (each of which invokes prepare-docker internally) short-circuit to a | |
| # cache hit instead of racing to rebuild. | |
| prepare-docker: | |
| name: Prepare Docker | |
| needs: install-and-build | |
| if: >- | |
| github.repository == 'n8n-io/n8n' && | |
| github.event_name != 'merge_group' && | |
| (needs.install-and-build.outputs.runtime == 'true' | |
| || needs.install-and-build.outputs.e2e == 'true' | |
| || needs.install-and-build.outputs.e2e_performance == 'true' | |
| || contains(github.event.pull_request.labels.*.name, 'force-e2e') | |
| || contains(github.event.pull_request.labels.*.name, 'run-perf-tests')) | |
| uses: ./.github/workflows/prepare-docker-reusable.yml | |
| with: | |
| branch: ${{ needs.install-and-build.outputs.commit_sha }} | |
| secrets: inherit | |
| # Internal-only 1-spec fail-fast sanity check on sqlite. | |
| sqlite-sanity: | |
| name: 'SQLite: Sanity Check' | |
| needs: [install-and-build, prepare-docker] | |
| if: >- | |
| needs.prepare-docker.result == 'success' && | |
| (needs.install-and-build.outputs.runtime == 'true' | |
| || needs.install-and-build.outputs.e2e == 'true' | |
| || contains(github.event.pull_request.labels.*.name, 'force-e2e')) && | |
| github.repository == 'n8n-io/n8n' && | |
| github.event_name != 'merge_group' && | |
| github.event.pull_request.head.repo.fork != true | |
| uses: ./.github/workflows/test-e2e-reusable.yml | |
| with: | |
| branch: ${{ needs.install-and-build.outputs.commit_sha }} | |
| test-mode: docker-artifact | |
| test-command: pnpm --filter=n8n-playwright test:container:sqlite:e2e tests/e2e/building-blocks/workflow-entry-points.spec.ts | |
| workers: '1' | |
| artifact-prefix: sanity | |
| secrets: inherit | |
| # Full e2e run. Internal PRs run multi-main (postgres + redis + caddy + 2 mains + 1 worker). | |
| # Fork PRs run sqlite-only and skip @licensed tests (no enterprise license secrets on forks). | |
| e2e: | |
| name: E2E | |
| needs: [install-and-build, prepare-docker] | |
| if: >- | |
| needs.prepare-docker.result == 'success' && | |
| (needs.install-and-build.outputs.runtime == 'true' | |
| || needs.install-and-build.outputs.e2e == 'true' | |
| || contains(github.event.pull_request.labels.*.name, 'force-e2e')) && | |
| needs.install-and-build.outputs.skip_tests != 'true' && | |
| github.event_name != 'merge_group' | |
| uses: ./.github/workflows/test-e2e-reusable.yml | |
| with: | |
| branch: ${{ needs.install-and-build.outputs.commit_sha }} | |
| test-mode: docker-artifact | |
| test-command: ${{ github.event.pull_request.head.repo.fork == true && 'pnpm --filter=n8n-playwright test:container:sqlite:e2e --grep-invert=@licensed' || 'pnpm --filter=n8n-playwright test:container:multi-main:e2e' }} | |
| workers: '1' | |
| pre-generated-matrix: ${{ needs.install-and-build.outputs.matrix }} | |
| artifact-prefix: e2e | |
| secrets: inherit | |
| # Boots the editor-ui against the Vite dev server and fails on any console | |
| # or page error during load. Catches regressions in dev-mode module | |
| # resolution (missing Vite alias, broken workspace package interop) that | |
| # the production-bundle e2e job bundles around. | |
| dev-server-smoke: | |
| name: Dev-server boot smoke | |
| needs: install-and-build | |
| if: needs.install-and-build.outputs.dev_server_smoke == 'true' && github.event_name != 'merge_group' | |
| uses: ./.github/workflows/test-dev-server-smoke-reusable.yml | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| secrets: inherit | |
| db-tests: | |
| name: DB Tests | |
| needs: install-and-build | |
| if: needs.install-and-build.outputs.db == 'true' | |
| uses: ./.github/workflows/test-db-reusable.yml | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| matrix: ${{ needs.install-and-build.outputs.db_test_matrix }} | |
| performance: | |
| name: Performance | |
| needs: install-and-build | |
| if: needs.install-and-build.outputs.performance == 'true' && github.event_name != 'merge_group' | |
| uses: ./.github/workflows/test-bench-reusable.yml | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| e2e-performance: | |
| name: E2E Performance | |
| needs: [install-and-build, prepare-docker] | |
| # Performance is internal-only (license secrets required, not available on forks). | |
| # Runs when path filter trips OR when the PR carries the `run-perf-tests` label | |
| # (opt-in coverage for shared-utility / Vue Flow changes that the path filter | |
| # would otherwise miss). | |
| if: >- | |
| needs.prepare-docker.result == 'success' && | |
| (needs.install-and-build.outputs.e2e_performance == 'true' || | |
| contains(github.event.pull_request.labels.*.name, 'run-perf-tests')) && | |
| github.event.pull_request.head.repo.fork != true | |
| uses: ./.github/workflows/test-e2e-performance-reusable.yml | |
| secrets: inherit | |
| security-checks: | |
| name: Security Checks | |
| needs: install-and-build | |
| if: needs.install-and-build.outputs.workflows == 'true' | |
| uses: ./.github/workflows/sec-ci-reusable.yml | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| secrets: inherit | |
| workflow-scripts: | |
| name: Workflow scripts | |
| needs: install-and-build | |
| if: needs.install-and-build.outputs.workflow_scripts == 'true' | |
| uses: ./.github/workflows/test-workflow-scripts-reusable.yml | |
| with: | |
| ref: ${{ needs.install-and-build.outputs.commit_sha }} | |
| secrets: inherit | |
| # In-process discovery eval — asserts the orchestrator reaches for browser/computer-use | |
| # tools at OAuth/screenshot moments. Lightweight (no Docker), runs in parallel with the | |
| # heavy workflow eval. Non-blocking initially; promote to required after stability. | |
| instance-ai-discovery-evals: | |
| name: Instance AI Discovery Evals | |
| needs: install-and-build | |
| if: >- | |
| !cancelled() && | |
| needs.install-and-build.result == 'success' && | |
| needs.install-and-build.outputs.instance_ai_workflow_eval == 'true' && | |
| github.repository == 'n8n-io/n8n' && | |
| github.event_name != 'merge_group' && | |
| (github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork) | |
| uses: ./.github/workflows/test-evals-discovery.yml | |
| with: | |
| branch: ${{ needs.install-and-build.outputs.commit_sha }} | |
| secrets: inherit | |
| # This job is required by GitHub branch protection rules. | |
| # PRs cannot be merged unless this job passes. | |
| required-checks: | |
| name: Required Checks | |
| needs: | |
| [ | |
| install-and-build, | |
| unit-test, | |
| typecheck, | |
| lint, | |
| check-packaging, | |
| sqlite-sanity, | |
| e2e, | |
| dev-server-smoke, | |
| db-tests, | |
| performance, | |
| security-checks, | |
| workflow-scripts, | |
| ] | |
| if: always() | |
| runs-on: ubuntu-slim | |
| steps: | |
| - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 | |
| with: | |
| sparse-checkout: .github/actions/ci-filter | |
| sparse-checkout-cone-mode: false | |
| - name: Validate required checks | |
| uses: ./.github/actions/ci-filter | |
| with: | |
| mode: validate | |
| job-results: ${{ toJSON(needs) }} | |
| # Posts a QA metrics comparison comment on the PR. | |
| # Runs after all checks so any job can emit metrics before this reports. | |
| post-qa-metrics-comment: | |
| name: QA Metrics | |
| needs: [required-checks, e2e-performance] | |
| # Only post when the perf suite actually measured this PR, otherwise the | |
| # comment fires on trivial PRs and flags master drift/noise that isn't theirs. | |
| if: always() && needs.e2e-performance.result == 'success' | |
| uses: ./.github/workflows/util-qa-metrics-comment-reusable.yml | |
| with: | |
| metrics: memory-heap-used-baseline,memory-rss-baseline,instance-ai-heap-used-baseline,instance-ai-rss-baseline,agents-heap-used-baseline,agents-rss-baseline,docker-image-size-n8n,docker-image-size-runners,canvas-cold-load-S-ms,canvas-cold-load-M-ms,canvas-cold-load-L-ms,canvas-server-heap-M-mb,canvas-server-heap-L-mb,canvas-browser-heap-M-mb,canvas-browser-heap-L-mb,canvas-pan-frame-p95-M-ms,canvas-pan-frame-p95-L-ms,canvas-tidy-up-M-ms,canvas-tidy-up-L-ms,canvas-exec-heavy-M-ms,canvas-exec-heavy-L-ms | |
| secrets: inherit |