Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .actrc +5 -0
- .devcontainer/Dockerfile +9 -0
- .devcontainer/devcontainer.json +19 -0
- .devcontainer/docker-compose.yml +24 -0
- .dockerignore +27 -0
- .editorconfig +20 -0
- .git-blame-ignore-revs +18 -0
- .gitattributes +17 -35
- .github/CODEOWNERS +4 -0
- .github/ISSUE_TEMPLATE/01-bug.yml +105 -0
- .github/ISSUE_TEMPLATE/config.yml +11 -0
- .github/actionlint.yml +8 -0
- .github/actions/docker-registry-login/action.yml +51 -0
- .github/actions/setup-nodejs/action.yml +49 -0
- .github/docker-compose.yml +33 -0
- .github/pull_request_template.md +29 -0
- .github/pull_request_title_conventions.md +116 -0
- .github/scripts/bump-versions.mjs +74 -0
- .github/scripts/docker/docker-config.mjs +177 -0
- .github/scripts/docker/docker-tags.mjs +113 -0
- .github/scripts/ensure-provenance-fields.mjs +44 -0
- .github/scripts/package.json +12 -0
- .github/scripts/trim-fe-packageJson.js +18 -0
- .github/scripts/update-changelog.mjs +40 -0
- .github/scripts/validate-docs-links.js +90 -0
- .github/test-metrics/playwright.json +672 -0
- .github/workflows/benchmark-destroy-nightly.yml +46 -0
- .github/workflows/benchmark-nightly.yml +122 -0
- .github/workflows/build-unit-test-pr-comment.yml +100 -0
- .github/workflows/build-windows.yml +81 -0
- .github/workflows/check-documentation-urls.yml +37 -0
- .github/workflows/check-pr-title.yml +21 -0
- .github/workflows/check-run-eligibility.yml +111 -0
- .github/workflows/chromatic.yml +83 -0
- .github/workflows/ci-evals.yml +73 -0
- .github/workflows/ci-manual-build-unit-tests.yml +132 -0
- .github/workflows/ci-master.yml +52 -0
- .github/workflows/ci-postgres-mysql.yml +152 -0
- .github/workflows/ci-pull-requests.yml +129 -0
- .github/workflows/ci-python-workflow-builder-evals.yml +56 -0
- .github/workflows/ci-python.yml +56 -0
- .github/workflows/ci-security.yml +23 -0
- .github/workflows/claude.yml +48 -0
- .github/workflows/create-patch-release-branch.yml +69 -0
- .github/workflows/data-tooling.yml +84 -0
- .github/workflows/docker-base-image.yml +68 -0
- .github/workflows/docker-build-push.yml +294 -0
- .github/workflows/docker-images-benchmark.yml +41 -0
- .github/workflows/linting-reusable.yml +33 -0
- .github/workflows/notify-pr-status.yml +27 -0
.actrc
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-P blacksmith-2vcpu-ubuntu-2204=ubuntu-latest
|
| 2 |
+
-P blacksmith-4vcpu-ubuntu-2204=ubuntu-latest
|
| 3 |
+
-P ubuntu-22.04=ubuntu-latest
|
| 4 |
+
-P ubuntu-20.04=ubuntu-latest
|
| 5 |
+
--container-architecture linux/amd64
|
.devcontainer/Dockerfile
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM n8nio/base:22
|
| 2 |
+
|
| 3 |
+
RUN apk add --no-cache --update openssh sudo shadow bash
|
| 4 |
+
RUN echo node ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/node && chmod 0440 /etc/sudoers.d/node
|
| 5 |
+
RUN mkdir /workspaces && chown node:node /workspaces
|
| 6 |
+
RUN npm install -g pnpm
|
| 7 |
+
|
| 8 |
+
USER node
|
| 9 |
+
RUN mkdir -p ~/.pnpm-store && pnpm config set store-dir ~/.pnpm-store --global
|
.devcontainer/devcontainer.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "n8n",
|
| 3 |
+
"dockerComposeFile": "docker-compose.yml",
|
| 4 |
+
"service": "n8n",
|
| 5 |
+
"workspaceFolder": "/workspaces",
|
| 6 |
+
"mounts": [
|
| 7 |
+
"type=bind,source=${localWorkspaceFolder},target=/workspaces,consistency=cached",
|
| 8 |
+
"type=bind,source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,consistency=cached",
|
| 9 |
+
"type=bind,source=${localEnv:HOME}/.n8n,target=/home/node/.n8n,consistency=cached"
|
| 10 |
+
],
|
| 11 |
+
"forwardPorts": [8080, 5678],
|
| 12 |
+
"postCreateCommand": "corepack prepare --activate && pnpm install",
|
| 13 |
+
"postAttachCommand": "pnpm build",
|
| 14 |
+
"customizations": {
|
| 15 |
+
"codespaces": {
|
| 16 |
+
"openFiles": ["CONTRIBUTING.md"]
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
}
|
.devcontainer/docker-compose.yml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
volumes:
|
| 2 |
+
postgres-data:
|
| 3 |
+
|
| 4 |
+
services:
|
| 5 |
+
postgres:
|
| 6 |
+
image: postgres:16-alpine
|
| 7 |
+
restart: unless-stopped
|
| 8 |
+
volumes:
|
| 9 |
+
- postgres-data:/var/lib/postgresql/data
|
| 10 |
+
environment:
|
| 11 |
+
- POSTGRES_DB=n8n
|
| 12 |
+
- POSTGRES_PASSWORD=password
|
| 13 |
+
|
| 14 |
+
n8n:
|
| 15 |
+
build:
|
| 16 |
+
context: .
|
| 17 |
+
dockerfile: Dockerfile
|
| 18 |
+
volumes:
|
| 19 |
+
- ..:/workspaces:cached
|
| 20 |
+
command: sleep infinity
|
| 21 |
+
environment:
|
| 22 |
+
DB_POSTGRESDB_HOST: postgres
|
| 23 |
+
DB_TYPE: postgresdb
|
| 24 |
+
DB_POSTGRESDB_PASSWORD: password
|
.dockerignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Whitelist approach: ignore everything, then allow only what Docker builds need
|
| 2 |
+
# This reduces build context from ~900MB to just what's required
|
| 3 |
+
|
| 4 |
+
# Ignore everything first
|
| 5 |
+
*
|
| 6 |
+
|
| 7 |
+
# === n8n main image (docker/images/n8n/Dockerfile) ===
|
| 8 |
+
!compiled
|
| 9 |
+
!compiled/**
|
| 10 |
+
!THIRD_PARTY_LICENSES.md
|
| 11 |
+
|
| 12 |
+
# === runners image (docker/images/runners/Dockerfile + Dockerfile.distroless) ===
|
| 13 |
+
!dist
|
| 14 |
+
!dist/task-runner-javascript
|
| 15 |
+
!dist/task-runner-javascript/**
|
| 16 |
+
!packages
|
| 17 |
+
!packages/@n8n
|
| 18 |
+
!packages/@n8n/task-runner-python
|
| 19 |
+
!packages/@n8n/task-runner-python/**
|
| 20 |
+
|
| 21 |
+
# === Docker build files (entrypoints, configs) ===
|
| 22 |
+
!docker
|
| 23 |
+
!docker/images
|
| 24 |
+
!docker/images/n8n
|
| 25 |
+
!docker/images/n8n/docker-entrypoint.sh
|
| 26 |
+
!docker/images/runners
|
| 27 |
+
!docker/images/runners/n8n-task-runners.json
|
.editorconfig
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
root = true
|
| 2 |
+
|
| 3 |
+
[*]
|
| 4 |
+
charset = utf-8
|
| 5 |
+
indent_style = tab
|
| 6 |
+
indent_size = 2
|
| 7 |
+
end_of_line = lf
|
| 8 |
+
insert_final_newline = true
|
| 9 |
+
trim_trailing_whitespace = true
|
| 10 |
+
|
| 11 |
+
[package.json]
|
| 12 |
+
indent_style = space
|
| 13 |
+
indent_size = 2
|
| 14 |
+
|
| 15 |
+
[*.yml]
|
| 16 |
+
indent_style = space
|
| 17 |
+
indent_size = 2
|
| 18 |
+
|
| 19 |
+
[*.ts]
|
| 20 |
+
quote_type = single
|
.git-blame-ignore-revs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Commits of large-scale changes to exclude from `git blame` results
|
| 2 |
+
|
| 3 |
+
# Set up linting and formatting (#2120)
|
| 4 |
+
|
| 5 |
+
56c4c6991fb21ba4b7bdcd22c929f63cc1d1defe
|
| 6 |
+
|
| 7 |
+
# refactor(editor): Apply Prettier (no-changelog) #4920
|
| 8 |
+
|
| 9 |
+
5ca2148c7ed06c90f999508928b7a51f9ac7a788
|
| 10 |
+
|
| 11 |
+
# refactor: Run lintfix (no-changelog) (#7537)
|
| 12 |
+
|
| 13 |
+
62c096710fab2f7e886518abdbded34b55e93f62
|
| 14 |
+
|
| 15 |
+
# refactor: Move test files alongside tested files (#11504)
|
| 16 |
+
|
| 17 |
+
7e58fc4fec468aca0b45d5bfe6150e1af632acbc
|
| 18 |
+
f32b13c6ed078be042a735bc8621f27e00dc3116
|
.gitattributes
CHANGED
|
@@ -1,35 +1,17 @@
|
|
| 1 |
-
*.
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
*.sh text eol=lf
|
| 2 |
+
assets/n8n-screenshot-readme.png filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
assets/n8n-screenshot.png filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
packages/frontend/@n8n/chat/resources/images/windowed.png filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
packages/frontend/@n8n/design-system/assets/fonts/InterVariable-Italic.woff2 filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
packages/frontend/@n8n/design-system/assets/fonts/InterVariable.woff2 filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
packages/frontend/@n8n/i18n/docs/img/cred.png filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
packages/frontend/@n8n/i18n/docs/img/header1.png filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
packages/frontend/@n8n/i18n/docs/img/header2.png filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
packages/frontend/@n8n/i18n/docs/img/header3.png filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
packages/frontend/@n8n/i18n/docs/img/header4.png filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
packages/frontend/@n8n/i18n/docs/img/header5.png filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
packages/frontend/editor-ui/public/static/data-mapping-gif.gif filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
packages/frontend/editor-ui/src/features/integrations/externalSecrets.ee/assets/images/doppler.webp filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
packages/nodes-base/nodes/AcuityScheduling/acuityScheduling.png filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.png filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
packages/testing/playwright/tests/cli-workflows/testData/pdfs/05-versions-space.pdf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.github/CODEOWNERS
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
packages/@n8n/db/src/migrations/ @n8n-io/migrations-review
|
| 2 |
+
|
| 3 |
+
# Node popularity data updates
|
| 4 |
+
packages/frontend/editor-ui/data/node-popularity.json @n8n-io/catalysts
|
.github/ISSUE_TEMPLATE/01-bug.yml
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Bug Report
|
| 2 |
+
description: Create a bug report to help us improve
|
| 3 |
+
body:
|
| 4 |
+
- type: markdown
|
| 5 |
+
attributes:
|
| 6 |
+
value: |
|
| 7 |
+
> ⚠️ This form is for reporting bugs only.
|
| 8 |
+
> ❌ Please do not use this form for general support, feature requests, or questions.
|
| 9 |
+
> 💬 For help and general inquiries, visit our [community support forum](https://community.n8n.io).
|
| 10 |
+
> ☁️ If you're experiencing issues with cloud instances not starting or license-related problems, contact [n8n support directly](mailto:help@n8n.io).
|
| 11 |
+
---
|
| 12 |
+
Thank you for helping us improve n8n!
|
| 13 |
+
To ensure we can address your report efficiently, please fill out all sections in English and provide as much detail as possible.
|
| 14 |
+
- type: textarea
|
| 15 |
+
id: description
|
| 16 |
+
attributes:
|
| 17 |
+
label: Bug Description
|
| 18 |
+
description: A clear and concise description of what the bug is
|
| 19 |
+
placeholder: Tell us what you see!
|
| 20 |
+
validations:
|
| 21 |
+
required: true
|
| 22 |
+
- type: textarea
|
| 23 |
+
id: reproduction
|
| 24 |
+
attributes:
|
| 25 |
+
label: To Reproduce
|
| 26 |
+
description: Steps to reproduce the behavior
|
| 27 |
+
placeholder: |
|
| 28 |
+
1. Go to '...'
|
| 29 |
+
2. Click on '....'
|
| 30 |
+
3. Scroll down to '....'
|
| 31 |
+
4. See error
|
| 32 |
+
validations:
|
| 33 |
+
required: true
|
| 34 |
+
- type: textarea
|
| 35 |
+
id: expected
|
| 36 |
+
attributes:
|
| 37 |
+
label: Expected behavior
|
| 38 |
+
description: A clear and concise description of what you expected to happen
|
| 39 |
+
validations:
|
| 40 |
+
required: true
|
| 41 |
+
- type: textarea
|
| 42 |
+
id: debug-info
|
| 43 |
+
attributes:
|
| 44 |
+
label: Debug Info
|
| 45 |
+
description: This can be found under Help > About n8n > Copy debug information
|
| 46 |
+
validations:
|
| 47 |
+
required: true
|
| 48 |
+
- type: markdown
|
| 49 |
+
attributes:
|
| 50 |
+
value: '## Environment'
|
| 51 |
+
- type: input
|
| 52 |
+
id: os
|
| 53 |
+
attributes:
|
| 54 |
+
label: Operating System
|
| 55 |
+
placeholder: ex. Ubuntu Linux 22.04
|
| 56 |
+
validations:
|
| 57 |
+
required: true
|
| 58 |
+
- type: input
|
| 59 |
+
id: n8n-version
|
| 60 |
+
attributes:
|
| 61 |
+
label: n8n Version
|
| 62 |
+
placeholder: ex. 1.25.0
|
| 63 |
+
validations:
|
| 64 |
+
required: true
|
| 65 |
+
- type: input
|
| 66 |
+
id: nodejs-version
|
| 67 |
+
attributes:
|
| 68 |
+
label: Node.js Version
|
| 69 |
+
placeholder: ex. 22.16.0
|
| 70 |
+
validations:
|
| 71 |
+
required: true
|
| 72 |
+
- type: dropdown
|
| 73 |
+
id: db
|
| 74 |
+
attributes:
|
| 75 |
+
label: Database
|
| 76 |
+
options:
|
| 77 |
+
- SQLite (default)
|
| 78 |
+
- PostgreSQL
|
| 79 |
+
- MySQL
|
| 80 |
+
- MariaDB
|
| 81 |
+
default: 0
|
| 82 |
+
validations:
|
| 83 |
+
required: true
|
| 84 |
+
- type: dropdown
|
| 85 |
+
id: execution-mode
|
| 86 |
+
attributes:
|
| 87 |
+
label: Execution mode
|
| 88 |
+
description: '[Info](https://docs.n8n.io/hosting/scaling/queue-mode/)'
|
| 89 |
+
options:
|
| 90 |
+
- main (default)
|
| 91 |
+
- queue
|
| 92 |
+
- own (deprecated)
|
| 93 |
+
default: 0
|
| 94 |
+
validations:
|
| 95 |
+
required: true
|
| 96 |
+
- type: dropdown
|
| 97 |
+
id: hosting
|
| 98 |
+
attributes:
|
| 99 |
+
label: Hosting
|
| 100 |
+
options:
|
| 101 |
+
- n8n cloud
|
| 102 |
+
- self hosted
|
| 103 |
+
default: 0
|
| 104 |
+
validations:
|
| 105 |
+
required: true
|
.github/ISSUE_TEMPLATE/config.yml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
blank_issues_enabled: false
|
| 2 |
+
contact_links:
|
| 3 |
+
- name: Feature request
|
| 4 |
+
url: https://community.n8n.io
|
| 5 |
+
about: Suggest an idea for this project
|
| 6 |
+
- name: Question / Problem
|
| 7 |
+
url: https://community.n8n.io
|
| 8 |
+
about: Questions and problems with n8n
|
| 9 |
+
- name: n8n Security Vulnerability
|
| 10 |
+
url: https://n8n.io/legal/#vulnerability
|
| 11 |
+
about: Learn about our Vulnerability Disclosure Policy
|
.github/actionlint.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
self-hosted-runner:
|
| 2 |
+
labels:
|
| 3 |
+
- blacksmith-2vcpu-ubuntu-2204
|
| 4 |
+
- blacksmith-4vcpu-ubuntu-2204
|
| 5 |
+
- blacksmith-2vcpu-ubuntu-2204-arm
|
| 6 |
+
- blacksmith-4vcpu-ubuntu-2204-arm
|
| 7 |
+
- blacksmith-8vcpu-ubuntu-2204
|
| 8 |
+
- ubuntu-slim
|
.github/actions/docker-registry-login/action.yml
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Composite action for logging into Docker registries (GHCR and/or DockerHub).
|
| 2 |
+
# Centralizes the login pattern used across multiple Docker workflows.
|
| 3 |
+
|
| 4 |
+
name: 'Docker Registry Login'
|
| 5 |
+
description: 'Login to GitHub Container Registry and/or DockerHub'
|
| 6 |
+
|
| 7 |
+
inputs:
|
| 8 |
+
login-ghcr:
|
| 9 |
+
description: 'Login to GitHub Container Registry'
|
| 10 |
+
required: false
|
| 11 |
+
default: 'true'
|
| 12 |
+
login-dockerhub:
|
| 13 |
+
description: 'Login to DockerHub'
|
| 14 |
+
required: false
|
| 15 |
+
default: 'false'
|
| 16 |
+
login-dhi:
|
| 17 |
+
description: 'Login to Docker Hardened Images registry (dhi.io)'
|
| 18 |
+
required: false
|
| 19 |
+
default: 'false'
|
| 20 |
+
dockerhub-username:
|
| 21 |
+
description: 'DockerHub username (required if login-dockerhub or login-dhi is true)'
|
| 22 |
+
required: false
|
| 23 |
+
dockerhub-password:
|
| 24 |
+
description: 'DockerHub password (required if login-dockerhub or login-dhi is true)'
|
| 25 |
+
required: false
|
| 26 |
+
|
| 27 |
+
runs:
|
| 28 |
+
using: 'composite'
|
| 29 |
+
steps:
|
| 30 |
+
- name: Login to GitHub Container Registry
|
| 31 |
+
if: inputs.login-ghcr == 'true'
|
| 32 |
+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
| 33 |
+
with:
|
| 34 |
+
registry: ghcr.io
|
| 35 |
+
username: ${{ github.actor }}
|
| 36 |
+
password: ${{ github.token }}
|
| 37 |
+
|
| 38 |
+
- name: Login to DockerHub
|
| 39 |
+
if: inputs.login-dockerhub == 'true'
|
| 40 |
+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
| 41 |
+
with:
|
| 42 |
+
username: ${{ inputs.dockerhub-username }}
|
| 43 |
+
password: ${{ inputs.dockerhub-password }}
|
| 44 |
+
|
| 45 |
+
- name: Login to DHI Registry
|
| 46 |
+
if: inputs.login-dhi == 'true'
|
| 47 |
+
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
| 48 |
+
with:
|
| 49 |
+
registry: dhi.io
|
| 50 |
+
username: ${{ inputs.dockerhub-username }}
|
| 51 |
+
password: ${{ inputs.dockerhub-password }}
|
.github/actions/setup-nodejs/action.yml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This action works transparently on both Blacksmith and GitHub-hosted runners.
|
| 2 |
+
# Blacksmith runners benefit from transparent caching and optional Docker layer caching.
|
| 3 |
+
# GitHub-hosted runners use standard GitHub Actions caching.
|
| 4 |
+
|
| 5 |
+
name: 'Node.js Build Setup'
|
| 6 |
+
description: 'Configures Node.js with pnpm, installs dependencies, enables Turborepo caching, (optional) sets up Docker layer caching, and builds the project or an optional command.'
|
| 7 |
+
|
| 8 |
+
inputs:
|
| 9 |
+
node-version:
|
| 10 |
+
description: 'Node.js version to use. Uses latest 22.x by default.'
|
| 11 |
+
required: false
|
| 12 |
+
default: '22.x'
|
| 13 |
+
enable-docker-cache:
|
| 14 |
+
description: 'Whether to set up Blacksmith Buildx for Docker layer caching (Blacksmith runners only).'
|
| 15 |
+
required: false
|
| 16 |
+
default: 'false'
|
| 17 |
+
type: boolean
|
| 18 |
+
build-command:
|
| 19 |
+
description: 'Command to execute for building the project or an optional command. Leave empty to skip build step.'
|
| 20 |
+
required: false
|
| 21 |
+
default: 'pnpm build'
|
| 22 |
+
type: string
|
| 23 |
+
|
| 24 |
+
runs:
|
| 25 |
+
using: 'composite'
|
| 26 |
+
steps:
|
| 27 |
+
- name: Setup pnpm
|
| 28 |
+
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
| 29 |
+
|
| 30 |
+
- name: Setup Node.js
|
| 31 |
+
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
| 32 |
+
with:
|
| 33 |
+
node-version: ${{ inputs.node-version }}
|
| 34 |
+
cache: 'pnpm'
|
| 35 |
+
|
| 36 |
+
- name: Install Dependencies
|
| 37 |
+
run: pnpm install --frozen-lockfile
|
| 38 |
+
shell: bash
|
| 39 |
+
|
| 40 |
+
- name: Configure Turborepo Cache
|
| 41 |
+
uses: rharkor/caching-for-turbo@cda201ff2b32699a2ae9f59704db029e3dde4fbd # v2.3.5
|
| 42 |
+
|
| 43 |
+
- name: Setup Docker Builder for Docker Cache
|
| 44 |
+
if: ${{ inputs.enable-docker-cache == 'true' }}
|
| 45 |
+
uses: useblacksmith/setup-docker-builder@53647ab5afe8827af5623b35bd4302eabd41619f # v1.2.0
|
| 46 |
+
|
| 47 |
+
- name: Build Project
|
| 48 |
+
run: ${{ inputs.build-command }}
|
| 49 |
+
shell: bash
|
.github/docker-compose.yml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
mariadb:
|
| 3 |
+
image: mariadb:10.5
|
| 4 |
+
environment:
|
| 5 |
+
- MARIADB_DATABASE=n8n
|
| 6 |
+
- MARIADB_ROOT_PASSWORD=password
|
| 7 |
+
- MARIADB_MYSQL_LOCALHOST_USER=true
|
| 8 |
+
ports:
|
| 9 |
+
- 3306:3306
|
| 10 |
+
tmpfs:
|
| 11 |
+
- /var/lib/mysql
|
| 12 |
+
|
| 13 |
+
mysql-8.4:
|
| 14 |
+
image: mysql:8.4
|
| 15 |
+
environment:
|
| 16 |
+
- MYSQL_DATABASE=n8n
|
| 17 |
+
- MYSQL_ROOT_PASSWORD=password
|
| 18 |
+
ports:
|
| 19 |
+
- 3306:3306
|
| 20 |
+
tmpfs:
|
| 21 |
+
- /var/lib/mysql
|
| 22 |
+
|
| 23 |
+
postgres:
|
| 24 |
+
image: postgres:16
|
| 25 |
+
restart: always
|
| 26 |
+
environment:
|
| 27 |
+
- POSTGRES_DB=n8n
|
| 28 |
+
- POSTGRES_USER=postgres
|
| 29 |
+
- POSTGRES_PASSWORD=password
|
| 30 |
+
ports:
|
| 31 |
+
- 5432:5432
|
| 32 |
+
tmpfs:
|
| 33 |
+
- /var/lib/postgresql/data
|
.github/pull_request_template.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Summary
|
| 2 |
+
|
| 3 |
+
<!--
|
| 4 |
+
Describe what the PR does and how to test.
|
| 5 |
+
Photos and videos are recommended.
|
| 6 |
+
-->
|
| 7 |
+
|
| 8 |
+
## Related Linear tickets, Github issues, and Community forum posts
|
| 9 |
+
|
| 10 |
+
<!--
|
| 11 |
+
Include links to **Linear ticket** or Github issue or Community forum post.
|
| 12 |
+
Important in order to close *automatically* and provide context to reviewers.
|
| 13 |
+
https://linear.app/n8n/issue/
|
| 14 |
+
-->
|
| 15 |
+
<!-- Use "closes #<issue-number>", "fixes #<issue-number>", or "resolves #<issue-number>" to automatically close issues when the PR is merged. -->
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
## Review / Merge checklist
|
| 19 |
+
|
| 20 |
+
- [ ] PR title and summary are descriptive. ([conventions](../blob/master/.github/pull_request_title_conventions.md)) <!--
|
| 21 |
+
**Remember, the title automatically goes into the changelog.
|
| 22 |
+
Use `(no-changelog)` otherwise.**
|
| 23 |
+
-->
|
| 24 |
+
- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created.
|
| 25 |
+
- [ ] Tests included. <!--
|
| 26 |
+
A bug is not considered fixed, unless a test is added to prevent it from happening again.
|
| 27 |
+
A feature is not complete without tests.
|
| 28 |
+
-->
|
| 29 |
+
- [ ] PR Labeled with `release/backport` (if the PR is an urgent fix that needs to be backported)
|
.github/pull_request_title_conventions.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PR Title Convention
|
| 2 |
+
|
| 3 |
+
We have very precise rules over how Pull Requests (to the `master` branch) must be formatted. This format basically follows the [Angular Commit Message Convention](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit). It leads to easier to read commit history and allows for automated generation of release notes:
|
| 4 |
+
|
| 5 |
+
A PR title consists of these elements:
|
| 6 |
+
|
| 7 |
+
```text
|
| 8 |
+
<type>(<scope>): <summary>
|
| 9 |
+
│ │ │
|
| 10 |
+
│ │ └─⫸ Summary: In imperative present tense.
|
| 11 |
+
| | Capitalized
|
| 12 |
+
| | No period at the end.
|
| 13 |
+
│ │
|
| 14 |
+
│ └─⫸ Scope: API | benchmark | core | editor | * Node
|
| 15 |
+
│
|
| 16 |
+
└─⫸ Type: build | ci | chore | docs | feat | fix | perf | refactor | test
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
- PR title
|
| 20 |
+
- type
|
| 21 |
+
- scope (_optional_)
|
| 22 |
+
- summary
|
| 23 |
+
- PR description
|
| 24 |
+
- body (optional)
|
| 25 |
+
- blank line
|
| 26 |
+
- footer (optional)
|
| 27 |
+
|
| 28 |
+
The structure looks like this:
|
| 29 |
+
|
| 30 |
+
## Type
|
| 31 |
+
|
| 32 |
+
Must be one of the following:
|
| 33 |
+
|
| 34 |
+
| type | description | appears in changelog |
|
| 35 |
+
| --- | --- | --- |
|
| 36 |
+
| `feat` | A new feature | ✅ |
|
| 37 |
+
| `fix` | A bug fix | ✅ |
|
| 38 |
+
| `perf` | A code change that improves performance | ✅ |
|
| 39 |
+
| `test` | Adding missing tests or correcting existing tests | ❌ |
|
| 40 |
+
| `docs` | Documentation only changes | ❌ |
|
| 41 |
+
| `refactor` | A behavior-neutral code change that neither fixes a bug nor adds a feature | ❌ |
|
| 42 |
+
| `build` | Changes that affect the build system or external dependencies (TypeScript, Jest, pnpm, etc.) | ❌ |
|
| 43 |
+
| `ci` | Changes to CI configuration files and scripts (e.g. Github actions) | ❌ |
|
| 44 |
+
| `chore` | Routine tasks, maintenance, and minor updates not covered by other types | ❌ |
|
| 45 |
+
|
| 46 |
+
> BREAKING CHANGES (see Footer section below), will **always** appear in the changelog unless suffixed with `no-changelog`.
|
| 47 |
+
|
| 48 |
+
## Scope (optional)
|
| 49 |
+
|
| 50 |
+
The scope should specify the place of the commit change as long as the commit clearly addresses one of the following supported scopes. (Otherwise, omit the scope!)
|
| 51 |
+
|
| 52 |
+
- `API` - changes to the _public_ API
|
| 53 |
+
- `benchmark` - changes to the benchmark cli
|
| 54 |
+
- `core` - changes to the core / private API / backend of n8n
|
| 55 |
+
- `editor` - changes to the Editor UI
|
| 56 |
+
- `* Node` - changes to a specific node or trigger node (”`*`” to be replaced with the node name, not its display name), e.g.
|
| 57 |
+
- mattermost → Mattermost Node
|
| 58 |
+
- microsoftToDo → Microsoft To Do Node
|
| 59 |
+
- n8n → n8n Node
|
| 60 |
+
|
| 61 |
+
## Summary
|
| 62 |
+
|
| 63 |
+
The summary contains succinct description of the change:
|
| 64 |
+
|
| 65 |
+
- use the imperative, present tense: "change" not "changed" nor "changes"
|
| 66 |
+
- capitalize the first letter
|
| 67 |
+
- _no_ dot (.) at the end
|
| 68 |
+
- do _not_ include Linear ticket IDs etc. (e.g. N8N-1234)
|
| 69 |
+
- suffix with “(no-changelog)” for commits / PRs that should not get mentioned in the changelog.
|
| 70 |
+
|
| 71 |
+
## Body (optional)
|
| 72 |
+
|
| 73 |
+
Just as in the **summary**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
| 74 |
+
|
| 75 |
+
## Footer (optional)
|
| 76 |
+
|
| 77 |
+
The footer can contain information about breaking changes and deprecations and is also the place to [reference GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), Linear tickets, and other PRs that this commit closes or is related to. For example:
|
| 78 |
+
|
| 79 |
+
```text
|
| 80 |
+
BREAKING CHANGE: <breaking change summary>
|
| 81 |
+
<BLANK LINE>
|
| 82 |
+
<breaking change description + migration instructions>
|
| 83 |
+
<BLANK LINE>
|
| 84 |
+
<BLANK LINE>
|
| 85 |
+
Fixes #<issue number>
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
or
|
| 89 |
+
|
| 90 |
+
```text
|
| 91 |
+
DEPRECATED: <what is deprecated>
|
| 92 |
+
<BLANK LINE>
|
| 93 |
+
<deprecation description + recommended update path>
|
| 94 |
+
<BLANK LINE>
|
| 95 |
+
<BLANK LINE>
|
| 96 |
+
Closes #<pr number>
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
A Breaking Change section should start with the phrase "`BREAKING CHANGE:` " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
|
| 100 |
+
|
| 101 |
+
> 💡 A breaking change can additionally also be marked by adding a “`!`” to the header, right before the “`:`”, e.g. `feat(editor)!: Remove support for dark mode`
|
| 102 |
+
>
|
| 103 |
+
> This makes locating breaking changes easier when just skimming through commit messages.
|
| 104 |
+
|
| 105 |
+
> 💡 The breaking changes must also be added to the [packages/cli/BREAKING-CHANGES.md](https://github.com/n8n-io/n8n/blob/master/packages/cli/BREAKING-CHANGES.md) file located in the n8n repository.
|
| 106 |
+
|
| 107 |
+
Similarly, a Deprecation section should start with "`DEPRECATED:` " followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path.
|
| 108 |
+
|
| 109 |
+
### Revert commits
|
| 110 |
+
|
| 111 |
+
If the commit reverts a previous commit, it should begin with `revert:` , followed by the header of the reverted commit.
|
| 112 |
+
|
| 113 |
+
The content of the commit message body should contain:
|
| 114 |
+
|
| 115 |
+
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
|
| 116 |
+
- a clear description of the reason for reverting the commit message.
|
.github/scripts/bump-versions.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import semver from 'semver';
|
| 2 |
+
import { writeFile, readFile } from 'fs/promises';
|
| 3 |
+
import { resolve } from 'path';
|
| 4 |
+
import child_process from 'child_process';
|
| 5 |
+
import { promisify } from 'util';
|
| 6 |
+
import assert from 'assert';
|
| 7 |
+
|
| 8 |
+
const exec = promisify(child_process.exec);
|
| 9 |
+
|
| 10 |
+
function generateExperimentalVersion(currentVersion) {
|
| 11 |
+
const parsed = semver.parse(currentVersion);
|
| 12 |
+
if (!parsed) throw new Error(`Invalid version: ${currentVersion}`);
|
| 13 |
+
|
| 14 |
+
// Check if it's already an experimental version
|
| 15 |
+
if (parsed.prerelease.length > 0 && parsed.prerelease[0] === 'exp') {
|
| 16 |
+
// Increment the experimental minor version
|
| 17 |
+
const expMinor = (parsed.prerelease[1] || 0) + 1;
|
| 18 |
+
return `${parsed.major}.${parsed.minor}.${parsed.patch}-exp.${expMinor}`;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
// Create new experimental version: <major>.<minor>.<patch>-exp.0
|
| 22 |
+
return `${parsed.major}.${parsed.minor}.${parsed.patch}-exp.0`;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
const rootDir = process.cwd();
|
| 26 |
+
const releaseType = process.env.RELEASE_TYPE;
|
| 27 |
+
assert.match(releaseType, /^(patch|minor|major|experimental|premajor)$/, 'Invalid RELEASE_TYPE');
|
| 28 |
+
|
| 29 |
+
// TODO: if releaseType is `auto` determine release type based on the changelog
|
| 30 |
+
|
| 31 |
+
const lastTag = (await exec('git describe --tags --match "n8n@*" --abbrev=0')).stdout.trim();
|
| 32 |
+
const packages = JSON.parse((await exec('pnpm ls -r --only-projects --json')).stdout);
|
| 33 |
+
|
| 34 |
+
const packageMap = {};
|
| 35 |
+
for (let { name, path, version, private: isPrivate, dependencies } of packages) {
|
| 36 |
+
if (isPrivate && path !== rootDir) continue;
|
| 37 |
+
if (path === rootDir) name = 'monorepo-root';
|
| 38 |
+
|
| 39 |
+
const isDirty = await exec(`git diff --quiet HEAD ${lastTag} -- ${path}`)
|
| 40 |
+
.then(() => false)
|
| 41 |
+
.catch((error) => true);
|
| 42 |
+
|
| 43 |
+
packageMap[name] = { path, isDirty, version };
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
assert.ok(
|
| 47 |
+
Object.values(packageMap).some(({ isDirty }) => isDirty),
|
| 48 |
+
'No changes found since the last release',
|
| 49 |
+
);
|
| 50 |
+
|
| 51 |
+
// Keep the monorepo version up to date with the released version
|
| 52 |
+
packageMap['monorepo-root'].version = packageMap['n8n'].version;
|
| 53 |
+
|
| 54 |
+
for (const packageName in packageMap) {
|
| 55 |
+
const { path, version, isDirty } = packageMap[packageName];
|
| 56 |
+
const packageFile = resolve(path, 'package.json');
|
| 57 |
+
const packageJson = JSON.parse(await readFile(packageFile, 'utf-8'));
|
| 58 |
+
|
| 59 |
+
packageJson.version = packageMap[packageName].nextVersion =
|
| 60 |
+
isDirty ||
|
| 61 |
+
Object.keys(packageJson.dependencies || {}).some(
|
| 62 |
+
(dependencyName) => packageMap[dependencyName]?.isDirty,
|
| 63 |
+
)
|
| 64 |
+
? releaseType === 'experimental'
|
| 65 |
+
? generateExperimentalVersion(version)
|
| 66 |
+
: releaseType === 'premajor'
|
| 67 |
+
? semver.inc(version, version.includes('-rc.') ? 'prerelease' : 'premajor', undefined, 'rc')
|
| 68 |
+
: semver.inc(version, releaseType)
|
| 69 |
+
: version;
|
| 70 |
+
|
| 71 |
+
await writeFile(packageFile, JSON.stringify(packageJson, null, 2) + '\n');
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
console.log(packageMap['n8n'].nextVersion);
|
.github/scripts/docker/docker-config.mjs
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
|
| 3 |
+
import { appendFileSync } from 'node:fs';
|
| 4 |
+
|
| 5 |
+
class BuildContext {
|
| 6 |
+
constructor() {
|
| 7 |
+
this.githubOutput = process.env.GITHUB_OUTPUT || null;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
determine({ event, pr, branch, version, releaseType, pushEnabled }) {
|
| 11 |
+
let context = {
|
| 12 |
+
version: '',
|
| 13 |
+
release_type: '',
|
| 14 |
+
platforms: ['linux/amd64', 'linux/arm64'],
|
| 15 |
+
push_to_ghcr: true,
|
| 16 |
+
push_to_docker: false,
|
| 17 |
+
};
|
| 18 |
+
|
| 19 |
+
if (version && releaseType) {
|
| 20 |
+
context.version = version;
|
| 21 |
+
context.release_type = releaseType;
|
| 22 |
+
context.push_to_docker = true;
|
| 23 |
+
} else {
|
| 24 |
+
switch (event) {
|
| 25 |
+
case 'schedule':
|
| 26 |
+
context.version = 'nightly';
|
| 27 |
+
context.release_type = 'nightly';
|
| 28 |
+
context.push_to_docker = true;
|
| 29 |
+
break;
|
| 30 |
+
|
| 31 |
+
case 'pull_request':
|
| 32 |
+
context.version = `pr-${pr}`;
|
| 33 |
+
context.release_type = 'dev';
|
| 34 |
+
context.platforms = ['linux/amd64'];
|
| 35 |
+
break;
|
| 36 |
+
|
| 37 |
+
case 'workflow_dispatch':
|
| 38 |
+
context.version = `branch-${this.sanitizeBranch(branch)}`;
|
| 39 |
+
context.release_type = 'branch';
|
| 40 |
+
context.platforms = ['linux/amd64'];
|
| 41 |
+
break;
|
| 42 |
+
|
| 43 |
+
case 'push':
|
| 44 |
+
if (branch === 'master') {
|
| 45 |
+
context.version = 'dev';
|
| 46 |
+
context.release_type = 'dev';
|
| 47 |
+
context.push_to_docker = true;
|
| 48 |
+
} else {
|
| 49 |
+
context.version = `branch-${this.sanitizeBranch(branch)}`;
|
| 50 |
+
context.release_type = 'branch';
|
| 51 |
+
context.platforms = ['linux/amd64'];
|
| 52 |
+
}
|
| 53 |
+
break;
|
| 54 |
+
|
| 55 |
+
case 'workflow_call':
|
| 56 |
+
case 'release':
|
| 57 |
+
if (!version) throw new Error('Version required for release');
|
| 58 |
+
context.version = version;
|
| 59 |
+
context.release_type = releaseType || 'stable';
|
| 60 |
+
context.push_to_docker = true;
|
| 61 |
+
break;
|
| 62 |
+
|
| 63 |
+
default:
|
| 64 |
+
throw new Error(`Unknown event: ${event}`);
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
// Handle push_enabled override
|
| 69 |
+
if (pushEnabled !== undefined) {
|
| 70 |
+
context.push_enabled = pushEnabled;
|
| 71 |
+
} else {
|
| 72 |
+
context.push_enabled = context.push_to_ghcr;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
return context;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
sanitizeBranch(branch) {
|
| 79 |
+
if (!branch) return 'unknown';
|
| 80 |
+
return branch
|
| 81 |
+
.toLowerCase()
|
| 82 |
+
.replace(/[^a-z0-9._-]/g, '-')
|
| 83 |
+
.replace(/^[.-]/, '')
|
| 84 |
+
.replace(/[.-]$/, '')
|
| 85 |
+
.substring(0, 128);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
buildMatrix(platforms) {
|
| 89 |
+
const runners = {
|
| 90 |
+
'linux/amd64': 'blacksmith-4vcpu-ubuntu-2204',
|
| 91 |
+
'linux/arm64': 'blacksmith-4vcpu-ubuntu-2204-arm',
|
| 92 |
+
};
|
| 93 |
+
|
| 94 |
+
const matrix = {
|
| 95 |
+
platform: [],
|
| 96 |
+
include: [],
|
| 97 |
+
};
|
| 98 |
+
|
| 99 |
+
for (const platform of platforms) {
|
| 100 |
+
const shortName = platform.split('/').pop(); // amd64 or arm64
|
| 101 |
+
matrix.platform.push(shortName);
|
| 102 |
+
matrix.include.push({
|
| 103 |
+
platform: shortName,
|
| 104 |
+
runner: runners[platform],
|
| 105 |
+
docker_platform: platform,
|
| 106 |
+
});
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
return matrix;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
output(context, matrix = null) {
|
| 113 |
+
const buildMatrix = matrix || this.buildMatrix(context.platforms);
|
| 114 |
+
|
| 115 |
+
if (this.githubOutput) {
|
| 116 |
+
const outputs = [
|
| 117 |
+
`version=${context.version}`,
|
| 118 |
+
`release_type=${context.release_type}`,
|
| 119 |
+
`platforms=${JSON.stringify(context.platforms)}`,
|
| 120 |
+
`push_to_ghcr=${context.push_to_ghcr}`,
|
| 121 |
+
`push_to_docker=${context.push_to_docker}`,
|
| 122 |
+
`push_enabled=${context.push_enabled}`,
|
| 123 |
+
`build_matrix=${JSON.stringify(buildMatrix)}`,
|
| 124 |
+
];
|
| 125 |
+
appendFileSync(this.githubOutput, outputs.join('\n') + '\n');
|
| 126 |
+
} else {
|
| 127 |
+
console.log(JSON.stringify({ ...context, build_matrix: buildMatrix }, null, 2));
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
// CLI - Simple argument parsing
|
| 133 |
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
| 134 |
+
const args = process.argv.slice(2);
|
| 135 |
+
const getArg = (name) => {
|
| 136 |
+
const index = args.indexOf(`--${name}`);
|
| 137 |
+
if (index === -1 || !args[index + 1]) return undefined;
|
| 138 |
+
const value = args[index + 1];
|
| 139 |
+
// Handle empty strings and 'null' as undefined
|
| 140 |
+
return value === '' || value === 'null' ? undefined : value;
|
| 141 |
+
};
|
| 142 |
+
|
| 143 |
+
try {
|
| 144 |
+
const context = new BuildContext();
|
| 145 |
+
const pushEnabledArg = getArg('push-enabled');
|
| 146 |
+
const result = context.determine({
|
| 147 |
+
event: getArg('event') || process.env.GITHUB_EVENT_NAME,
|
| 148 |
+
pr: getArg('pr') || process.env.GITHUB_PR_NUMBER,
|
| 149 |
+
branch: getArg('branch') || process.env.GITHUB_REF_NAME,
|
| 150 |
+
version: getArg('version'),
|
| 151 |
+
releaseType: getArg('release-type'),
|
| 152 |
+
pushEnabled:
|
| 153 |
+
pushEnabledArg === 'true' ? true : pushEnabledArg === 'false' ? false : undefined,
|
| 154 |
+
});
|
| 155 |
+
|
| 156 |
+
const matrix = context.buildMatrix(result.platforms);
|
| 157 |
+
|
| 158 |
+
// Debug output when GITHUB_OUTPUT is set (running in Actions)
|
| 159 |
+
if (context.githubOutput) {
|
| 160 |
+
console.log('=== Build Context ===');
|
| 161 |
+
console.log(`version: ${result.version}`);
|
| 162 |
+
console.log(`release_type: ${result.release_type}`);
|
| 163 |
+
console.log(`platforms: ${JSON.stringify(result.platforms, null, 2)}`);
|
| 164 |
+
console.log(`push_to_ghcr: ${result.push_to_ghcr}`);
|
| 165 |
+
console.log(`push_to_docker: ${result.push_to_docker}`);
|
| 166 |
+
console.log(`push_enabled: ${result.push_enabled}`);
|
| 167 |
+
console.log('build_matrix:', JSON.stringify(matrix, null, 2));
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
context.output(result, matrix);
|
| 171 |
+
} catch (error) {
|
| 172 |
+
console.error(`Error: ${error.message}`);
|
| 173 |
+
process.exit(1);
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
export default BuildContext;
|
.github/scripts/docker/docker-tags.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
|
| 3 |
+
import { appendFileSync } from 'node:fs';
|
| 4 |
+
|
| 5 |
+
class TagGenerator {
|
| 6 |
+
constructor() {
|
| 7 |
+
this.githubOwner = process.env.GITHUB_REPOSITORY_OWNER || 'n8n-io';
|
| 8 |
+
this.dockerUsername = process.env.DOCKER_USERNAME || 'n8nio';
|
| 9 |
+
this.githubOutput = process.env.GITHUB_OUTPUT || null;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
generate({ image, version, platform, includeDockerHub = false }) {
|
| 13 |
+
let imageName = image;
|
| 14 |
+
let versionSuffix = '';
|
| 15 |
+
|
| 16 |
+
if (image === 'runners-distroless') {
|
| 17 |
+
imageName = 'runners';
|
| 18 |
+
versionSuffix = '-distroless';
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
const platformSuffix = platform ? `-${platform.split('/').pop()}` : '';
|
| 22 |
+
const fullVersion = `${version}${versionSuffix}${platformSuffix}`;
|
| 23 |
+
|
| 24 |
+
const tags = {
|
| 25 |
+
ghcr: [`ghcr.io/${this.githubOwner}/${imageName}:${fullVersion}`],
|
| 26 |
+
docker: includeDockerHub ? [`${this.dockerUsername}/${imageName}:${fullVersion}`] : [],
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
tags.all = [...tags.ghcr, ...tags.docker];
|
| 30 |
+
return tags;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
output(tags, prefix = '') {
|
| 34 |
+
if (this.githubOutput) {
|
| 35 |
+
const prefixStr = prefix ? `${prefix}_` : '';
|
| 36 |
+
const primaryTag = tags.ghcr[0] ? tags.ghcr[0].replace(/-amd64$|-arm64$/, '') : '';
|
| 37 |
+
const outputs = [
|
| 38 |
+
`${prefixStr}tags=${tags.all.join(',')}`,
|
| 39 |
+
`${prefixStr}ghcr_tag=${tags.ghcr[0] || ''}`,
|
| 40 |
+
`${prefixStr}docker_tag=${tags.docker[0] || ''}`,
|
| 41 |
+
`${prefixStr}primary_tag=${primaryTag}`,
|
| 42 |
+
];
|
| 43 |
+
appendFileSync(this.githubOutput, outputs.join('\n') + '\n');
|
| 44 |
+
} else {
|
| 45 |
+
console.log(JSON.stringify(tags, null, 2));
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
generateAll({ version, platform, includeDockerHub = false }) {
|
| 50 |
+
const images = ['n8n', 'runners', 'runners-distroless'];
|
| 51 |
+
const results = {};
|
| 52 |
+
|
| 53 |
+
for (const image of images) {
|
| 54 |
+
const tags = this.generate({ image, version, platform, includeDockerHub });
|
| 55 |
+
const prefix = image.replace('-distroless', '_distroless');
|
| 56 |
+
results[prefix] = tags;
|
| 57 |
+
|
| 58 |
+
if (this.githubOutput) {
|
| 59 |
+
this.output(tags, prefix);
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
return results;
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
| 68 |
+
const args = process.argv.slice(2);
|
| 69 |
+
const getArg = (name) => {
|
| 70 |
+
const index = args.indexOf(`--${name}`);
|
| 71 |
+
return index !== -1 && args[index + 1] ? args[index + 1] : undefined;
|
| 72 |
+
};
|
| 73 |
+
const hasFlag = (name) => args.includes(`--${name}`);
|
| 74 |
+
|
| 75 |
+
try {
|
| 76 |
+
const generator = new TagGenerator();
|
| 77 |
+
const version = getArg('version');
|
| 78 |
+
|
| 79 |
+
if (!version) {
|
| 80 |
+
console.error('Error: --version is required');
|
| 81 |
+
process.exit(1);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
if (hasFlag('all')) {
|
| 85 |
+
const results = generator.generateAll({
|
| 86 |
+
version,
|
| 87 |
+
platform: getArg('platform'),
|
| 88 |
+
includeDockerHub: hasFlag('include-docker'),
|
| 89 |
+
});
|
| 90 |
+
if (!generator.githubOutput) {
|
| 91 |
+
console.log(JSON.stringify(results, null, 2));
|
| 92 |
+
}
|
| 93 |
+
} else {
|
| 94 |
+
const image = getArg('image');
|
| 95 |
+
if (!image) {
|
| 96 |
+
console.error('Error: Either --image or --all is required');
|
| 97 |
+
process.exit(1);
|
| 98 |
+
}
|
| 99 |
+
const tags = generator.generate({
|
| 100 |
+
image,
|
| 101 |
+
version,
|
| 102 |
+
platform: getArg('platform'),
|
| 103 |
+
includeDockerHub: hasFlag('include-docker'),
|
| 104 |
+
});
|
| 105 |
+
generator.output(tags);
|
| 106 |
+
}
|
| 107 |
+
} catch (error) {
|
| 108 |
+
console.error(`Error: ${error.message}`);
|
| 109 |
+
process.exit(1);
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
export default TagGenerator;
|
.github/scripts/ensure-provenance-fields.mjs
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { writeFile, readFile, copyFile } from 'fs/promises';
|
| 2 |
+
import { resolve, dirname } from 'path';
|
| 3 |
+
import child_process from 'child_process';
|
| 4 |
+
import { fileURLToPath } from 'url';
|
| 5 |
+
import { promisify } from 'util';
|
| 6 |
+
|
| 7 |
+
const exec = promisify(child_process.exec);
|
| 8 |
+
|
| 9 |
+
const commonFiles = ['LICENSE.md', 'LICENSE_EE.md'];
|
| 10 |
+
|
| 11 |
+
const baseDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
| 12 |
+
const packages = JSON.parse((await exec('pnpm ls -r --only-projects --json')).stdout);
|
| 13 |
+
|
| 14 |
+
for (let { name, path, version, private: isPrivate } of packages) {
|
| 15 |
+
if (isPrivate) continue;
|
| 16 |
+
|
| 17 |
+
const packageFile = resolve(path, 'package.json');
|
| 18 |
+
const packageJson = {
|
| 19 |
+
...JSON.parse(await readFile(packageFile, 'utf-8')),
|
| 20 |
+
// Add these fields to all published package.json files to ensure provenance checks pass
|
| 21 |
+
license: 'SEE LICENSE IN LICENSE.md',
|
| 22 |
+
homepage: 'https://n8n.io',
|
| 23 |
+
author: {
|
| 24 |
+
name: 'Jan Oberhauser',
|
| 25 |
+
email: 'jan@n8n.io',
|
| 26 |
+
},
|
| 27 |
+
repository: {
|
| 28 |
+
type: 'git',
|
| 29 |
+
url: 'git+https://github.com/n8n-io/n8n.git',
|
| 30 |
+
},
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
// Copy over LICENSE.md and LICENSE_EE.md into every published package, and ensure they get included in the published package
|
| 34 |
+
await Promise.all(
|
| 35 |
+
commonFiles.map(async (file) => {
|
| 36 |
+
await copyFile(resolve(baseDir, file), resolve(path, file));
|
| 37 |
+
if (packageJson.files && !packageJson.files.includes(file)) {
|
| 38 |
+
packageJson.files.push(file);
|
| 39 |
+
}
|
| 40 |
+
}),
|
| 41 |
+
);
|
| 42 |
+
|
| 43 |
+
await writeFile(packageFile, JSON.stringify(packageJson, null, 2) + '\n');
|
| 44 |
+
}
|
.github/scripts/package.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"dependencies": {
|
| 3 |
+
"cacheable-lookup": "6.1.0",
|
| 4 |
+
"conventional-changelog": "^4.0.0",
|
| 5 |
+
"debug": "4.3.4",
|
| 6 |
+
"glob": "10.5.0",
|
| 7 |
+
"p-limit": "3.1.0",
|
| 8 |
+
"picocolors": "1.0.1",
|
| 9 |
+
"semver": "7.5.4",
|
| 10 |
+
"tempfile": "5.0.0"
|
| 11 |
+
}
|
| 12 |
+
}
|
.github/scripts/trim-fe-packageJson.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const { writeFileSync } = require('fs');
|
| 2 |
+
const { resolve } = require('path');
|
| 3 |
+
const baseDir = resolve(__dirname, '../..');
|
| 4 |
+
|
| 5 |
+
const trimPackageJson = (packageName) => {
|
| 6 |
+
const filePath = resolve(baseDir, 'packages', packageName, 'package.json');
|
| 7 |
+
const { scripts, peerDependencies, devDependencies, dependencies, ...packageJson } = require(
|
| 8 |
+
filePath,
|
| 9 |
+
);
|
| 10 |
+
if (packageName === 'frontend/@n8n/chat') {
|
| 11 |
+
packageJson.dependencies = dependencies;
|
| 12 |
+
}
|
| 13 |
+
writeFileSync(filePath, JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
trimPackageJson('frontend/@n8n/chat');
|
| 17 |
+
trimPackageJson('frontend/@n8n/design-system');
|
| 18 |
+
trimPackageJson('frontend/editor-ui');
|
.github/scripts/update-changelog.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import createTempFile from 'tempfile';
|
| 2 |
+
import conventionalChangelog from 'conventional-changelog';
|
| 3 |
+
import { resolve } from 'path';
|
| 4 |
+
import { createReadStream, createWriteStream } from 'fs';
|
| 5 |
+
import { dirname } from 'path';
|
| 6 |
+
import { fileURLToPath } from 'url';
|
| 7 |
+
import { pipeline } from 'stream/promises';
|
| 8 |
+
import packageJson from '../../package.json' with { type: 'json' };
|
| 9 |
+
|
| 10 |
+
const baseDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
| 11 |
+
const fullChangelogFile = resolve(baseDir, 'CHANGELOG.md');
|
| 12 |
+
// Version includes experimental versions (e.g., 1.2.3-exp.0)
|
| 13 |
+
const versionChangelogFile = resolve(baseDir, `CHANGELOG-${packageJson.version}.md`);
|
| 14 |
+
|
| 15 |
+
const changelogStream = conventionalChangelog({
|
| 16 |
+
preset: 'angular',
|
| 17 |
+
releaseCount: 1,
|
| 18 |
+
tagPrefix: 'n8n@',
|
| 19 |
+
transform: (commit, callback) => {
|
| 20 |
+
const hasNoChangelogInHeader = commit.header.includes('(no-changelog)');
|
| 21 |
+
const isBenchmarkScope = commit.scope === 'benchmark';
|
| 22 |
+
|
| 23 |
+
// Ignore commits that have 'benchmark' scope or '(no-changelog)' in the header
|
| 24 |
+
callback(null, hasNoChangelogInHeader || isBenchmarkScope ? undefined : commit);
|
| 25 |
+
},
|
| 26 |
+
}).on('error', (err) => {
|
| 27 |
+
console.error(err.stack);
|
| 28 |
+
process.exit(1);
|
| 29 |
+
});
|
| 30 |
+
|
| 31 |
+
// Write the new changelog to a new temporary file, so that the contents can be used in the PR description
|
| 32 |
+
await pipeline(changelogStream, createWriteStream(versionChangelogFile));
|
| 33 |
+
|
| 34 |
+
// Since we can't read and write from the same file at the same time,
|
| 35 |
+
// we use a temporary file to output the updated changelog to.
|
| 36 |
+
const tmpFile = createTempFile();
|
| 37 |
+
const tmpStream = createWriteStream(tmpFile);
|
| 38 |
+
await pipeline(createReadStream(versionChangelogFile), tmpStream, { end: false });
|
| 39 |
+
await pipeline(createReadStream(fullChangelogFile), tmpStream);
|
| 40 |
+
await pipeline(createReadStream(tmpFile), createWriteStream(fullChangelogFile));
|
.github/scripts/validate-docs-links.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env node
|
| 2 |
+
|
| 3 |
+
const packages = ['nodes-base', '@n8n/nodes-langchain'];
|
| 4 |
+
const concurrency = 20;
|
| 5 |
+
let exitCode = 0;
|
| 6 |
+
|
| 7 |
+
const debug = require('debug')('n8n');
|
| 8 |
+
const path = require('path');
|
| 9 |
+
const https = require('https');
|
| 10 |
+
const glob = require('glob');
|
| 11 |
+
const pLimit = require('p-limit');
|
| 12 |
+
const picocolors = require('picocolors');
|
| 13 |
+
const Lookup = require('cacheable-lookup').default;
|
| 14 |
+
|
| 15 |
+
const agent = new https.Agent({ keepAlive: true, keepAliveMsecs: 5000 });
|
| 16 |
+
new Lookup().install(agent);
|
| 17 |
+
const limiter = pLimit(concurrency);
|
| 18 |
+
|
| 19 |
+
const validateUrl = async (packageName, kind, type) =>
|
| 20 |
+
new Promise((resolve, reject) => {
|
| 21 |
+
const name = type.displayName;
|
| 22 |
+
const documentationUrl =
|
| 23 |
+
kind === 'credentials'
|
| 24 |
+
? type.documentationUrl
|
| 25 |
+
: type.codex?.resources?.primaryDocumentation?.[0]?.url;
|
| 26 |
+
if (!documentationUrl) resolve([name, null]);
|
| 27 |
+
|
| 28 |
+
const url = new URL(
|
| 29 |
+
/^https?:\/\//.test(documentationUrl)
|
| 30 |
+
? documentationUrl
|
| 31 |
+
: `https://docs.n8n.io/integrations/builtin/${kind}/${documentationUrl.toLowerCase()}/`,
|
| 32 |
+
);
|
| 33 |
+
https
|
| 34 |
+
.request(
|
| 35 |
+
{
|
| 36 |
+
hostname: url.hostname,
|
| 37 |
+
port: 443,
|
| 38 |
+
path: url.pathname,
|
| 39 |
+
method: 'HEAD',
|
| 40 |
+
agent,
|
| 41 |
+
},
|
| 42 |
+
(res) => {
|
| 43 |
+
debug(picocolors.green('✓'), packageName, kind, name);
|
| 44 |
+
resolve([name, res.statusCode]);
|
| 45 |
+
},
|
| 46 |
+
)
|
| 47 |
+
.on('error', (e) => {
|
| 48 |
+
debug(picocolors.red('✘'), packageName, kind, name);
|
| 49 |
+
reject(e);
|
| 50 |
+
})
|
| 51 |
+
.end();
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
const checkLinks = async (packageName, kind) => {
|
| 55 |
+
const baseDir = path.resolve(__dirname, '../../packages', packageName);
|
| 56 |
+
let types = require(path.join(baseDir, `dist/types/${kind}.json`));
|
| 57 |
+
if (kind === 'nodes')
|
| 58 |
+
types = types.filter(
|
| 59 |
+
({ codex, hidden }) => !!codex?.resources?.primaryDocumentation && !hidden,
|
| 60 |
+
);
|
| 61 |
+
debug(packageName, kind, types.length);
|
| 62 |
+
|
| 63 |
+
const statuses = await Promise.all(
|
| 64 |
+
types.map((type) =>
|
| 65 |
+
limiter(() => {
|
| 66 |
+
return validateUrl(packageName, kind, type);
|
| 67 |
+
}),
|
| 68 |
+
),
|
| 69 |
+
);
|
| 70 |
+
|
| 71 |
+
const missingDocs = [];
|
| 72 |
+
const invalidUrls = [];
|
| 73 |
+
for (const [name, statusCode] of statuses) {
|
| 74 |
+
if (statusCode === null) missingDocs.push(name);
|
| 75 |
+
if (statusCode !== 200) invalidUrls.push(name);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
if (missingDocs.length)
|
| 79 |
+
console.log('Documentation URL missing in %s for %s', packageName, kind, missingDocs);
|
| 80 |
+
if (invalidUrls.length)
|
| 81 |
+
console.log('Documentation URL invalid in %s for %s', packageName, kind, invalidUrls);
|
| 82 |
+
if (missingDocs.length || invalidUrls.length) exitCode = 1;
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
(async () => {
|
| 86 |
+
for (const packageName of packages) {
|
| 87 |
+
await Promise.all([checkLinks(packageName, 'credentials'), checkLinks(packageName, 'nodes')]);
|
| 88 |
+
if (exitCode !== 0) process.exit(exitCode);
|
| 89 |
+
}
|
| 90 |
+
})();
|
.github/test-metrics/playwright.json
ADDED
|
@@ -0,0 +1,672 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"updatedAt": "2025-12-23T17:51:26.730Z",
|
| 3 |
+
"source": "currents",
|
| 4 |
+
"projectId": "I0yzoc",
|
| 5 |
+
"specs": {
|
| 6 |
+
"tests/e2e/workflows/list/workflows.spec.ts": {
|
| 7 |
+
"avgDuration": 113599,
|
| 8 |
+
"testCount": 9,
|
| 9 |
+
"flakyRate": 0.0066
|
| 10 |
+
},
|
| 11 |
+
"tests/e2e/workflows/templates/templates.spec.ts": {
|
| 12 |
+
"avgDuration": 19603,
|
| 13 |
+
"testCount": 9,
|
| 14 |
+
"flakyRate": 0.0016
|
| 15 |
+
},
|
| 16 |
+
"tests/e2e/workflows/editor/tags.spec.ts": {
|
| 17 |
+
"avgDuration": 38592,
|
| 18 |
+
"testCount": 7,
|
| 19 |
+
"flakyRate": 0.0014
|
| 20 |
+
},
|
| 21 |
+
"tests/e2e/chat-hub/chat-hub-workflow-agent.spec.ts": {
|
| 22 |
+
"avgDuration": 45555,
|
| 23 |
+
"testCount": 2,
|
| 24 |
+
"flakyRate": 0.0814
|
| 25 |
+
},
|
| 26 |
+
"tests/e2e/workflows/editor/workflow-actions/settings.spec.ts": {
|
| 27 |
+
"avgDuration": 60000,
|
| 28 |
+
"testCount": 1,
|
| 29 |
+
"flakyRate": 0
|
| 30 |
+
},
|
| 31 |
+
"tests/e2e/workflows/editor/subworkflows/workflow-selector.spec.ts": {
|
| 32 |
+
"avgDuration": 28137,
|
| 33 |
+
"testCount": 5,
|
| 34 |
+
"flakyRate": 0.0012
|
| 35 |
+
},
|
| 36 |
+
"tests/e2e/workflows/editor/workflow-actions/save.spec.ts": {
|
| 37 |
+
"avgDuration": 60000,
|
| 38 |
+
"testCount": 1,
|
| 39 |
+
"flakyRate": 0
|
| 40 |
+
},
|
| 41 |
+
"tests/e2e/workflows/editor/workflow-actions/run.spec.ts": {
|
| 42 |
+
"avgDuration": 60000,
|
| 43 |
+
"testCount": 1,
|
| 44 |
+
"flakyRate": 0
|
| 45 |
+
},
|
| 46 |
+
"tests/e2e/workflows/editor/workflow-actions/publish.spec.ts": {
|
| 47 |
+
"avgDuration": 22021,
|
| 48 |
+
"testCount": 8,
|
| 49 |
+
"flakyRate": 0
|
| 50 |
+
},
|
| 51 |
+
"tests/e2e/workflows/checklist/production-checklist.spec.ts": {
|
| 52 |
+
"avgDuration": 27187,
|
| 53 |
+
"testCount": 7,
|
| 54 |
+
"flakyRate": 0.0013
|
| 55 |
+
},
|
| 56 |
+
"tests/e2e/workflows/executions/list.spec.ts": {
|
| 57 |
+
"avgDuration": 61008,
|
| 58 |
+
"testCount": 11,
|
| 59 |
+
"flakyRate": 0.0042
|
| 60 |
+
},
|
| 61 |
+
"tests/e2e/workflows/editor/workflow-actions/duplicate.spec.ts": {
|
| 62 |
+
"avgDuration": 60000,
|
| 63 |
+
"testCount": 1,
|
| 64 |
+
"flakyRate": 0
|
| 65 |
+
},
|
| 66 |
+
"tests/e2e/workflows/editor/workflow-actions/copy-paste.spec.ts": {
|
| 67 |
+
"avgDuration": 60000,
|
| 68 |
+
"testCount": 1,
|
| 69 |
+
"flakyRate": 0
|
| 70 |
+
},
|
| 71 |
+
"tests/e2e/ai/workflow-builder.spec.ts": {
|
| 72 |
+
"avgDuration": 32211,
|
| 73 |
+
"testCount": 5,
|
| 74 |
+
"flakyRate": 0.0096
|
| 75 |
+
},
|
| 76 |
+
"tests/e2e/workflows/editor/workflow-actions/archive.spec.ts": {
|
| 77 |
+
"avgDuration": 60000,
|
| 78 |
+
"testCount": 1,
|
| 79 |
+
"flakyRate": 0
|
| 80 |
+
},
|
| 81 |
+
"tests/e2e/settings/workers/workers.spec.ts": {
|
| 82 |
+
"avgDuration": 5662,
|
| 83 |
+
"testCount": 4,
|
| 84 |
+
"flakyRate": 0.0011
|
| 85 |
+
},
|
| 86 |
+
"tests/e2e/nodes/webhook.spec.ts": {
|
| 87 |
+
"avgDuration": 55672,
|
| 88 |
+
"testCount": 9,
|
| 89 |
+
"flakyRate": 0.0021
|
| 90 |
+
},
|
| 91 |
+
"tests/e2e/api/webhook-isolation.spec.ts": {
|
| 92 |
+
"avgDuration": 5058,
|
| 93 |
+
"testCount": 15,
|
| 94 |
+
"flakyRate": 0.0005
|
| 95 |
+
},
|
| 96 |
+
"tests/e2e/app-config/versions.spec.ts": {
|
| 97 |
+
"avgDuration": 5875,
|
| 98 |
+
"testCount": 2,
|
| 99 |
+
"flakyRate": 0
|
| 100 |
+
},
|
| 101 |
+
"tests/e2e/settings/environments/variables.spec.ts": {
|
| 102 |
+
"avgDuration": 15854,
|
| 103 |
+
"testCount": 7,
|
| 104 |
+
"flakyRate": 0.0002
|
| 105 |
+
},
|
| 106 |
+
"tests/e2e/settings/users/users.spec.ts": {
|
| 107 |
+
"avgDuration": 11978,
|
| 108 |
+
"testCount": 4,
|
| 109 |
+
"flakyRate": 0.015
|
| 110 |
+
},
|
| 111 |
+
"tests/e2e/building-blocks/user-service.spec.ts": {
|
| 112 |
+
"avgDuration": 6400,
|
| 113 |
+
"testCount": 4,
|
| 114 |
+
"flakyRate": 0.0005
|
| 115 |
+
},
|
| 116 |
+
"tests/e2e/workflows/editor/canvas/undo-redo.spec.ts": {
|
| 117 |
+
"avgDuration": 65670,
|
| 118 |
+
"testCount": 13,
|
| 119 |
+
"flakyRate": 0.0193
|
| 120 |
+
},
|
| 121 |
+
"tests/e2e/building-blocks/workflow-entry-points.spec.ts": {
|
| 122 |
+
"avgDuration": 13573,
|
| 123 |
+
"testCount": 5,
|
| 124 |
+
"flakyRate": 0.0006
|
| 125 |
+
},
|
| 126 |
+
"tests/e2e/chat-hub/chat-hub-tools.spec.ts": {
|
| 127 |
+
"avgDuration": 11472,
|
| 128 |
+
"testCount": 1,
|
| 129 |
+
"flakyRate": 0.0233
|
| 130 |
+
},
|
| 131 |
+
"tests/e2e/capabilities/task-runner.spec.ts": {
|
| 132 |
+
"avgDuration": 52322,
|
| 133 |
+
"testCount": 3,
|
| 134 |
+
"flakyRate": 0.1299
|
| 135 |
+
},
|
| 136 |
+
"tests/e2e/workflows/editor/subworkflows/debugging.spec.ts": {
|
| 137 |
+
"avgDuration": 9686,
|
| 138 |
+
"testCount": 3,
|
| 139 |
+
"flakyRate": 0.0007
|
| 140 |
+
},
|
| 141 |
+
"tests/e2e/workflows/editor/subworkflows/extraction.spec.ts": {
|
| 142 |
+
"avgDuration": 35747,
|
| 143 |
+
"testCount": 2,
|
| 144 |
+
"flakyRate": 0.0011
|
| 145 |
+
},
|
| 146 |
+
"tests/e2e/settings/environments/source-control.spec.ts": {
|
| 147 |
+
"avgDuration": 142866,
|
| 148 |
+
"testCount": 5,
|
| 149 |
+
"flakyRate": 0.1412
|
| 150 |
+
},
|
| 151 |
+
"tests/e2e/auth/signin.spec.ts": {
|
| 152 |
+
"avgDuration": 90584,
|
| 153 |
+
"testCount": 3,
|
| 154 |
+
"flakyRate": 0
|
| 155 |
+
},
|
| 156 |
+
"tests/e2e/chat-hub/chat-hub-settings.spec.ts": {
|
| 157 |
+
"avgDuration": 29402,
|
| 158 |
+
"testCount": 2,
|
| 159 |
+
"flakyRate": 0.0674
|
| 160 |
+
},
|
| 161 |
+
"tests/e2e/app-config/security-notifications.spec.ts": {
|
| 162 |
+
"avgDuration": 14479,
|
| 163 |
+
"testCount": 5,
|
| 164 |
+
"flakyRate": 0.0004
|
| 165 |
+
},
|
| 166 |
+
"tests/e2e/workflows/editor/ndv/schema-preview.spec.ts": {
|
| 167 |
+
"avgDuration": 5002,
|
| 168 |
+
"testCount": 1,
|
| 169 |
+
"flakyRate": 0.0021
|
| 170 |
+
},
|
| 171 |
+
"tests/e2e/nodes/schedule-trigger-node.spec.ts": {
|
| 172 |
+
"avgDuration": 3494,
|
| 173 |
+
"testCount": 1,
|
| 174 |
+
"flakyRate": 0.0007
|
| 175 |
+
},
|
| 176 |
+
"tests/e2e/regression/SUG-38-inline-expression-preview.spec.ts": {
|
| 177 |
+
"avgDuration": 4768,
|
| 178 |
+
"testCount": 1,
|
| 179 |
+
"flakyRate": 0.0007
|
| 180 |
+
},
|
| 181 |
+
"tests/e2e/regression/SUG-121-fields-reset-after-closing-ndv.spec.ts": {
|
| 182 |
+
"avgDuration": 4232,
|
| 183 |
+
"testCount": 1,
|
| 184 |
+
"flakyRate": 0.0007
|
| 185 |
+
},
|
| 186 |
+
"tests/e2e/workflows/editor/routing.spec.ts": {
|
| 187 |
+
"avgDuration": 28147,
|
| 188 |
+
"testCount": 6,
|
| 189 |
+
"flakyRate": 0.0042
|
| 190 |
+
},
|
| 191 |
+
"tests/e2e/workflows/editor/ndv/resource-mapper.spec.ts": {
|
| 192 |
+
"avgDuration": 17808,
|
| 193 |
+
"testCount": 4,
|
| 194 |
+
"flakyRate": 0.0005
|
| 195 |
+
},
|
| 196 |
+
"tests/e2e/workflows/editor/ndv/resource-locator.spec.ts": {
|
| 197 |
+
"avgDuration": 36783,
|
| 198 |
+
"testCount": 7,
|
| 199 |
+
"flakyRate": 0.0007
|
| 200 |
+
},
|
| 201 |
+
"tests/e2e/ai/rag-callout.spec.ts": {
|
| 202 |
+
"avgDuration": 7145,
|
| 203 |
+
"testCount": 2,
|
| 204 |
+
"flakyRate": 0
|
| 205 |
+
},
|
| 206 |
+
"tests/e2e/source-control/push.spec.ts": {
|
| 207 |
+
"avgDuration": 161130,
|
| 208 |
+
"testCount": 4,
|
| 209 |
+
"flakyRate": 0.4348
|
| 210 |
+
},
|
| 211 |
+
"tests/e2e/source-control/pull.spec.ts": {
|
| 212 |
+
"avgDuration": 63320,
|
| 213 |
+
"testCount": 2,
|
| 214 |
+
"flakyRate": 0.2028
|
| 215 |
+
},
|
| 216 |
+
"tests/e2e/capabilities/proxy-server.spec.ts": {
|
| 217 |
+
"avgDuration": 27599,
|
| 218 |
+
"testCount": 4,
|
| 219 |
+
"flakyRate": 0.0028
|
| 220 |
+
},
|
| 221 |
+
"tests/e2e/projects/project-settings.spec.ts": {
|
| 222 |
+
"avgDuration": 48766,
|
| 223 |
+
"testCount": 8,
|
| 224 |
+
"flakyRate": 0.0005
|
| 225 |
+
},
|
| 226 |
+
"tests/e2e/chat-hub/chat-hub-personal-agent.spec.ts": {
|
| 227 |
+
"avgDuration": 114815,
|
| 228 |
+
"testCount": 4,
|
| 229 |
+
"flakyRate": 0.0448
|
| 230 |
+
},
|
| 231 |
+
"tests/e2e/settings/personal/personal.spec.ts": {
|
| 232 |
+
"avgDuration": 40996,
|
| 233 |
+
"testCount": 9,
|
| 234 |
+
"flakyRate": 0.0003
|
| 235 |
+
},
|
| 236 |
+
"tests/e2e/auth/password-reset.spec.ts": {
|
| 237 |
+
"avgDuration": 18625,
|
| 238 |
+
"testCount": 1,
|
| 239 |
+
"flakyRate": 0.0007
|
| 240 |
+
},
|
| 241 |
+
"tests/e2e/workflows/editor/subworkflows/wait.spec.ts": {
|
| 242 |
+
"avgDuration": 40569,
|
| 243 |
+
"testCount": 4,
|
| 244 |
+
"flakyRate": 0.2677
|
| 245 |
+
},
|
| 246 |
+
"tests/e2e/nodes/pdf-node.spec.ts": {
|
| 247 |
+
"avgDuration": 5500,
|
| 248 |
+
"testCount": 1,
|
| 249 |
+
"flakyRate": 0.003
|
| 250 |
+
},
|
| 251 |
+
"tests/e2e/auth/oidc.spec.ts": {
|
| 252 |
+
"avgDuration": 46170,
|
| 253 |
+
"testCount": 1,
|
| 254 |
+
"flakyRate": 0.0033
|
| 255 |
+
},
|
| 256 |
+
"tests/e2e/credentials/oauth.spec.ts": {
|
| 257 |
+
"avgDuration": 4270,
|
| 258 |
+
"testCount": 1,
|
| 259 |
+
"flakyRate": 0.0007
|
| 260 |
+
},
|
| 261 |
+
"tests/e2e/workflows/editor/ndv/io-filter.spec.ts": {
|
| 262 |
+
"avgDuration": 12720,
|
| 263 |
+
"testCount": 2,
|
| 264 |
+
"flakyRate": 0.0037
|
| 265 |
+
},
|
| 266 |
+
"tests/e2e/building-blocks/node-details-configuration.spec.ts": {
|
| 267 |
+
"avgDuration": 33860,
|
| 268 |
+
"testCount": 7,
|
| 269 |
+
"flakyRate": 0.0003
|
| 270 |
+
},
|
| 271 |
+
"tests/e2e/node-creator/workflows.spec.ts": {
|
| 272 |
+
"avgDuration": 6428,
|
| 273 |
+
"testCount": 2,
|
| 274 |
+
"flakyRate": 0
|
| 275 |
+
},
|
| 276 |
+
"tests/e2e/node-creator/vector-stores.spec.ts": {
|
| 277 |
+
"avgDuration": 9088,
|
| 278 |
+
"testCount": 3,
|
| 279 |
+
"flakyRate": 0.001
|
| 280 |
+
},
|
| 281 |
+
"tests/e2e/node-creator/special-nodes.spec.ts": {
|
| 282 |
+
"avgDuration": 13503,
|
| 283 |
+
"testCount": 3,
|
| 284 |
+
"flakyRate": 0.0012
|
| 285 |
+
},
|
| 286 |
+
"tests/e2e/node-creator/navigation.spec.ts": {
|
| 287 |
+
"avgDuration": 15911,
|
| 288 |
+
"testCount": 4,
|
| 289 |
+
"flakyRate": 0.0002
|
| 290 |
+
},
|
| 291 |
+
"tests/e2e/node-creator/categories.spec.ts": {
|
| 292 |
+
"avgDuration": 17552,
|
| 293 |
+
"testCount": 5,
|
| 294 |
+
"flakyRate": 0.0027
|
| 295 |
+
},
|
| 296 |
+
"tests/e2e/node-creator/actions.spec.ts": {
|
| 297 |
+
"avgDuration": 12991,
|
| 298 |
+
"testCount": 4,
|
| 299 |
+
"flakyRate": 0.0004
|
| 300 |
+
},
|
| 301 |
+
"tests/e2e/app-config/nps-survey.spec.ts": {
|
| 302 |
+
"avgDuration": 37040,
|
| 303 |
+
"testCount": 2,
|
| 304 |
+
"flakyRate": 0.0025
|
| 305 |
+
},
|
| 306 |
+
"tests/e2e/workflows/editor/ndv/ndv-parameters.spec.ts": {
|
| 307 |
+
"avgDuration": 48876,
|
| 308 |
+
"testCount": 9,
|
| 309 |
+
"flakyRate": 0.0012
|
| 310 |
+
},
|
| 311 |
+
"tests/e2e/workflows/editor/ndv/paired-item.spec.ts": {
|
| 312 |
+
"avgDuration": 30863,
|
| 313 |
+
"testCount": 5,
|
| 314 |
+
"flakyRate": 0.0006
|
| 315 |
+
},
|
| 316 |
+
"tests/e2e/workflows/editor/ndv/ndv-floating-nodes.spec.ts": {
|
| 317 |
+
"avgDuration": 25025,
|
| 318 |
+
"testCount": 4,
|
| 319 |
+
"flakyRate": 0.0042
|
| 320 |
+
},
|
| 321 |
+
"tests/e2e/workflows/editor/ndv/ndv-data-display.spec.ts": {
|
| 322 |
+
"avgDuration": 63163,
|
| 323 |
+
"testCount": 10,
|
| 324 |
+
"flakyRate": 0.0047
|
| 325 |
+
},
|
| 326 |
+
"tests/e2e/workflows/editor/ndv/ndv-core.spec.ts": {
|
| 327 |
+
"avgDuration": 59650,
|
| 328 |
+
"testCount": 16,
|
| 329 |
+
"flakyRate": 0.0003
|
| 330 |
+
},
|
| 331 |
+
"tests/e2e/workflows/editor/execution/partial.spec.ts": {
|
| 332 |
+
"avgDuration": 8298,
|
| 333 |
+
"testCount": 2,
|
| 334 |
+
"flakyRate": 0.0004
|
| 335 |
+
},
|
| 336 |
+
"tests/e2e/workflows/editor/execution/logs.spec.ts": {
|
| 337 |
+
"avgDuration": 39950,
|
| 338 |
+
"testCount": 9,
|
| 339 |
+
"flakyRate": 0.0002
|
| 340 |
+
},
|
| 341 |
+
"tests/e2e/settings/log-streaming/log-streaming-observability.spec.ts": {
|
| 342 |
+
"avgDuration": 41907,
|
| 343 |
+
"testCount": 2,
|
| 344 |
+
"flakyRate": 0.0019
|
| 345 |
+
},
|
| 346 |
+
"tests/e2e/settings/log-streaming/log-streaming-ui-e2e.spec.ts": {
|
| 347 |
+
"avgDuration": 30182,
|
| 348 |
+
"testCount": 1,
|
| 349 |
+
"flakyRate": 0.0039
|
| 350 |
+
},
|
| 351 |
+
"tests/e2e/settings/log-streaming/log-streaming.spec.ts": {
|
| 352 |
+
"avgDuration": 14143,
|
| 353 |
+
"testCount": 5,
|
| 354 |
+
"flakyRate": 0.0003
|
| 355 |
+
},
|
| 356 |
+
"tests/e2e/ai/langchain-agents.spec.ts": {
|
| 357 |
+
"avgDuration": 69954,
|
| 358 |
+
"testCount": 7,
|
| 359 |
+
"flakyRate": 0.0017
|
| 360 |
+
},
|
| 361 |
+
"tests/e2e/ai/langchain-tools.spec.ts": {
|
| 362 |
+
"avgDuration": 60000,
|
| 363 |
+
"testCount": 1,
|
| 364 |
+
"flakyRate": 0
|
| 365 |
+
},
|
| 366 |
+
"tests/e2e/ai/langchain-memory.spec.ts": {
|
| 367 |
+
"avgDuration": 60000,
|
| 368 |
+
"testCount": 1,
|
| 369 |
+
"flakyRate": 0
|
| 370 |
+
},
|
| 371 |
+
"tests/e2e/ai/langchain-chains.spec.ts": {
|
| 372 |
+
"avgDuration": 41803,
|
| 373 |
+
"testCount": 4,
|
| 374 |
+
"flakyRate": 0.0014
|
| 375 |
+
},
|
| 376 |
+
"tests/e2e/ai/langchain-vectorstores.spec.ts": {
|
| 377 |
+
"avgDuration": 36841,
|
| 378 |
+
"testCount": 2,
|
| 379 |
+
"flakyRate": 0.0153
|
| 380 |
+
},
|
| 381 |
+
"tests/e2e/workflows/editor/expressions/inline.spec.ts": {
|
| 382 |
+
"avgDuration": 36109,
|
| 383 |
+
"testCount": 8,
|
| 384 |
+
"flakyRate": 0.0003
|
| 385 |
+
},
|
| 386 |
+
"tests/e2e/workflows/editor/execution/inject-previous.spec.ts": {
|
| 387 |
+
"avgDuration": 12687,
|
| 388 |
+
"testCount": 2,
|
| 389 |
+
"flakyRate": 0
|
| 390 |
+
},
|
| 391 |
+
"tests/e2e/workflows/list/import.spec.ts": {
|
| 392 |
+
"avgDuration": 15225,
|
| 393 |
+
"testCount": 5,
|
| 394 |
+
"flakyRate": 0.0003
|
| 395 |
+
},
|
| 396 |
+
"tests/e2e/nodes/if-node.spec.ts": {
|
| 397 |
+
"avgDuration": 7492,
|
| 398 |
+
"testCount": 2,
|
| 399 |
+
"flakyRate": 0.0004
|
| 400 |
+
},
|
| 401 |
+
"tests/e2e/nodes/http-request-node.spec.ts": {
|
| 402 |
+
"avgDuration": 9072,
|
| 403 |
+
"testCount": 2,
|
| 404 |
+
"flakyRate": 0.0011
|
| 405 |
+
},
|
| 406 |
+
"tests/e2e/regression/GHC-5776-ai-sessions-metadata-license-error.spec.ts": {
|
| 407 |
+
"avgDuration": 2602,
|
| 408 |
+
"testCount": 1,
|
| 409 |
+
"flakyRate": 0.0007
|
| 410 |
+
},
|
| 411 |
+
"tests/e2e/nodes/form-trigger-node.spec.ts": {
|
| 412 |
+
"avgDuration": 27007,
|
| 413 |
+
"testCount": 3,
|
| 414 |
+
"flakyRate": 0.0004
|
| 415 |
+
},
|
| 416 |
+
"tests/e2e/projects/folders-operations.spec.ts": {
|
| 417 |
+
"avgDuration": 46752,
|
| 418 |
+
"testCount": 13,
|
| 419 |
+
"flakyRate": 0.0006
|
| 420 |
+
},
|
| 421 |
+
"tests/e2e/projects/folders-basic.spec.ts": {
|
| 422 |
+
"avgDuration": 28139,
|
| 423 |
+
"testCount": 11,
|
| 424 |
+
"flakyRate": 0.0005
|
| 425 |
+
},
|
| 426 |
+
"tests/e2e/projects/folders-advanced.spec.ts": {
|
| 427 |
+
"avgDuration": 20134,
|
| 428 |
+
"testCount": 5,
|
| 429 |
+
"flakyRate": 0.0004
|
| 430 |
+
},
|
| 431 |
+
"tests/e2e/workflows/editor/canvas/focus-panel.spec.ts": {
|
| 432 |
+
"avgDuration": 4056,
|
| 433 |
+
"testCount": 1,
|
| 434 |
+
"flakyRate": 0.0007
|
| 435 |
+
},
|
| 436 |
+
"tests/e2e/chat-hub/chat-hub-attachment.spec.ts": {
|
| 437 |
+
"avgDuration": 44171,
|
| 438 |
+
"testCount": 3,
|
| 439 |
+
"flakyRate": 0.0147
|
| 440 |
+
},
|
| 441 |
+
"tests/e2e/api/webhook-external.spec.ts": {
|
| 442 |
+
"avgDuration": 9425,
|
| 443 |
+
"testCount": 2,
|
| 444 |
+
"flakyRate": 0.0594
|
| 445 |
+
},
|
| 446 |
+
"tests/e2e/workflows/editor/expressions/modal.spec.ts": {
|
| 447 |
+
"avgDuration": 41609,
|
| 448 |
+
"testCount": 6,
|
| 449 |
+
"flakyRate": 0.0006
|
| 450 |
+
},
|
| 451 |
+
"tests/e2e/workflows/editor/execution/execution.spec.ts": {
|
| 452 |
+
"avgDuration": 47322,
|
| 453 |
+
"testCount": 14,
|
| 454 |
+
"flakyRate": 0.0001
|
| 455 |
+
},
|
| 456 |
+
"tests/e2e/workflows/editor/execution/previous-nodes.spec.ts": {
|
| 457 |
+
"avgDuration": 60000,
|
| 458 |
+
"testCount": 1,
|
| 459 |
+
"flakyRate": 0
|
| 460 |
+
},
|
| 461 |
+
"tests/e2e/ai/evaluations.spec.ts": {
|
| 462 |
+
"avgDuration": 60000,
|
| 463 |
+
"testCount": 1,
|
| 464 |
+
"flakyRate": 0
|
| 465 |
+
},
|
| 466 |
+
"tests/e2e/app-config/env-feature-flags.spec.ts": {
|
| 467 |
+
"avgDuration": 1359,
|
| 468 |
+
"testCount": 2,
|
| 469 |
+
"flakyRate": 0
|
| 470 |
+
},
|
| 471 |
+
"tests/e2e/nodes/email-send-node.spec.ts": {
|
| 472 |
+
"avgDuration": 22418,
|
| 473 |
+
"testCount": 1,
|
| 474 |
+
"flakyRate": 0.0054
|
| 475 |
+
},
|
| 476 |
+
"tests/e2e/workflows/editor/code/editors.spec.ts": {
|
| 477 |
+
"avgDuration": 49237,
|
| 478 |
+
"testCount": 11,
|
| 479 |
+
"flakyRate": 0.0003
|
| 480 |
+
},
|
| 481 |
+
"tests/e2e/workflows/editor/editor-after-route-changes.spec.ts": {
|
| 482 |
+
"avgDuration": 16091,
|
| 483 |
+
"testCount": 1,
|
| 484 |
+
"flakyRate": 0.0211
|
| 485 |
+
},
|
| 486 |
+
"tests/e2e/app-config/demo.spec.ts": {
|
| 487 |
+
"avgDuration": 14718,
|
| 488 |
+
"testCount": 4,
|
| 489 |
+
"flakyRate": 0.0176
|
| 490 |
+
},
|
| 491 |
+
"tests/e2e/workflows/editor/execution/debug.spec.ts": {
|
| 492 |
+
"avgDuration": 34255,
|
| 493 |
+
"testCount": 3,
|
| 494 |
+
"flakyRate": 0.0037
|
| 495 |
+
},
|
| 496 |
+
"tests/e2e/workflows/editor/expressions/transformation.spec.ts": {
|
| 497 |
+
"avgDuration": 30134,
|
| 498 |
+
"testCount": 6,
|
| 499 |
+
"flakyRate": 0.0001
|
| 500 |
+
},
|
| 501 |
+
"tests/e2e/workflows/editor/ndv/pinning.spec.ts": {
|
| 502 |
+
"avgDuration": 36652,
|
| 503 |
+
"testCount": 10,
|
| 504 |
+
"flakyRate": 0.0004
|
| 505 |
+
},
|
| 506 |
+
"tests/e2e/data-tables/tables.spec.ts": {
|
| 507 |
+
"avgDuration": 71055,
|
| 508 |
+
"testCount": 7,
|
| 509 |
+
"flakyRate": 0.0011
|
| 510 |
+
},
|
| 511 |
+
"tests/e2e/data-tables/details.spec.ts": {
|
| 512 |
+
"avgDuration": 72323,
|
| 513 |
+
"testCount": 11,
|
| 514 |
+
"flakyRate": 0.0007
|
| 515 |
+
},
|
| 516 |
+
"tests/e2e/workflows/editor/expressions/mapping.spec.ts": {
|
| 517 |
+
"avgDuration": 42523,
|
| 518 |
+
"testCount": 10,
|
| 519 |
+
"flakyRate": 0.0047
|
| 520 |
+
},
|
| 521 |
+
"tests/e2e/credentials/crud.spec.ts": {
|
| 522 |
+
"avgDuration": 80126,
|
| 523 |
+
"testCount": 15,
|
| 524 |
+
"flakyRate": 0.0009
|
| 525 |
+
},
|
| 526 |
+
"tests/e2e/building-blocks/credentials.spec.ts": {
|
| 527 |
+
"avgDuration": 28608,
|
| 528 |
+
"testCount": 6,
|
| 529 |
+
"flakyRate": 0.0008
|
| 530 |
+
},
|
| 531 |
+
"tests/e2e/credentials/api-operations.spec.ts": {
|
| 532 |
+
"avgDuration": 1602,
|
| 533 |
+
"testCount": 5,
|
| 534 |
+
"flakyRate": 0.0003
|
| 535 |
+
},
|
| 536 |
+
"tests/e2e/settings/community-nodes/community-nodes.spec.ts": {
|
| 537 |
+
"avgDuration": 4091,
|
| 538 |
+
"testCount": 1,
|
| 539 |
+
"flakyRate": 0.0007
|
| 540 |
+
},
|
| 541 |
+
"tests/e2e/nodes/community-nodes.spec.ts": {
|
| 542 |
+
"avgDuration": 13999,
|
| 543 |
+
"testCount": 3,
|
| 544 |
+
"flakyRate": 0.0005
|
| 545 |
+
},
|
| 546 |
+
"tests/e2e/workflows/editor/code/code-node.spec.ts": {
|
| 547 |
+
"avgDuration": 74990,
|
| 548 |
+
"testCount": 12,
|
| 549 |
+
"flakyRate": 0.0226
|
| 550 |
+
},
|
| 551 |
+
"tests/e2e/chat-hub/chat-hub-chat-user.spec.ts": {
|
| 552 |
+
"avgDuration": 9556,
|
| 553 |
+
"testCount": 1,
|
| 554 |
+
"flakyRate": 0
|
| 555 |
+
},
|
| 556 |
+
"tests/e2e/ai/chat-session.spec.ts": {
|
| 557 |
+
"avgDuration": 6490,
|
| 558 |
+
"testCount": 1,
|
| 559 |
+
"flakyRate": 0.01
|
| 560 |
+
},
|
| 561 |
+
"tests/e2e/workflows/editor/canvas/canvas-zoom.spec.ts": {
|
| 562 |
+
"avgDuration": 54599,
|
| 563 |
+
"testCount": 12,
|
| 564 |
+
"flakyRate": 0.0005
|
| 565 |
+
},
|
| 566 |
+
"tests/e2e/workflows/editor/canvas/canvas-nodes.spec.ts": {
|
| 567 |
+
"avgDuration": 65556,
|
| 568 |
+
"testCount": 8,
|
| 569 |
+
"flakyRate": 0.0042
|
| 570 |
+
},
|
| 571 |
+
"tests/e2e/building-blocks/canvas-actions.spec.ts": {
|
| 572 |
+
"avgDuration": 31578,
|
| 573 |
+
"testCount": 9,
|
| 574 |
+
"flakyRate": 0.0006
|
| 575 |
+
},
|
| 576 |
+
"tests/e2e/workflows/editor/canvas/actions.spec.ts": {
|
| 577 |
+
"avgDuration": 80903,
|
| 578 |
+
"testCount": 20,
|
| 579 |
+
"flakyRate": 0.0005
|
| 580 |
+
},
|
| 581 |
+
"tests/e2e/workflows/editor/canvas/stickies.spec.ts": {
|
| 582 |
+
"avgDuration": 2962,
|
| 583 |
+
"testCount": 1,
|
| 584 |
+
"flakyRate": 0.0015
|
| 585 |
+
},
|
| 586 |
+
"tests/e2e/regression/CAT-726-canvas-node-connectors-not-rendered-when-nodes-inserted.spec.ts": {
|
| 587 |
+
"avgDuration": 4951,
|
| 588 |
+
"testCount": 1,
|
| 589 |
+
"flakyRate": 0
|
| 590 |
+
},
|
| 591 |
+
"tests/e2e/app-config/become-creator.spec.ts": {
|
| 592 |
+
"avgDuration": 5583,
|
| 593 |
+
"testCount": 2,
|
| 594 |
+
"flakyRate": 0.0004
|
| 595 |
+
},
|
| 596 |
+
"tests/e2e/chat-hub/chat-hub-basic.spec.ts": {
|
| 597 |
+
"avgDuration": 99260,
|
| 598 |
+
"testCount": 4,
|
| 599 |
+
"flakyRate": 0.0319
|
| 600 |
+
},
|
| 601 |
+
"tests/e2e/auth/authenticated.spec.ts": {
|
| 602 |
+
"avgDuration": 13964,
|
| 603 |
+
"testCount": 5,
|
| 604 |
+
"flakyRate": 0
|
| 605 |
+
},
|
| 606 |
+
"tests/e2e/auth/admin-smoke.spec.ts": {
|
| 607 |
+
"avgDuration": 2237,
|
| 608 |
+
"testCount": 1,
|
| 609 |
+
"flakyRate": 0
|
| 610 |
+
},
|
| 611 |
+
"tests/e2e/regression/AI-812-partial-execs-broken-when-using-chat-trigger.spec.ts": {
|
| 612 |
+
"avgDuration": 8215,
|
| 613 |
+
"testCount": 2,
|
| 614 |
+
"flakyRate": 0.0011
|
| 615 |
+
},
|
| 616 |
+
"tests/e2e/regression/AI-716-correctly-set-up-agent-model-shows-error.spec.ts": {
|
| 617 |
+
"avgDuration": 4666,
|
| 618 |
+
"testCount": 1,
|
| 619 |
+
"flakyRate": 0
|
| 620 |
+
},
|
| 621 |
+
"tests/e2e/regression/AI-1401-sub-nodes-input-panel.spec.ts": {
|
| 622 |
+
"avgDuration": 4941,
|
| 623 |
+
"testCount": 1,
|
| 624 |
+
"flakyRate": 0.0007
|
| 625 |
+
},
|
| 626 |
+
"tests/e2e/ai/assistant-basic.spec.ts": {
|
| 627 |
+
"avgDuration": 55527,
|
| 628 |
+
"testCount": 11,
|
| 629 |
+
"flakyRate": 0.0003
|
| 630 |
+
},
|
| 631 |
+
"tests/e2e/ai/assistant-support-chat.spec.ts": {
|
| 632 |
+
"avgDuration": 13201,
|
| 633 |
+
"testCount": 3,
|
| 634 |
+
"flakyRate": 0.0002
|
| 635 |
+
},
|
| 636 |
+
"tests/e2e/ai/assistant-credential-help.spec.ts": {
|
| 637 |
+
"avgDuration": 18891,
|
| 638 |
+
"testCount": 4,
|
| 639 |
+
"flakyRate": 0.0007
|
| 640 |
+
},
|
| 641 |
+
"tests/e2e/ai/assistant-code-help.spec.ts": {
|
| 642 |
+
"avgDuration": 12021,
|
| 643 |
+
"testCount": 2,
|
| 644 |
+
"flakyRate": 0.0004
|
| 645 |
+
},
|
| 646 |
+
"tests/e2e/regression/ADO-2929-can-load-old-switch-node-workflows.spec.ts": {
|
| 647 |
+
"avgDuration": 4430,
|
| 648 |
+
"testCount": 1,
|
| 649 |
+
"flakyRate": 0
|
| 650 |
+
},
|
| 651 |
+
"tests/e2e/regression/ADO-2372-prevent-clipping-params.spec.ts": {
|
| 652 |
+
"avgDuration": 8217,
|
| 653 |
+
"testCount": 2,
|
| 654 |
+
"flakyRate": 0.0004
|
| 655 |
+
},
|
| 656 |
+
"tests/e2e/regression/ADO-2270-opening-webhook-ndv-marks-workflow-as-unsaved.spec.ts": {
|
| 657 |
+
"avgDuration": 3801,
|
| 658 |
+
"testCount": 1,
|
| 659 |
+
"flakyRate": 0.0015
|
| 660 |
+
},
|
| 661 |
+
"tests/e2e/regression/ADO-2230-ndv-reset-data-pagination.spec.ts": {
|
| 662 |
+
"avgDuration": 3794,
|
| 663 |
+
"testCount": 1,
|
| 664 |
+
"flakyRate": 0
|
| 665 |
+
},
|
| 666 |
+
"tests/e2e/regression/ADO-1338-ndv-missing-input-panel.spec.ts": {
|
| 667 |
+
"avgDuration": 9604,
|
| 668 |
+
"testCount": 1,
|
| 669 |
+
"flakyRate": 0
|
| 670 |
+
}
|
| 671 |
+
}
|
| 672 |
+
}
|
.github/workflows/benchmark-destroy-nightly.yml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Destroy Benchmark Env
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
schedule:
|
| 5 |
+
- cron: '0 5 * * *'
|
| 6 |
+
workflow_dispatch:
|
| 7 |
+
|
| 8 |
+
permissions:
|
| 9 |
+
id-token: write
|
| 10 |
+
contents: read
|
| 11 |
+
|
| 12 |
+
concurrency:
|
| 13 |
+
group: benchmark
|
| 14 |
+
cancel-in-progress: false
|
| 15 |
+
|
| 16 |
+
jobs:
|
| 17 |
+
build:
|
| 18 |
+
runs-on: ubuntu-latest
|
| 19 |
+
environment: benchmarking
|
| 20 |
+
|
| 21 |
+
steps:
|
| 22 |
+
- name: Checkout
|
| 23 |
+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 24 |
+
|
| 25 |
+
- name: Azure login
|
| 26 |
+
uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1
|
| 27 |
+
with:
|
| 28 |
+
client-id: ${{ secrets.BENCHMARK_ARM_CLIENT_ID }}
|
| 29 |
+
tenant-id: ${{ secrets.BENCHMARK_ARM_TENANT_ID }}
|
| 30 |
+
subscription-id: ${{ secrets.BENCHMARK_ARM_SUBSCRIPTION_ID }}
|
| 31 |
+
|
| 32 |
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
| 33 |
+
with:
|
| 34 |
+
node-version: 22.x
|
| 35 |
+
|
| 36 |
+
- name: Setup corepack and pnpm
|
| 37 |
+
run: |
|
| 38 |
+
npm i -g corepack@0.33
|
| 39 |
+
corepack enable
|
| 40 |
+
|
| 41 |
+
- name: Install dependencies
|
| 42 |
+
run: pnpm install --frozen-lockfile
|
| 43 |
+
|
| 44 |
+
- name: Destroy cloud env
|
| 45 |
+
run: pnpm destroy-cloud-env
|
| 46 |
+
working-directory: packages/@n8n/benchmark
|
.github/workflows/benchmark-nightly.yml
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Run Nightly Benchmark
|
| 2 |
+
run-name: Benchmark ${{ inputs.n8n_tag || 'nightly' }}
|
| 3 |
+
|
| 4 |
+
on:
|
| 5 |
+
schedule:
|
| 6 |
+
- cron: '30 1,2,3 * * *'
|
| 7 |
+
workflow_dispatch:
|
| 8 |
+
inputs:
|
| 9 |
+
debug:
|
| 10 |
+
description: 'Use debug logging'
|
| 11 |
+
required: true
|
| 12 |
+
default: 'false'
|
| 13 |
+
n8n_tag:
|
| 14 |
+
description: 'Name of the n8n docker tag to run the benchmark against.'
|
| 15 |
+
required: true
|
| 16 |
+
default: 'nightly'
|
| 17 |
+
benchmark_tag:
|
| 18 |
+
description: 'Name of the benchmark cli docker tag to run the benchmark with.'
|
| 19 |
+
required: true
|
| 20 |
+
default: 'latest'
|
| 21 |
+
|
| 22 |
+
env:
|
| 23 |
+
ARM_CLIENT_ID: ${{ secrets.BENCHMARK_ARM_CLIENT_ID }}
|
| 24 |
+
ARM_SUBSCRIPTION_ID: ${{ secrets.BENCHMARK_ARM_SUBSCRIPTION_ID }}
|
| 25 |
+
ARM_TENANT_ID: ${{ secrets.BENCHMARK_ARM_TENANT_ID }}
|
| 26 |
+
N8N_TAG: ${{ inputs.n8n_tag || 'nightly' }}
|
| 27 |
+
N8N_BENCHMARK_TAG: ${{ inputs.benchmark_tag || 'latest' }}
|
| 28 |
+
DEBUG: ${{ inputs.debug == 'true' && '--debug' || '' }}
|
| 29 |
+
|
| 30 |
+
permissions:
|
| 31 |
+
id-token: write
|
| 32 |
+
contents: read
|
| 33 |
+
|
| 34 |
+
concurrency:
|
| 35 |
+
group: benchmark
|
| 36 |
+
cancel-in-progress: false
|
| 37 |
+
|
| 38 |
+
jobs:
|
| 39 |
+
build:
|
| 40 |
+
runs-on: ubuntu-latest
|
| 41 |
+
environment: benchmarking
|
| 42 |
+
|
| 43 |
+
steps:
|
| 44 |
+
- name: Checkout
|
| 45 |
+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 46 |
+
|
| 47 |
+
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3
|
| 48 |
+
with:
|
| 49 |
+
terraform_version: '1.8.5'
|
| 50 |
+
|
| 51 |
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
| 52 |
+
with:
|
| 53 |
+
node-version: 22.x
|
| 54 |
+
|
| 55 |
+
- name: Setup corepack and pnpm
|
| 56 |
+
run: |
|
| 57 |
+
npm i -g corepack@0.33
|
| 58 |
+
corepack enable
|
| 59 |
+
|
| 60 |
+
- name: Install dependencies
|
| 61 |
+
run: pnpm install --frozen-lockfile
|
| 62 |
+
|
| 63 |
+
- name: Azure login
|
| 64 |
+
uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1
|
| 65 |
+
with:
|
| 66 |
+
client-id: ${{ env.ARM_CLIENT_ID }}
|
| 67 |
+
tenant-id: ${{ env.ARM_TENANT_ID }}
|
| 68 |
+
subscription-id: ${{ env.ARM_SUBSCRIPTION_ID }}
|
| 69 |
+
|
| 70 |
+
- name: Destroy any existing environment
|
| 71 |
+
run: pnpm destroy-cloud-env
|
| 72 |
+
working-directory: packages/@n8n/benchmark
|
| 73 |
+
|
| 74 |
+
- name: Provision the environment
|
| 75 |
+
run: pnpm provision-cloud-env ${{ env.DEBUG }}
|
| 76 |
+
working-directory: packages/@n8n/benchmark
|
| 77 |
+
|
| 78 |
+
- name: Run the benchmark
|
| 79 |
+
id: benchmark
|
| 80 |
+
env:
|
| 81 |
+
BENCHMARK_RESULT_WEBHOOK_URL: ${{ secrets.BENCHMARK_RESULT_WEBHOOK_URL }}
|
| 82 |
+
BENCHMARK_RESULT_WEBHOOK_AUTH_HEADER: ${{ secrets.BENCHMARK_RESULT_WEBHOOK_AUTH_HEADER }}
|
| 83 |
+
N8N_LICENSE_CERT: ${{ secrets.N8N_BENCHMARK_LICENSE_CERT }}
|
| 84 |
+
run: |
|
| 85 |
+
pnpm benchmark-in-cloud \
|
| 86 |
+
--vus 5 \
|
| 87 |
+
--duration 1m \
|
| 88 |
+
--n8nTag ${{ env.N8N_TAG }} \
|
| 89 |
+
--benchmarkTag ${{ env.N8N_BENCHMARK_TAG }} \
|
| 90 |
+
${{ env.DEBUG }}
|
| 91 |
+
working-directory: packages/@n8n/benchmark
|
| 92 |
+
|
| 93 |
+
# We need to login again because the access token expires
|
| 94 |
+
- name: Azure login
|
| 95 |
+
if: always()
|
| 96 |
+
uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1
|
| 97 |
+
with:
|
| 98 |
+
client-id: ${{ env.ARM_CLIENT_ID }}
|
| 99 |
+
tenant-id: ${{ env.ARM_TENANT_ID }}
|
| 100 |
+
subscription-id: ${{ env.ARM_SUBSCRIPTION_ID }}
|
| 101 |
+
|
| 102 |
+
- name: Destroy the environment
|
| 103 |
+
if: always()
|
| 104 |
+
run: pnpm destroy-cloud-env ${{ env.DEBUG }}
|
| 105 |
+
working-directory: packages/@n8n/benchmark
|
| 106 |
+
|
| 107 |
+
- name: Fail `build` job if `benchmark` step failed
|
| 108 |
+
if: steps.benchmark.outcome == 'failure'
|
| 109 |
+
run: exit 1
|
| 110 |
+
|
| 111 |
+
notify-on-failure:
|
| 112 |
+
name: Notify Cats on failure
|
| 113 |
+
runs-on: ubuntu-latest
|
| 114 |
+
needs: [build]
|
| 115 |
+
if: failure()
|
| 116 |
+
steps:
|
| 117 |
+
- uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
| 118 |
+
with:
|
| 119 |
+
status: ${{ job.status }}
|
| 120 |
+
channel: '#team-catalysts'
|
| 121 |
+
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
| 122 |
+
message: Benchmark run failed for n8n tag `${{ inputs.n8n_tag || 'nightly' }}` - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
.github/workflows/build-unit-test-pr-comment.yml
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Trigger build/unit tests on PR comment
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
issue_comment:
|
| 5 |
+
types: [created]
|
| 6 |
+
|
| 7 |
+
permissions:
|
| 8 |
+
pull-requests: read
|
| 9 |
+
contents: read
|
| 10 |
+
actions: write
|
| 11 |
+
issues: write
|
| 12 |
+
|
| 13 |
+
jobs:
|
| 14 |
+
validate_and_dispatch:
|
| 15 |
+
name: Validate user and dispatch CI workflow
|
| 16 |
+
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/build-unit-test')
|
| 17 |
+
runs-on: ubuntu-latest
|
| 18 |
+
steps:
|
| 19 |
+
- name: Validate user permissions and collect PR data
|
| 20 |
+
id: check_permissions
|
| 21 |
+
uses: actions/github-script@v7
|
| 22 |
+
with:
|
| 23 |
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
| 24 |
+
script: |
|
| 25 |
+
const commenter = context.actor;
|
| 26 |
+
const body = (context.payload.comment.body || '').trim();
|
| 27 |
+
const isCommand = body.startsWith('/build-unit-test');
|
| 28 |
+
const allowedPermissions = ['admin', 'write', 'maintain'];
|
| 29 |
+
const commentId = context.payload.comment.id;
|
| 30 |
+
const { owner, repo } = context.repo;
|
| 31 |
+
|
| 32 |
+
async function react(content) {
|
| 33 |
+
try {
|
| 34 |
+
await github.rest.reactions.createForIssueComment({
|
| 35 |
+
owner,
|
| 36 |
+
repo,
|
| 37 |
+
comment_id: commentId,
|
| 38 |
+
content,
|
| 39 |
+
});
|
| 40 |
+
} catch (error) {
|
| 41 |
+
console.log(`Failed to add reaction '${content}': ${error.message}`);
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
core.setOutput('proceed', 'false');
|
| 46 |
+
core.setOutput('headSha', '');
|
| 47 |
+
core.setOutput('prNumber', '');
|
| 48 |
+
|
| 49 |
+
if (!context.payload.issue.pull_request || !isCommand) {
|
| 50 |
+
console.log('Comment is not /build-unit-test on a pull request. Skipping.');
|
| 51 |
+
return;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
let permission;
|
| 55 |
+
try {
|
| 56 |
+
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
| 57 |
+
owner,
|
| 58 |
+
repo,
|
| 59 |
+
username: commenter,
|
| 60 |
+
});
|
| 61 |
+
permission = data.permission;
|
| 62 |
+
} catch (error) {
|
| 63 |
+
console.log(`Could not verify permissions for @${commenter}: ${error.message}`);
|
| 64 |
+
await react('confused');
|
| 65 |
+
return;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
if (!allowedPermissions.includes(permission)) {
|
| 69 |
+
console.log(`User @${commenter} has '${permission}' permission; requires admin/write/maintain.`);
|
| 70 |
+
await react('-1');
|
| 71 |
+
return;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
try {
|
| 75 |
+
const prNumber = context.issue.number;
|
| 76 |
+
const { data: pr } = await github.rest.pulls.get({
|
| 77 |
+
owner,
|
| 78 |
+
repo,
|
| 79 |
+
pull_number: prNumber,
|
| 80 |
+
});
|
| 81 |
+
await react('+1');
|
| 82 |
+
core.setOutput('proceed', 'true');
|
| 83 |
+
core.setOutput('headSha', pr.head.sha);
|
| 84 |
+
core.setOutput('prNumber', String(prNumber));
|
| 85 |
+
} catch (error) {
|
| 86 |
+
console.log(`Failed to fetch PR details for PR #${context.issue.number}: ${error.message}`);
|
| 87 |
+
await react('confused');
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
- name: Dispatch build/unit test workflow
|
| 91 |
+
if: ${{ steps.check_permissions.outputs.proceed == 'true' }}
|
| 92 |
+
env:
|
| 93 |
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
| 94 |
+
HEAD_SHA: ${{ steps.check_permissions.outputs.headSha }}
|
| 95 |
+
PR_NUMBER: ${{ steps.check_permissions.outputs.prNumber }}
|
| 96 |
+
run: |
|
| 97 |
+
gh workflow run ci-manual-build-unit-tests.yml \
|
| 98 |
+
--repo "${{ github.repository }}" \
|
| 99 |
+
-f ref="${HEAD_SHA}" \
|
| 100 |
+
-f pr_number="${PR_NUMBER}"
|
.github/workflows/build-windows.yml
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Windows CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
inputs:
|
| 6 |
+
notify_on_failure:
|
| 7 |
+
description: 'Send Slack notification on build failure'
|
| 8 |
+
required: false
|
| 9 |
+
type: boolean
|
| 10 |
+
default: false
|
| 11 |
+
workflow_call:
|
| 12 |
+
inputs:
|
| 13 |
+
notify_on_failure:
|
| 14 |
+
description: 'Send Slack notification on build failure'
|
| 15 |
+
required: false
|
| 16 |
+
type: boolean
|
| 17 |
+
default: false
|
| 18 |
+
secrets:
|
| 19 |
+
QBOT_SLACK_TOKEN:
|
| 20 |
+
required: false
|
| 21 |
+
pull_request:
|
| 22 |
+
branches: [master]
|
| 23 |
+
paths:
|
| 24 |
+
- '**/package.json'
|
| 25 |
+
- '**/turbo.json'
|
| 26 |
+
- '.github/workflows/build-windows.yml'
|
| 27 |
+
|
| 28 |
+
jobs:
|
| 29 |
+
build:
|
| 30 |
+
runs-on: windows-latest
|
| 31 |
+
|
| 32 |
+
steps:
|
| 33 |
+
- name: Checkout code
|
| 34 |
+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 35 |
+
|
| 36 |
+
- name: Setup Node.js and Build
|
| 37 |
+
uses: ./.github/actions/setup-nodejs
|
| 38 |
+
with:
|
| 39 |
+
build-command: pnpm build
|
| 40 |
+
|
| 41 |
+
- name: Send Slack notification on failure
|
| 42 |
+
if: failure() && inputs.notify_on_failure == true
|
| 43 |
+
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
| 44 |
+
with:
|
| 45 |
+
method: chat.postMessage
|
| 46 |
+
token: ${{ secrets.QBOT_SLACK_TOKEN }}
|
| 47 |
+
payload: |
|
| 48 |
+
{
|
| 49 |
+
"channel": "C035KBDA917",
|
| 50 |
+
"text": "🚨 Windows build failed for `${{ github.repository }}` on branch `${{ github.ref_name }}`",
|
| 51 |
+
"blocks": [
|
| 52 |
+
{
|
| 53 |
+
"type": "header",
|
| 54 |
+
"text": { "type": "plain_text", "text": "🚨 Windows Build Failed" }
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
"type": "section",
|
| 58 |
+
"fields": [
|
| 59 |
+
{ "type": "mrkdwn", "text": "*Repository:*\n<${{ github.server_url }}/${{ github.repository }}|${{ github.repository }}>" },
|
| 60 |
+
{ "type": "mrkdwn", "text": "*Branch:*\n`${{ github.ref_name }}`" },
|
| 61 |
+
{ "type": "mrkdwn", "text": "*Commit:*\n`${{ github.sha }}`" },
|
| 62 |
+
{ "type": "mrkdwn", "text": "*Trigger:*\n${{ github.event_name }}" }
|
| 63 |
+
]
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"type": "section",
|
| 67 |
+
"text": { "type": "mrkdwn", "text": ":warning: *Cross-platform compatibility issue detected*\nThis likely indicates Unix-specific commands in package.json scripts or build configuration that don't work on Windows." }
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"type": "actions",
|
| 71 |
+
"elements": [
|
| 72 |
+
{
|
| 73 |
+
"type": "button",
|
| 74 |
+
"text": { "type": "plain_text", "text": ":github: View Workflow Run" },
|
| 75 |
+
"style": "danger",
|
| 76 |
+
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
| 77 |
+
}
|
| 78 |
+
]
|
| 79 |
+
}
|
| 80 |
+
]
|
| 81 |
+
}
|
.github/workflows/check-documentation-urls.yml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Check Documentation URLs
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
release:
|
| 5 |
+
types: [published]
|
| 6 |
+
schedule:
|
| 7 |
+
- cron: '0 0 * * *'
|
| 8 |
+
workflow_dispatch:
|
| 9 |
+
|
| 10 |
+
jobs:
|
| 11 |
+
check-docs-urls:
|
| 12 |
+
runs-on: ubuntu-latest
|
| 13 |
+
|
| 14 |
+
timeout-minutes: 5
|
| 15 |
+
|
| 16 |
+
steps:
|
| 17 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 18 |
+
|
| 19 |
+
- name: Setup Node.js
|
| 20 |
+
uses: ./.github/actions/setup-nodejs
|
| 21 |
+
with:
|
| 22 |
+
build-command: turbo build --filter=*nodes*
|
| 23 |
+
|
| 24 |
+
- run: npm install --prefix=.github/scripts --no-package-lock
|
| 25 |
+
|
| 26 |
+
- name: Test URLs
|
| 27 |
+
run: node .github/scripts/validate-docs-links.js
|
| 28 |
+
|
| 29 |
+
- name: Notify Slack on failure
|
| 30 |
+
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
| 31 |
+
if: failure()
|
| 32 |
+
with:
|
| 33 |
+
status: ${{ job.status }}
|
| 34 |
+
channel: '#alerts-build'
|
| 35 |
+
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
| 36 |
+
message: |
|
| 37 |
+
<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}| Documentation URLs check failed >
|
.github/workflows/check-pr-title.yml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Check PR title
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request:
|
| 5 |
+
types:
|
| 6 |
+
- opened
|
| 7 |
+
- edited
|
| 8 |
+
- synchronize
|
| 9 |
+
branches:
|
| 10 |
+
- 'master'
|
| 11 |
+
- '1.x'
|
| 12 |
+
|
| 13 |
+
jobs:
|
| 14 |
+
check-pr-title:
|
| 15 |
+
runs-on: ubuntu-latest
|
| 16 |
+
timeout-minutes: 5
|
| 17 |
+
steps:
|
| 18 |
+
- name: Validate PR title
|
| 19 |
+
uses: n8n-io/validate-n8n-pull-request-title@c3b6fd06bda12eebd57a592c0cf3b747d5b73569 # v2.4.0
|
| 20 |
+
env:
|
| 21 |
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
.github/workflows/check-run-eligibility.yml
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Determines if conditions are met for running subsequent jobs on a Pull Request.
|
| 2 |
+
#
|
| 3 |
+
# !! IMPORTANT !!
|
| 4 |
+
# This workflow RELIES on being called from a parent workflow triggered by
|
| 5 |
+
# a `pull_request` or `pull_request_target` event. It uses `github.event`
|
| 6 |
+
# to access PR details.
|
| 7 |
+
#
|
| 8 |
+
# It checks if all the following conditions are TRUE:
|
| 9 |
+
# 1. The PR is NOT from a fork (i.e., it's an internal PR).
|
| 10 |
+
# 2. The PR has been approved by a maintainer (`is_pr_approved_by_maintainer`).
|
| 11 |
+
# 3. The PR's source branch does NOT match an excluded pattern.
|
| 12 |
+
# 4. The PR includes relevant file changes (`paths_filter_patterns`).
|
| 13 |
+
#
|
| 14 |
+
# It outputs `should_run` as 'true' if ALL conditions pass, 'false' otherwise.
|
| 15 |
+
|
| 16 |
+
name: PR Eligibility Check
|
| 17 |
+
|
| 18 |
+
on:
|
| 19 |
+
workflow_call:
|
| 20 |
+
inputs:
|
| 21 |
+
is_pr_approved_by_maintainer:
|
| 22 |
+
required: true
|
| 23 |
+
type: boolean
|
| 24 |
+
paths_filter_patterns:
|
| 25 |
+
description: "Path filter patterns for 'paths-filter-action'."
|
| 26 |
+
required: false
|
| 27 |
+
type: string
|
| 28 |
+
default: |
|
| 29 |
+
not_ignored:
|
| 30 |
+
- '!.devcontainer/**'
|
| 31 |
+
- '!.github/*'
|
| 32 |
+
- '!.github/scripts/*'
|
| 33 |
+
- '!.github/workflows/benchmark-*'
|
| 34 |
+
- '!.github/workflows/check-*'
|
| 35 |
+
- '!.vscode/**'
|
| 36 |
+
- '!docker/**'
|
| 37 |
+
- '!packages/@n8n/benchmark/**'
|
| 38 |
+
- '!packages/@n8n/task-runner-python/**'
|
| 39 |
+
- '!**/*.md'
|
| 40 |
+
excluded_source_branch_patterns:
|
| 41 |
+
description: 'Newline-separated list of glob patterns for source branches to EXCLUDE.'
|
| 42 |
+
required: false
|
| 43 |
+
type: string
|
| 44 |
+
default: |
|
| 45 |
+
release/*
|
| 46 |
+
master
|
| 47 |
+
|
| 48 |
+
outputs:
|
| 49 |
+
should_run:
|
| 50 |
+
description: "Outputs 'true' if all eligibility checks pass, otherwise 'false'."
|
| 51 |
+
value: ${{ jobs.evaluate_conditions.outputs.run_decision }}
|
| 52 |
+
|
| 53 |
+
jobs:
|
| 54 |
+
evaluate_conditions:
|
| 55 |
+
runs-on: ubuntu-latest
|
| 56 |
+
outputs:
|
| 57 |
+
run_decision: ${{ steps.evaluate.outputs.should_run }}
|
| 58 |
+
steps:
|
| 59 |
+
- name: Check out current commit
|
| 60 |
+
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
|
| 61 |
+
with:
|
| 62 |
+
ref: ${{ github.event.pull_request.head.sha }}
|
| 63 |
+
|
| 64 |
+
- name: Determine changed files
|
| 65 |
+
uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2
|
| 66 |
+
id: changed
|
| 67 |
+
with:
|
| 68 |
+
filters: ${{ inputs.paths_filter_patterns }}
|
| 69 |
+
predicate-quantifier: 'every'
|
| 70 |
+
|
| 71 |
+
- name: Evaluate Conditions & Set Output
|
| 72 |
+
id: evaluate
|
| 73 |
+
env:
|
| 74 |
+
IS_FORK: ${{ github.event.pull_request.head.repo.fork }}
|
| 75 |
+
IS_APPROVED: ${{ inputs.is_pr_approved_by_maintainer }}
|
| 76 |
+
FILES_CHANGED: ${{ steps.changed.outputs.not_ignored == 'true' }}
|
| 77 |
+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
| 78 |
+
EXCLUDED_PATTERNS: ${{ inputs.excluded_source_branch_patterns }}
|
| 79 |
+
run: |
|
| 80 |
+
if [[ "$IS_FORK" == "true" ]]; then
|
| 81 |
+
is_community="true"
|
| 82 |
+
else
|
| 83 |
+
is_community="false"
|
| 84 |
+
fi
|
| 85 |
+
|
| 86 |
+
source_branch_excluded="false"
|
| 87 |
+
while IFS= read -r pattern; do
|
| 88 |
+
# shellcheck disable=SC2053
|
| 89 |
+
if [[ -n "$pattern" && "$HEAD_REF" == $pattern ]]; then
|
| 90 |
+
source_branch_excluded="true"
|
| 91 |
+
break
|
| 92 |
+
fi
|
| 93 |
+
done <<< "$EXCLUDED_PATTERNS"
|
| 94 |
+
|
| 95 |
+
echo "--- Checking Conditions ---"
|
| 96 |
+
echo "Is NOT Community PR: $([[ "$is_community" == "false" ]] && echo true || echo false)"
|
| 97 |
+
echo "Files Changed: $FILES_CHANGED"
|
| 98 |
+
echo "Source Branch Excluded: $source_branch_excluded"
|
| 99 |
+
echo "Is Approved: $IS_APPROVED"
|
| 100 |
+
echo "-------------------------"
|
| 101 |
+
|
| 102 |
+
if [[ "$is_community" == "false" && \
|
| 103 |
+
"$FILES_CHANGED" == "true" && \
|
| 104 |
+
"$source_branch_excluded" == "false" && \
|
| 105 |
+
"$IS_APPROVED" == "true" ]]; then
|
| 106 |
+
echo "Decision: Conditions met. Setting should_run=true."
|
| 107 |
+
echo "should_run=true" >> "$GITHUB_OUTPUT"
|
| 108 |
+
else
|
| 109 |
+
echo "Decision: Conditions not met. Setting should_run=false."
|
| 110 |
+
echo "should_run=false" >> "$GITHUB_OUTPUT"
|
| 111 |
+
fi
|
.github/workflows/chromatic.yml
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Chromatic
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
schedule:
|
| 5 |
+
- cron: '0 0 * * *'
|
| 6 |
+
workflow_dispatch:
|
| 7 |
+
pull_request_review:
|
| 8 |
+
types: [submitted]
|
| 9 |
+
|
| 10 |
+
concurrency:
|
| 11 |
+
group: chromatic-${{ github.event.pull_request.number || github.ref }}
|
| 12 |
+
cancel-in-progress: true
|
| 13 |
+
|
| 14 |
+
jobs:
|
| 15 |
+
changeset:
|
| 16 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 17 |
+
steps:
|
| 18 |
+
- name: Determine changed files
|
| 19 |
+
uses: tomi/paths-filter-action@v3.0.2
|
| 20 |
+
id: changed
|
| 21 |
+
if: github.event_name == 'pull_request_review'
|
| 22 |
+
with:
|
| 23 |
+
filters: |
|
| 24 |
+
design_system:
|
| 25 |
+
- 'packages/frontend/@n8n/design-system/**'
|
| 26 |
+
- '.github/workflows/storybook.yml'
|
| 27 |
+
outputs:
|
| 28 |
+
has_changes: ${{ steps.changed.outputs.design_system || 'false' }}
|
| 29 |
+
|
| 30 |
+
chromatic:
|
| 31 |
+
needs: [changeset]
|
| 32 |
+
if: |
|
| 33 |
+
github.event_name == 'schedule' ||
|
| 34 |
+
github.event_name == 'workflow_dispatch' ||
|
| 35 |
+
(
|
| 36 |
+
github.event_name == 'pull_request_review' &&
|
| 37 |
+
needs.changeset.outputs.has_changes == 'true' &&
|
| 38 |
+
github.event.review.state == 'approved' &&
|
| 39 |
+
!startsWith(github.event.pull_request.head.ref, 'release/') &&
|
| 40 |
+
!startsWith(github.event.pull_request.head.ref, 'release-pr/')
|
| 41 |
+
)
|
| 42 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 43 |
+
steps:
|
| 44 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 45 |
+
with:
|
| 46 |
+
fetch-depth: 0
|
| 47 |
+
|
| 48 |
+
- name: Setup Node.js
|
| 49 |
+
uses: ./.github/actions/setup-nodejs
|
| 50 |
+
with:
|
| 51 |
+
build-command: pnpm run build --filter=@n8n/utils --filter=@n8n/vitest-config --filter=@n8n/design-system
|
| 52 |
+
|
| 53 |
+
- name: Publish to Chromatic
|
| 54 |
+
uses: chromaui/action@1cfa065cbdab28f6ca3afaeb3d761383076a35aa # v11
|
| 55 |
+
id: chromatic_tests
|
| 56 |
+
continue-on-error: true
|
| 57 |
+
with:
|
| 58 |
+
workingDir: packages/frontend/@n8n/design-system
|
| 59 |
+
autoAcceptChanges: 'master'
|
| 60 |
+
skip: 'release/**'
|
| 61 |
+
onlyChanged: true
|
| 62 |
+
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
| 63 |
+
exitZeroOnChanges: false
|
| 64 |
+
|
| 65 |
+
- name: Success comment
|
| 66 |
+
if: steps.chromatic_tests.outcome == 'success' && github.ref != 'refs/heads/master'
|
| 67 |
+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
|
| 68 |
+
with:
|
| 69 |
+
issue-number: ${{ github.event.pull_request.number }}
|
| 70 |
+
token: ${{ secrets.GITHUB_TOKEN }}
|
| 71 |
+
edit-mode: replace
|
| 72 |
+
body: |
|
| 73 |
+
:white_check_mark: No visual regressions found.
|
| 74 |
+
|
| 75 |
+
- name: Fail comment
|
| 76 |
+
if: steps.chromatic_tests.outcome != 'success' && github.ref != 'refs/heads/master'
|
| 77 |
+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
|
| 78 |
+
with:
|
| 79 |
+
issue-number: ${{ github.event.pull_request.number }}
|
| 80 |
+
token: ${{ secrets.GITHUB_TOKEN }}
|
| 81 |
+
edit-mode: replace
|
| 82 |
+
body: |
|
| 83 |
+
[:warning: Visual regressions found](${{steps.chromatic_tests.outputs.url}}): ${{steps.chromatic_tests.outputs.changeCount}}
|
.github/workflows/ci-evals.yml
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Run Workflow Builder Evals
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- master
|
| 7 |
+
paths:
|
| 8 |
+
- 'packages/@n8n/ai-workflow-builder.ee/**'
|
| 9 |
+
- '.github/workflows/ci-evals.yml'
|
| 10 |
+
schedule:
|
| 11 |
+
- cron: '0 22 * * 6'
|
| 12 |
+
workflow_dispatch:
|
| 13 |
+
inputs:
|
| 14 |
+
branch:
|
| 15 |
+
description: 'GitHub branch to test.'
|
| 16 |
+
required: false
|
| 17 |
+
default: 'master'
|
| 18 |
+
dataset:
|
| 19 |
+
description: 'LangSmith dataset to use.'
|
| 20 |
+
required: false
|
| 21 |
+
default: 'workflow-builder-canvas-prompts'
|
| 22 |
+
|
| 23 |
+
jobs:
|
| 24 |
+
evals:
|
| 25 |
+
name: Run Evaluations
|
| 26 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 27 |
+
env:
|
| 28 |
+
N8N_AI_ANTHROPIC_KEY: ${{ secrets.EVALS_ANTHROPIC_KEY }}
|
| 29 |
+
LANGSMITH_TRACING: true
|
| 30 |
+
LANGSMITH_ENDPOINT: ${{ secrets.EVALS_LANGSMITH_ENDPOINT }}
|
| 31 |
+
LANGSMITH_API_KEY: ${{ secrets.EVALS_LANGSMITH_API_KEY }}
|
| 32 |
+
steps:
|
| 33 |
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 34 |
+
with:
|
| 35 |
+
ref: ${{ github.event.inputs.branch || github.ref }}
|
| 36 |
+
|
| 37 |
+
- name: Select dataset
|
| 38 |
+
run: |
|
| 39 |
+
DATASET="workflow-builder-canvas-prompts"
|
| 40 |
+
if [ "${{ github.event_name }}" = "schedule" ]; then
|
| 41 |
+
DATASET="prompts-v2"
|
| 42 |
+
elif [ -n "${{ github.event.inputs.dataset }}" ]; then
|
| 43 |
+
DATASET="${{ github.event.inputs.dataset }}"
|
| 44 |
+
fi
|
| 45 |
+
echo "LANGSMITH_DATASET_NAME=$DATASET" >> "$GITHUB_ENV"
|
| 46 |
+
|
| 47 |
+
- name: Setup and Build
|
| 48 |
+
uses: ./.github/actions/setup-nodejs
|
| 49 |
+
|
| 50 |
+
- name: Install uv
|
| 51 |
+
uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # 6.5.0
|
| 52 |
+
with:
|
| 53 |
+
enable-cache: true
|
| 54 |
+
|
| 55 |
+
- name: Install just
|
| 56 |
+
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3.0.0
|
| 57 |
+
|
| 58 |
+
- name: Install Python
|
| 59 |
+
working-directory: packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python
|
| 60 |
+
run: uv python install 3.11
|
| 61 |
+
|
| 62 |
+
- name: Install workflow comparison dependencies
|
| 63 |
+
working-directory: packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python
|
| 64 |
+
run: just sync-all
|
| 65 |
+
|
| 66 |
+
- name: Export Node Types
|
| 67 |
+
run: |
|
| 68 |
+
./packages/cli/bin/n8n export:nodes --output ./packages/@n8n/ai-workflow-builder.ee/evaluations/nodes.json
|
| 69 |
+
|
| 70 |
+
- name: Run Evaluations
|
| 71 |
+
working-directory: packages/@n8n/ai-workflow-builder.ee/evaluations
|
| 72 |
+
run: |
|
| 73 |
+
pnpm eval:langsmith --repetitions 3
|
.github/workflows/ci-manual-build-unit-tests.yml
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Build, unit test and lint (manual trigger)
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
inputs:
|
| 6 |
+
ref:
|
| 7 |
+
description: Commit SHA or ref to check out
|
| 8 |
+
required: true
|
| 9 |
+
pr_number:
|
| 10 |
+
description: PR number (optional, for check reporting)
|
| 11 |
+
required: false
|
| 12 |
+
type: string
|
| 13 |
+
|
| 14 |
+
permissions:
|
| 15 |
+
contents: read
|
| 16 |
+
checks: write
|
| 17 |
+
|
| 18 |
+
jobs:
|
| 19 |
+
create-check-run:
|
| 20 |
+
name: Create Check Run
|
| 21 |
+
runs-on: ubuntu-latest
|
| 22 |
+
if: inputs.pr_number != ''
|
| 23 |
+
outputs:
|
| 24 |
+
check_run_id: ${{ steps.create.outputs.check_run_id }}
|
| 25 |
+
steps:
|
| 26 |
+
- name: Create pending check run on PR
|
| 27 |
+
id: create
|
| 28 |
+
uses: actions/github-script@v7
|
| 29 |
+
with:
|
| 30 |
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
| 31 |
+
script: |
|
| 32 |
+
const { data: checkRun } = await github.rest.checks.create({
|
| 33 |
+
owner: context.repo.owner,
|
| 34 |
+
repo: context.repo.repo,
|
| 35 |
+
name: 'Build & Unit Tests - Checks',
|
| 36 |
+
head_sha: '${{ inputs.ref }}',
|
| 37 |
+
status: 'in_progress',
|
| 38 |
+
output: {
|
| 39 |
+
title: 'Build & Unit Tests - Checks',
|
| 40 |
+
summary: 'Running build, unit tests, and lint...'
|
| 41 |
+
}
|
| 42 |
+
});
|
| 43 |
+
|
| 44 |
+
core.setOutput('check_run_id', checkRun.id);
|
| 45 |
+
console.log(`Created check run ${checkRun.id} on commit ${{ inputs.ref }}`);
|
| 46 |
+
|
| 47 |
+
install-and-build:
|
| 48 |
+
name: Install & Build
|
| 49 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 50 |
+
env:
|
| 51 |
+
NODE_OPTIONS: '--max-old-space-size=6144'
|
| 52 |
+
steps:
|
| 53 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 54 |
+
with:
|
| 55 |
+
ref: ${{ inputs.ref }}
|
| 56 |
+
|
| 57 |
+
- name: Setup and Build
|
| 58 |
+
uses: ./.github/actions/setup-nodejs
|
| 59 |
+
|
| 60 |
+
- name: Run format check
|
| 61 |
+
run: pnpm format:check
|
| 62 |
+
|
| 63 |
+
- name: Run typecheck
|
| 64 |
+
run: pnpm typecheck
|
| 65 |
+
|
| 66 |
+
unit-tests:
|
| 67 |
+
name: Unit tests
|
| 68 |
+
needs: install-and-build
|
| 69 |
+
uses: ./.github/workflows/units-tests-reusable.yml
|
| 70 |
+
with:
|
| 71 |
+
ref: ${{ inputs.ref }}
|
| 72 |
+
collectCoverage: true
|
| 73 |
+
secrets:
|
| 74 |
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
| 75 |
+
|
| 76 |
+
lint:
|
| 77 |
+
name: Lint
|
| 78 |
+
needs: install-and-build
|
| 79 |
+
uses: ./.github/workflows/linting-reusable.yml
|
| 80 |
+
with:
|
| 81 |
+
ref: ${{ inputs.ref }}
|
| 82 |
+
|
| 83 |
+
post-build-unit-tests:
|
| 84 |
+
name: Build & Unit Tests - Checks
|
| 85 |
+
runs-on: ubuntu-latest
|
| 86 |
+
needs: [create-check-run, install-and-build, unit-tests, lint]
|
| 87 |
+
if: always()
|
| 88 |
+
steps:
|
| 89 |
+
- name: Update check run on PR (if triggered from PR comment)
|
| 90 |
+
if: inputs.pr_number != ''
|
| 91 |
+
uses: actions/github-script@v7
|
| 92 |
+
with:
|
| 93 |
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
| 94 |
+
script: |
|
| 95 |
+
const checkRunId = '${{ needs.create-check-run.outputs.check_run_id }}';
|
| 96 |
+
|
| 97 |
+
if (!checkRunId) {
|
| 98 |
+
console.log('No check run ID found, skipping update');
|
| 99 |
+
return;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
const buildResult = '${{ needs.install-and-build.result }}';
|
| 103 |
+
const testResult = '${{ needs.unit-tests.result }}';
|
| 104 |
+
const lintResult = '${{ needs.lint.result }}';
|
| 105 |
+
|
| 106 |
+
const conclusion = (buildResult === 'success' && testResult === 'success' && lintResult === 'success')
|
| 107 |
+
? 'success'
|
| 108 |
+
: 'failure';
|
| 109 |
+
|
| 110 |
+
const summary = `
|
| 111 |
+
**Build**: ${buildResult}
|
| 112 |
+
**Unit Tests**: ${testResult}
|
| 113 |
+
**Lint**: ${lintResult}
|
| 114 |
+
`;
|
| 115 |
+
|
| 116 |
+
await github.rest.checks.update({
|
| 117 |
+
owner: context.repo.owner,
|
| 118 |
+
repo: context.repo.repo,
|
| 119 |
+
check_run_id: parseInt(checkRunId),
|
| 120 |
+
status: 'completed',
|
| 121 |
+
conclusion: conclusion,
|
| 122 |
+
output: {
|
| 123 |
+
title: 'Build & Unit Tests - Checks',
|
| 124 |
+
summary: summary
|
| 125 |
+
}
|
| 126 |
+
});
|
| 127 |
+
|
| 128 |
+
console.log(`Updated check run ${checkRunId} with conclusion: ${conclusion}`);
|
| 129 |
+
|
| 130 |
+
- name: Fail if any job failed
|
| 131 |
+
if: needs.install-and-build.result == 'failure' || needs.unit-tests.result == 'failure' || needs.lint.result == 'failure'
|
| 132 |
+
run: exit 1
|
.github/workflows/ci-master.yml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Test Master
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- master
|
| 7 |
+
- 1.x
|
| 8 |
+
paths-ignore:
|
| 9 |
+
- packages/@n8n/task-runner-python/**
|
| 10 |
+
|
| 11 |
+
jobs:
|
| 12 |
+
build-github:
|
| 13 |
+
name: Build for Github Cache
|
| 14 |
+
runs-on: ubuntu-latest
|
| 15 |
+
steps:
|
| 16 |
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 17 |
+
|
| 18 |
+
- name: Setup and Build
|
| 19 |
+
uses: ./.github/actions/setup-nodejs
|
| 20 |
+
|
| 21 |
+
unit-test:
|
| 22 |
+
name: Unit tests
|
| 23 |
+
uses: ./.github/workflows/units-tests-reusable.yml
|
| 24 |
+
strategy:
|
| 25 |
+
matrix:
|
| 26 |
+
node-version: [20.x, 22.x, 24.3.x]
|
| 27 |
+
with:
|
| 28 |
+
ref: ${{ github.sha }}
|
| 29 |
+
nodeVersion: ${{ matrix.node-version }}
|
| 30 |
+
collectCoverage: ${{ matrix.node-version == '22.x' }}
|
| 31 |
+
secrets:
|
| 32 |
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
| 33 |
+
|
| 34 |
+
lint:
|
| 35 |
+
name: Lint
|
| 36 |
+
uses: ./.github/workflows/linting-reusable.yml
|
| 37 |
+
with:
|
| 38 |
+
ref: ${{ github.sha }}
|
| 39 |
+
|
| 40 |
+
notify-on-failure:
|
| 41 |
+
name: Notify Slack on failure
|
| 42 |
+
runs-on: ubuntu-latest
|
| 43 |
+
needs: [unit-test, lint, build-github]
|
| 44 |
+
steps:
|
| 45 |
+
- name: Notify Slack on failure
|
| 46 |
+
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
| 47 |
+
if: failure()
|
| 48 |
+
with:
|
| 49 |
+
status: ${{ job.status }}
|
| 50 |
+
channel: '#alerts-build'
|
| 51 |
+
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
| 52 |
+
message: ${{ github.ref_name }} branch (build or test or lint) failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
.github/workflows/ci-postgres-mysql.yml
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Test Postgres and MySQL schemas
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
schedule:
|
| 5 |
+
- cron: '0 0 * * *'
|
| 6 |
+
workflow_dispatch:
|
| 7 |
+
pull_request:
|
| 8 |
+
paths:
|
| 9 |
+
- packages/cli/src/databases/**
|
| 10 |
+
- packages/cli/src/modules/*/database/**
|
| 11 |
+
- packages/cli/src/modules/**/*.entity.ts
|
| 12 |
+
- packages/cli/src/modules/**/*.repository.ts
|
| 13 |
+
- packages/cli/test/integration/**
|
| 14 |
+
- packages/cli/test/shared/db/**
|
| 15 |
+
- packages/@n8n/db/**
|
| 16 |
+
- packages/cli/**/__tests__/**
|
| 17 |
+
- .github/workflows/ci-postgres-mysql.yml
|
| 18 |
+
- .github/docker-compose.yml
|
| 19 |
+
|
| 20 |
+
concurrency:
|
| 21 |
+
group: db-${{ github.event.pull_request.number || github.ref }}
|
| 22 |
+
cancel-in-progress: true
|
| 23 |
+
|
| 24 |
+
env:
|
| 25 |
+
NODE_OPTIONS: '--max-old-space-size=3072'
|
| 26 |
+
|
| 27 |
+
jobs:
|
| 28 |
+
build:
|
| 29 |
+
name: Install & Build
|
| 30 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 31 |
+
if: github.event_name != 'pull_request_review' || startsWith(github.event.pull_request.base.ref, 'release/')
|
| 32 |
+
steps:
|
| 33 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 34 |
+
|
| 35 |
+
- name: Setup and Build
|
| 36 |
+
uses: ./.github/actions/setup-nodejs
|
| 37 |
+
|
| 38 |
+
sqlite-pooled:
|
| 39 |
+
name: SQLite Pooled
|
| 40 |
+
needs: build
|
| 41 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 42 |
+
timeout-minutes: 20
|
| 43 |
+
env:
|
| 44 |
+
DB_TYPE: sqlite
|
| 45 |
+
DB_SQLITE_POOL_SIZE: 4
|
| 46 |
+
steps:
|
| 47 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 48 |
+
|
| 49 |
+
- name: Setup and Build
|
| 50 |
+
uses: ./.github/actions/setup-nodejs
|
| 51 |
+
|
| 52 |
+
- name: Test SQLite Pooled
|
| 53 |
+
working-directory: packages/cli
|
| 54 |
+
run: pnpm test:sqlite
|
| 55 |
+
|
| 56 |
+
mariadb:
|
| 57 |
+
name: MariaDB
|
| 58 |
+
needs: build
|
| 59 |
+
runs-on: blacksmith-4vcpu-ubuntu-2204
|
| 60 |
+
timeout-minutes: 30
|
| 61 |
+
if: false
|
| 62 |
+
env:
|
| 63 |
+
DB_MYSQLDB_PASSWORD: password
|
| 64 |
+
DB_MYSQLDB_POOL_SIZE: 1
|
| 65 |
+
DB_MYSQLDB_CONNECTION_TIMEOUT: 120000
|
| 66 |
+
DB_MYSQLDB_ACQUIRE_TIMEOUT: 120000
|
| 67 |
+
DB_MYSQLDB_TIMEOUT: 120000
|
| 68 |
+
NODE_OPTIONS: '--max-old-space-size=7168'
|
| 69 |
+
steps:
|
| 70 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 71 |
+
|
| 72 |
+
- name: Setup and Build
|
| 73 |
+
uses: ./.github/actions/setup-nodejs
|
| 74 |
+
|
| 75 |
+
- name: Start MariaDB
|
| 76 |
+
uses: isbang/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
|
| 77 |
+
with:
|
| 78 |
+
compose-file: ./.github/docker-compose.yml
|
| 79 |
+
services: |
|
| 80 |
+
mariadb
|
| 81 |
+
|
| 82 |
+
- name: Test MariaDB
|
| 83 |
+
working-directory: packages/cli
|
| 84 |
+
run: pnpm test:mariadb --testTimeout 120000
|
| 85 |
+
|
| 86 |
+
mysql:
|
| 87 |
+
name: MySQL 8.4
|
| 88 |
+
needs: build
|
| 89 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 90 |
+
timeout-minutes: 20
|
| 91 |
+
if: false
|
| 92 |
+
env:
|
| 93 |
+
DB_MYSQLDB_PASSWORD: password
|
| 94 |
+
DB_MYSQLDB_POOL_SIZE: 1
|
| 95 |
+
DB_MYSQLDB_CONNECTION_TIMEOUT: 120000
|
| 96 |
+
DB_MYSQLDB_ACQUIRE_TIMEOUT: 120000
|
| 97 |
+
DB_MYSQLDB_TIMEOUT: 120000
|
| 98 |
+
steps:
|
| 99 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 100 |
+
|
| 101 |
+
- name: Setup and Build
|
| 102 |
+
uses: ./.github/actions/setup-nodejs
|
| 103 |
+
|
| 104 |
+
- name: Start MySQL
|
| 105 |
+
uses: isbang/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
|
| 106 |
+
with:
|
| 107 |
+
compose-file: ./.github/docker-compose.yml
|
| 108 |
+
services: mysql-8.4
|
| 109 |
+
|
| 110 |
+
- name: Test MySQL
|
| 111 |
+
working-directory: packages/cli
|
| 112 |
+
# We sleep here due to flakiness with DB tests if we connect to the database too soon
|
| 113 |
+
run: sleep 2s && pnpm test:mysql --testTimeout 120000
|
| 114 |
+
|
| 115 |
+
postgres:
|
| 116 |
+
name: Postgres
|
| 117 |
+
needs: build
|
| 118 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 119 |
+
timeout-minutes: 20
|
| 120 |
+
env:
|
| 121 |
+
DB_POSTGRESDB_PASSWORD: password
|
| 122 |
+
DB_POSTGRESDB_POOL_SIZE: 1 # Detect connection pooling deadlocks
|
| 123 |
+
steps:
|
| 124 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 125 |
+
|
| 126 |
+
- name: Setup and Build
|
| 127 |
+
uses: ./.github/actions/setup-nodejs
|
| 128 |
+
|
| 129 |
+
- name: Start Postgres
|
| 130 |
+
uses: isbang/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
|
| 131 |
+
with:
|
| 132 |
+
compose-file: ./.github/docker-compose.yml
|
| 133 |
+
services: |
|
| 134 |
+
postgres
|
| 135 |
+
|
| 136 |
+
- name: Test Postgres
|
| 137 |
+
working-directory: packages/cli
|
| 138 |
+
run: pnpm test:postgres
|
| 139 |
+
|
| 140 |
+
notify-on-failure:
|
| 141 |
+
name: Notify Slack on failure
|
| 142 |
+
runs-on: ubuntu-latest
|
| 143 |
+
needs: [sqlite-pooled, postgres]
|
| 144 |
+
steps:
|
| 145 |
+
- name: Notify Slack on failure
|
| 146 |
+
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
| 147 |
+
if: failure() && github.ref == 'refs/heads/master'
|
| 148 |
+
with:
|
| 149 |
+
status: ${{ job.status }}
|
| 150 |
+
channel: '#alerts-build'
|
| 151 |
+
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
| 152 |
+
message: Postgres, MariaDB or MySQL tests failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
.github/workflows/ci-pull-requests.yml
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Build, unit test and lint branch
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request:
|
| 5 |
+
merge_group:
|
| 6 |
+
|
| 7 |
+
concurrency:
|
| 8 |
+
group: ci-${{ github.event.pull_request.number || github.event.merge_group.head_sha || github.ref }}
|
| 9 |
+
cancel-in-progress: true
|
| 10 |
+
|
| 11 |
+
env:
|
| 12 |
+
COVERAGE_ENABLED: 'true' # Set globally for all jobs - ensures Turbo cache consistency
|
| 13 |
+
|
| 14 |
+
jobs:
|
| 15 |
+
install-and-build:
|
| 16 |
+
name: Install & Build
|
| 17 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 18 |
+
env:
|
| 19 |
+
NODE_OPTIONS: '--max-old-space-size=6144'
|
| 20 |
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
| 21 |
+
outputs:
|
| 22 |
+
non_python_changed: ${{ steps.paths-filter.outputs.non-python == 'true' }}
|
| 23 |
+
workflows_changed: ${{ steps.paths-filter.outputs.workflows == 'true' }}
|
| 24 |
+
commit_sha: ${{ steps.commit-sha.outputs.sha }}
|
| 25 |
+
steps:
|
| 26 |
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 27 |
+
with:
|
| 28 |
+
# Use merge_group SHA when in merge queue, otherwise PR merge ref
|
| 29 |
+
ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || format('refs/pull/{0}/merge', github.event.pull_request.number) }}
|
| 30 |
+
|
| 31 |
+
- name: Capture commit SHA for cache consistency
|
| 32 |
+
id: commit-sha
|
| 33 |
+
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
| 34 |
+
|
| 35 |
+
- name: Check for relevant changes
|
| 36 |
+
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
| 37 |
+
id: paths-filter
|
| 38 |
+
with:
|
| 39 |
+
filters: |
|
| 40 |
+
non-python:
|
| 41 |
+
- '**'
|
| 42 |
+
- '!packages/@n8n/task-runner-python/**'
|
| 43 |
+
workflows:
|
| 44 |
+
- .github/**
|
| 45 |
+
- name: Setup and Build
|
| 46 |
+
if: steps.paths-filter.outputs.non-python == 'true'
|
| 47 |
+
uses: ./.github/actions/setup-nodejs
|
| 48 |
+
|
| 49 |
+
- name: Run format check
|
| 50 |
+
if: steps.paths-filter.outputs.non-python == 'true'
|
| 51 |
+
run: pnpm format:check
|
| 52 |
+
|
| 53 |
+
unit-test:
|
| 54 |
+
name: Unit tests
|
| 55 |
+
if: needs.install-and-build.outputs.non_python_changed == 'true'
|
| 56 |
+
uses: ./.github/workflows/units-tests-reusable.yml
|
| 57 |
+
needs: install-and-build
|
| 58 |
+
with:
|
| 59 |
+
ref: ${{ needs.install-and-build.outputs.commit_sha }}
|
| 60 |
+
collectCoverage: true
|
| 61 |
+
secrets:
|
| 62 |
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
| 63 |
+
|
| 64 |
+
typecheck:
|
| 65 |
+
name: Typecheck
|
| 66 |
+
if: needs.install-and-build.outputs.non_python_changed == 'true'
|
| 67 |
+
runs-on: blacksmith-4vcpu-ubuntu-2204
|
| 68 |
+
needs: install-and-build
|
| 69 |
+
steps:
|
| 70 |
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 71 |
+
with:
|
| 72 |
+
ref: ${{ needs.install-and-build.outputs.commit_sha }}
|
| 73 |
+
|
| 74 |
+
- name: Setup Node.js
|
| 75 |
+
uses: ./.github/actions/setup-nodejs
|
| 76 |
+
with:
|
| 77 |
+
build-command: pnpm typecheck
|
| 78 |
+
|
| 79 |
+
lint:
|
| 80 |
+
name: Lint
|
| 81 |
+
if: needs.install-and-build.outputs.non_python_changed == 'true'
|
| 82 |
+
uses: ./.github/workflows/linting-reusable.yml
|
| 83 |
+
needs: install-and-build
|
| 84 |
+
with:
|
| 85 |
+
ref: ${{ needs.install-and-build.outputs.commit_sha }}
|
| 86 |
+
|
| 87 |
+
e2e-tests:
|
| 88 |
+
name: E2E Tests
|
| 89 |
+
needs: install-and-build
|
| 90 |
+
if: needs.install-and-build.outputs.non_python_changed == 'true'
|
| 91 |
+
uses: ./.github/workflows/playwright-test-ci.yml
|
| 92 |
+
with:
|
| 93 |
+
branch: ${{ needs.install-and-build.outputs.commit_sha }}
|
| 94 |
+
secrets: inherit
|
| 95 |
+
|
| 96 |
+
security-checks:
|
| 97 |
+
name: Security Checks
|
| 98 |
+
needs: install-and-build
|
| 99 |
+
if: needs.install-and-build.outputs.workflows_changed == 'true'
|
| 100 |
+
uses: ./.github/workflows/ci-security.yml
|
| 101 |
+
with:
|
| 102 |
+
ref: ${{ needs.install-and-build.outputs.commit_sha }}
|
| 103 |
+
secrets: inherit
|
| 104 |
+
|
| 105 |
+
# This job is required by GitHub branch protection rules.
|
| 106 |
+
# PRs cannot be merged unless this job passes.
|
| 107 |
+
# If you add/remove jobs that should block merging, update the 'needs' array
|
| 108 |
+
# and the skip conditions in the 'if' block below.
|
| 109 |
+
required-checks:
|
| 110 |
+
name: Required Checks
|
| 111 |
+
needs: [install-and-build, unit-test, typecheck, lint, e2e-tests, security-checks]
|
| 112 |
+
if: always()
|
| 113 |
+
runs-on: ubuntu-slim
|
| 114 |
+
steps:
|
| 115 |
+
- name: Fail if any required job failed or was skipped unexpectedly
|
| 116 |
+
# Explicit checks for each job's skip conditions:
|
| 117 |
+
# - Non-python jobs (unit-test, typecheck, lint, e2e-tests): can skip when non_python_changed == false
|
| 118 |
+
# - security-checks: can skip when workflows_changed == false
|
| 119 |
+
if: |
|
| 120 |
+
contains(needs.*.result, 'failure') ||
|
| 121 |
+
(needs.install-and-build.outputs.non_python_changed == 'true' && (
|
| 122 |
+
needs.unit-test.result == 'skipped' ||
|
| 123 |
+
needs.typecheck.result == 'skipped' ||
|
| 124 |
+
needs.lint.result == 'skipped' ||
|
| 125 |
+
needs.e2e-tests.result == 'skipped'
|
| 126 |
+
)) ||
|
| 127 |
+
(needs.install-and-build.outputs.workflows_changed == 'true' &&
|
| 128 |
+
needs.security-checks.result == 'skipped')
|
| 129 |
+
run: exit 1
|
.github/workflows/ci-python-workflow-builder-evals.yml
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: AI Workflow Builder Evals Python CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request:
|
| 5 |
+
paths:
|
| 6 |
+
- packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python/**
|
| 7 |
+
- .github/workflows/ci-python-workflow-builder-evals.yml
|
| 8 |
+
push:
|
| 9 |
+
paths:
|
| 10 |
+
- packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python/**
|
| 11 |
+
|
| 12 |
+
jobs:
|
| 13 |
+
workflow-comparison:
|
| 14 |
+
name: Workflow Comparison Python
|
| 15 |
+
runs-on: ubuntu-latest
|
| 16 |
+
defaults:
|
| 17 |
+
run:
|
| 18 |
+
working-directory: packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python
|
| 19 |
+
steps:
|
| 20 |
+
- name: Check out project
|
| 21 |
+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 22 |
+
|
| 23 |
+
- name: Install uv
|
| 24 |
+
uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # 6.5.0
|
| 25 |
+
with:
|
| 26 |
+
enable-cache: true
|
| 27 |
+
|
| 28 |
+
- name: Install just
|
| 29 |
+
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3.0.0
|
| 30 |
+
|
| 31 |
+
- name: Install Python
|
| 32 |
+
run: uv python install 3.11
|
| 33 |
+
|
| 34 |
+
- name: Install project dependencies
|
| 35 |
+
run: just sync-all
|
| 36 |
+
|
| 37 |
+
- name: Format check
|
| 38 |
+
run: just format-check
|
| 39 |
+
|
| 40 |
+
- name: Typecheck
|
| 41 |
+
run: just typecheck
|
| 42 |
+
|
| 43 |
+
- name: Lint
|
| 44 |
+
run: just lint
|
| 45 |
+
|
| 46 |
+
- name: Python unit tests
|
| 47 |
+
run: uv run pytest --cov=src --cov-report=xml --cov-report=term-missing
|
| 48 |
+
|
| 49 |
+
- name: Upload coverage to Codecov
|
| 50 |
+
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
|
| 51 |
+
with:
|
| 52 |
+
token: ${{ secrets.CODECOV_TOKEN }}
|
| 53 |
+
files: packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python/coverage.xml
|
| 54 |
+
flags: tests
|
| 55 |
+
name: workflow-comparison-python
|
| 56 |
+
fail_ci_if_error: false
|
.github/workflows/ci-python.yml
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Python CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request:
|
| 5 |
+
paths:
|
| 6 |
+
- packages/@n8n/task-runner-python/**
|
| 7 |
+
- .github/workflows/ci-python.yml
|
| 8 |
+
push:
|
| 9 |
+
paths:
|
| 10 |
+
- packages/@n8n/task-runner-python/**
|
| 11 |
+
|
| 12 |
+
jobs:
|
| 13 |
+
checks:
|
| 14 |
+
name: Checks
|
| 15 |
+
runs-on: ubuntu-latest
|
| 16 |
+
defaults:
|
| 17 |
+
run:
|
| 18 |
+
working-directory: packages/@n8n/task-runner-python
|
| 19 |
+
steps:
|
| 20 |
+
- name: Check out project
|
| 21 |
+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 22 |
+
|
| 23 |
+
- name: Install uv
|
| 24 |
+
uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # 6.5.0
|
| 25 |
+
with:
|
| 26 |
+
enable-cache: true
|
| 27 |
+
|
| 28 |
+
- name: Install just
|
| 29 |
+
uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3.0.0
|
| 30 |
+
|
| 31 |
+
- name: Install Python
|
| 32 |
+
run: uv python install 3.13
|
| 33 |
+
|
| 34 |
+
- name: Install project dependencies
|
| 35 |
+
run: just sync-all
|
| 36 |
+
|
| 37 |
+
- name: Format check
|
| 38 |
+
run: just format-check
|
| 39 |
+
|
| 40 |
+
- name: Typecheck
|
| 41 |
+
run: just typecheck
|
| 42 |
+
|
| 43 |
+
- name: Lint
|
| 44 |
+
run: just lint
|
| 45 |
+
|
| 46 |
+
- name: Python unit tests
|
| 47 |
+
run: uv run pytest --cov=src --cov-report=xml --cov-report=term-missing
|
| 48 |
+
|
| 49 |
+
- name: Upload coverage to Codecov
|
| 50 |
+
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
|
| 51 |
+
with:
|
| 52 |
+
token: ${{ secrets.CODECOV_TOKEN }}
|
| 53 |
+
files: packages/@n8n/task-runner-python/coverage.xml
|
| 54 |
+
flags: tests
|
| 55 |
+
name: task-runner-python
|
| 56 |
+
fail_ci_if_error: false
|
.github/workflows/ci-security.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Security Checks
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_call:
|
| 5 |
+
inputs:
|
| 6 |
+
ref:
|
| 7 |
+
description: GitHub ref to scan.
|
| 8 |
+
required: false
|
| 9 |
+
type: string
|
| 10 |
+
default: ''
|
| 11 |
+
|
| 12 |
+
jobs:
|
| 13 |
+
poutine-scan:
|
| 14 |
+
name: Poutine Security Scan
|
| 15 |
+
uses: ./.github/workflows/security-poutine-scan-callable.yml
|
| 16 |
+
with:
|
| 17 |
+
ref: ${{ inputs.ref }}
|
| 18 |
+
secrets: inherit
|
| 19 |
+
|
| 20 |
+
# Future security checks can be added here:
|
| 21 |
+
# - dependency-scan:
|
| 22 |
+
# - secret-detection:
|
| 23 |
+
# - container-scan:
|
.github/workflows/claude.yml
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Claude PR Assistant
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
issue_comment:
|
| 5 |
+
types: [created]
|
| 6 |
+
pull_request_review_comment:
|
| 7 |
+
types: [created]
|
| 8 |
+
issues:
|
| 9 |
+
types: [opened, assigned]
|
| 10 |
+
pull_request_review:
|
| 11 |
+
types: [submitted]
|
| 12 |
+
|
| 13 |
+
jobs:
|
| 14 |
+
claude-code-action:
|
| 15 |
+
if: |
|
| 16 |
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
| 17 |
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
| 18 |
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
| 19 |
+
(github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
|
| 20 |
+
runs-on: ubuntu-latest
|
| 21 |
+
permissions:
|
| 22 |
+
contents: read
|
| 23 |
+
pull-requests: read
|
| 24 |
+
issues: read
|
| 25 |
+
id-token: write
|
| 26 |
+
steps:
|
| 27 |
+
- name: Checkout repository
|
| 28 |
+
uses: actions/checkout@v4
|
| 29 |
+
with:
|
| 30 |
+
fetch-depth: 1
|
| 31 |
+
|
| 32 |
+
- name: Run Claude PR Action
|
| 33 |
+
uses: anthropics/claude-code-action@beta
|
| 34 |
+
with:
|
| 35 |
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
| 36 |
+
# Or use OAuth token instead:
|
| 37 |
+
# claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
| 38 |
+
timeout_minutes: '60'
|
| 39 |
+
# mode: tag # Default: responds to @claude mentions
|
| 40 |
+
# Optional: Restrict network access to specific domains only
|
| 41 |
+
# experimental_allowed_domains: |
|
| 42 |
+
# .anthropic.com
|
| 43 |
+
# .github.com
|
| 44 |
+
# api.github.com
|
| 45 |
+
# .githubusercontent.com
|
| 46 |
+
# bun.sh
|
| 47 |
+
# registry.npmjs.org
|
| 48 |
+
# .blob.core.windows.net
|
.github/workflows/create-patch-release-branch.yml
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Create Branch For Patch Release
|
| 2 |
+
on:
|
| 3 |
+
workflow_dispatch:
|
| 4 |
+
inputs:
|
| 5 |
+
commit_shas:
|
| 6 |
+
description: 'Comma-separated commit SHAs'
|
| 7 |
+
required: true
|
| 8 |
+
old_version:
|
| 9 |
+
description: 'Old version to be patched'
|
| 10 |
+
required: true
|
| 11 |
+
default: '1.0.0'
|
| 12 |
+
new_version:
|
| 13 |
+
description: 'The new patch version'
|
| 14 |
+
required: true
|
| 15 |
+
default: '1.0.1'
|
| 16 |
+
resumeUrl:
|
| 17 |
+
description: 'n8n workflow resume URL'
|
| 18 |
+
required: true
|
| 19 |
+
jobs:
|
| 20 |
+
create-branch:
|
| 21 |
+
runs-on: ubuntu-latest
|
| 22 |
+
permissions:
|
| 23 |
+
contents: write
|
| 24 |
+
pull-requests: write
|
| 25 |
+
steps:
|
| 26 |
+
- uses: actions/checkout@v4
|
| 27 |
+
with:
|
| 28 |
+
fetch-depth: 0
|
| 29 |
+
- name: Validate inputs
|
| 30 |
+
run: |
|
| 31 |
+
if ! [[ "${{ inputs.old_version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then
|
| 32 |
+
echo "Invalid old version format: ${{ inputs.old_version }}"
|
| 33 |
+
exit 1
|
| 34 |
+
fi
|
| 35 |
+
if ! [[ "${{ inputs.new_version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then
|
| 36 |
+
echo "Invalid new version format: ${{ inputs.new_version }}"
|
| 37 |
+
exit 1
|
| 38 |
+
fi
|
| 39 |
+
- name: Notify if inputs are invalid
|
| 40 |
+
if: ${{ failure() }}
|
| 41 |
+
run: |
|
| 42 |
+
curl -X POST -H "Content-Type: application/json" -d '{ "success": false, "message": "The old or new version you provided is invalid, make sure they both follow the SemVer format" }' ${{ inputs.resumeUrl }}
|
| 43 |
+
exit 1
|
| 44 |
+
- name: Setup, cherry-pick and push branch
|
| 45 |
+
run: |
|
| 46 |
+
git config user.name "github-actions[bot]"
|
| 47 |
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
| 48 |
+
git switch "n8n@${{ inputs.old_version }}" --detach
|
| 49 |
+
BRANCH="patch/${{ inputs.new_version }}"
|
| 50 |
+
git checkout -b "$BRANCH"
|
| 51 |
+
IFS=',' read -ra SHAS <<< "${{ inputs.commit_shas }}"
|
| 52 |
+
for sha in "${SHAS[@]}"; do
|
| 53 |
+
sha=$(echo "$sha" | xargs)
|
| 54 |
+
if ! git merge-base --is-ancestor "$sha" HEAD; then
|
| 55 |
+
echo "Cherry-picking commit $sha"
|
| 56 |
+
git cherry-pick "$sha"
|
| 57 |
+
else
|
| 58 |
+
echo "Commit $sha is already in the branch, skipping"
|
| 59 |
+
fi
|
| 60 |
+
done
|
| 61 |
+
git push -f origin "$BRANCH"
|
| 62 |
+
- name: Notify if cherry-pick is successful
|
| 63 |
+
if: ${{ success() }}
|
| 64 |
+
run: |
|
| 65 |
+
curl -X POST -H "Content-Type: application/json" -d '{ "success": true }' ${{ inputs.resumeUrl }}
|
| 66 |
+
- name: Notify if cherry-pick is not successful
|
| 67 |
+
if: ${{ failure() }}
|
| 68 |
+
run: |
|
| 69 |
+
curl -X POST -H "Content-Type: application/json" -d '{ "success": false, "message": "There was a conflict when trying to create the branch, please do the cherry-pick and resolve the conflicts manually or do not include the PRs that caused the conflict" }' ${{ inputs.resumeUrl }}
|
.github/workflows/data-tooling.yml
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Data tooling, test exports and imports of data to various db types
|
| 2 |
+
|
| 3 |
+
# TODO: Uncomment this after it works on a manual invocation
|
| 4 |
+
# on:
|
| 5 |
+
# pull_request:
|
| 6 |
+
# branches:
|
| 7 |
+
# - '**'
|
| 8 |
+
# - '!release/*'
|
| 9 |
+
|
| 10 |
+
on:
|
| 11 |
+
workflow_dispatch:
|
| 12 |
+
|
| 13 |
+
jobs:
|
| 14 |
+
sqlite-export-sqlite-import:
|
| 15 |
+
name: sqlite database export -> sqlite database import
|
| 16 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 17 |
+
outputs:
|
| 18 |
+
db_changed: ${{ steps.paths-filter.outputs.db == 'true' }}
|
| 19 |
+
steps:
|
| 20 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 21 |
+
# with:
|
| 22 |
+
# ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
| 23 |
+
|
| 24 |
+
- name: Check for frontend changes
|
| 25 |
+
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
| 26 |
+
id: paths-filter
|
| 27 |
+
with:
|
| 28 |
+
filters: |
|
| 29 |
+
db:
|
| 30 |
+
- packages/@n8n/db/**
|
| 31 |
+
- packages/cli/**
|
| 32 |
+
- name: Setup, build and export sqlite
|
| 33 |
+
if: steps.paths-filter.outputs.db == 'true'
|
| 34 |
+
uses: ./.github/actions/setup-nodejs
|
| 35 |
+
with:
|
| 36 |
+
build-command: |
|
| 37 |
+
pnpm build
|
| 38 |
+
./packages/cli/bin/n8n export:entities --outputDir packages/cli/commands/export/outputs
|
| 39 |
+
./packages/cli/bin/n8n import:entities --inputDir packages/cli/commands/export/outputs --truncateTables
|
| 40 |
+
postgres-export-postgres-import:
|
| 41 |
+
name: postgres export -> postgres import
|
| 42 |
+
runs-on: blacksmith-2vcpu-ubuntu-2204
|
| 43 |
+
outputs:
|
| 44 |
+
db_changed: ${{ steps.paths-filter.outputs.db == 'true' }}
|
| 45 |
+
env:
|
| 46 |
+
DB_TYPE: postgresdb
|
| 47 |
+
DB_POSTGRESDB_DATABASE: n8n
|
| 48 |
+
DB_POSTGRESDB_HOST: localhost
|
| 49 |
+
DB_POSTGRESDB_PORT: 5432
|
| 50 |
+
DB_POSTGRESDB_USER: postgres
|
| 51 |
+
DB_POSTGRESDB_PASSWORD: password
|
| 52 |
+
DB_POSTGRESDB_POOL_SIZE: 1 # Detect connection pooling deadlocks
|
| 53 |
+
steps:
|
| 54 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 55 |
+
# with:
|
| 56 |
+
# ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
| 57 |
+
|
| 58 |
+
- name: Check for db changes
|
| 59 |
+
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
| 60 |
+
id: paths-filter
|
| 61 |
+
with:
|
| 62 |
+
filters: |
|
| 63 |
+
db:
|
| 64 |
+
- packages/@n8n/db/**
|
| 65 |
+
- packages/cli/**
|
| 66 |
+
|
| 67 |
+
- name: Setup and Build
|
| 68 |
+
if: steps.paths-filter.outputs.db == 'true'
|
| 69 |
+
uses: ./.github/actions/setup-nodejs
|
| 70 |
+
|
| 71 |
+
- name: Start Postgres
|
| 72 |
+
if: steps.paths-filter.outputs.db == 'true'
|
| 73 |
+
uses: isbang/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
|
| 74 |
+
with:
|
| 75 |
+
compose-file: ./.github/docker-compose.yml
|
| 76 |
+
services: |
|
| 77 |
+
postgres
|
| 78 |
+
|
| 79 |
+
- name: Export postgres
|
| 80 |
+
if: steps.paths-filter.outputs.db == 'true'
|
| 81 |
+
run: ./packages/cli/bin/n8n export:entities --outputDir packages/cli/commands/export/outputs
|
| 82 |
+
- name: Import postgres
|
| 83 |
+
if: steps.paths-filter.outputs.db == 'true'
|
| 84 |
+
run: ./packages/cli/bin/n8n import:entities --inputDir packages/cli/commands/export/outputs --truncateTables
|
.github/workflows/docker-base-image.yml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Docker Base Image CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- master
|
| 7 |
+
paths:
|
| 8 |
+
- 'docker/images/n8n-base/Dockerfile'
|
| 9 |
+
pull_request:
|
| 10 |
+
paths:
|
| 11 |
+
- 'docker/images/n8n-base/Dockerfile'
|
| 12 |
+
workflow_dispatch:
|
| 13 |
+
inputs:
|
| 14 |
+
push:
|
| 15 |
+
description: 'Push to registries'
|
| 16 |
+
required: false
|
| 17 |
+
default: false
|
| 18 |
+
type: boolean
|
| 19 |
+
|
| 20 |
+
jobs:
|
| 21 |
+
build:
|
| 22 |
+
runs-on: ubuntu-latest
|
| 23 |
+
strategy:
|
| 24 |
+
matrix:
|
| 25 |
+
node_version: ['20', '22.21.1', '24']
|
| 26 |
+
steps:
|
| 27 |
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 28 |
+
|
| 29 |
+
- name: Set up QEMU
|
| 30 |
+
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
| 31 |
+
|
| 32 |
+
- name: Set up Docker Buildx
|
| 33 |
+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
| 34 |
+
|
| 35 |
+
- name: Login to DHI Registry (for pulling base images)
|
| 36 |
+
uses: ./.github/actions/docker-registry-login
|
| 37 |
+
with:
|
| 38 |
+
login-ghcr: 'false'
|
| 39 |
+
login-dhi: 'true'
|
| 40 |
+
dockerhub-username: ${{ secrets.DOCKER_USERNAME }}
|
| 41 |
+
dockerhub-password: ${{ secrets.DOCKER_PASSWORD }}
|
| 42 |
+
|
| 43 |
+
- name: Login to Docker registries (for pushing)
|
| 44 |
+
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.push == true)
|
| 45 |
+
uses: ./.github/actions/docker-registry-login
|
| 46 |
+
with:
|
| 47 |
+
login-ghcr: 'true'
|
| 48 |
+
login-dockerhub: 'true'
|
| 49 |
+
dockerhub-username: ${{ secrets.DOCKER_USERNAME }}
|
| 50 |
+
dockerhub-password: ${{ secrets.DOCKER_PASSWORD }}
|
| 51 |
+
|
| 52 |
+
- name: Build and push
|
| 53 |
+
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
| 54 |
+
with:
|
| 55 |
+
context: .
|
| 56 |
+
file: ./docker/images/n8n-base/Dockerfile
|
| 57 |
+
build-args: |
|
| 58 |
+
NODE_VERSION=${{ matrix.node_version }}
|
| 59 |
+
platforms: linux/amd64,linux/arm64
|
| 60 |
+
provenance: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.push == true) }}
|
| 61 |
+
sbom: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.push == true) }}
|
| 62 |
+
push: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.push == true) }}
|
| 63 |
+
tags: |
|
| 64 |
+
${{ secrets.DOCKER_USERNAME }}/base:${{ matrix.node_version }}-${{ github.sha }}
|
| 65 |
+
${{ secrets.DOCKER_USERNAME }}/base:${{ matrix.node_version }}
|
| 66 |
+
ghcr.io/${{ github.repository_owner }}/base:${{ matrix.node_version }}-${{ github.sha }}
|
| 67 |
+
ghcr.io/${{ github.repository_owner }}/base:${{ matrix.node_version }}
|
| 68 |
+
no-cache: true
|
.github/workflows/docker-build-push.yml
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This workflow is used to build and push the Docker image for n8nio/n8n and n8nio/runners
|
| 2 |
+
#
|
| 3 |
+
# - Uses docker-config.mjs for context determination, this determines what needs to be built based on the trigger
|
| 4 |
+
# - Uses docker-tags.mjs for tag generation, this generates the tags for the images
|
| 5 |
+
|
| 6 |
+
name: 'Docker: Build and Push'
|
| 7 |
+
|
| 8 |
+
env:
|
| 9 |
+
NODE_OPTIONS: '--max-old-space-size=7168'
|
| 10 |
+
NODE_VERSION: '22.21.1'
|
| 11 |
+
|
| 12 |
+
on:
|
| 13 |
+
schedule:
|
| 14 |
+
- cron: '0 0 * * *'
|
| 15 |
+
|
| 16 |
+
workflow_call:
|
| 17 |
+
inputs:
|
| 18 |
+
n8n_version:
|
| 19 |
+
description: 'N8N version to build'
|
| 20 |
+
required: true
|
| 21 |
+
type: string
|
| 22 |
+
release_type:
|
| 23 |
+
description: 'Release type (stable, nightly, dev)'
|
| 24 |
+
required: false
|
| 25 |
+
type: string
|
| 26 |
+
default: 'stable'
|
| 27 |
+
push_enabled:
|
| 28 |
+
description: 'Whether to push the built images'
|
| 29 |
+
required: false
|
| 30 |
+
type: boolean
|
| 31 |
+
default: true
|
| 32 |
+
|
| 33 |
+
workflow_dispatch:
|
| 34 |
+
inputs:
|
| 35 |
+
push_enabled:
|
| 36 |
+
description: 'Push image to registry'
|
| 37 |
+
required: false
|
| 38 |
+
type: boolean
|
| 39 |
+
default: true
|
| 40 |
+
success_url:
|
| 41 |
+
description: 'URL to call after the build is successful'
|
| 42 |
+
required: false
|
| 43 |
+
type: string
|
| 44 |
+
|
| 45 |
+
jobs:
|
| 46 |
+
determine-build-context:
|
| 47 |
+
name: Determine Build Context
|
| 48 |
+
runs-on: ubuntu-latest
|
| 49 |
+
outputs:
|
| 50 |
+
release_type: ${{ steps.context.outputs.release_type }}
|
| 51 |
+
n8n_version: ${{ steps.context.outputs.version }}
|
| 52 |
+
push_enabled: ${{ steps.context.outputs.push_enabled }}
|
| 53 |
+
push_to_docker: ${{ steps.context.outputs.push_to_docker }}
|
| 54 |
+
build_matrix: ${{ steps.context.outputs.build_matrix }}
|
| 55 |
+
steps:
|
| 56 |
+
- name: Checkout code
|
| 57 |
+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 58 |
+
|
| 59 |
+
- name: Determine build context
|
| 60 |
+
id: context
|
| 61 |
+
run: |
|
| 62 |
+
node .github/scripts/docker/docker-config.mjs \
|
| 63 |
+
--event "${{ github.event_name }}" \
|
| 64 |
+
--pr "${{ github.event.pull_request.number }}" \
|
| 65 |
+
--branch "${{ github.ref_name }}" \
|
| 66 |
+
--version "${{ inputs.n8n_version }}" \
|
| 67 |
+
--release-type "${{ inputs.release_type }}" \
|
| 68 |
+
--push-enabled "${{ inputs.push_enabled }}"
|
| 69 |
+
|
| 70 |
+
build-and-push-docker:
|
| 71 |
+
name: Build App, then Build and Push Docker Image (${{ matrix.platform }})
|
| 72 |
+
needs: determine-build-context
|
| 73 |
+
runs-on: ${{ matrix.runner }}
|
| 74 |
+
timeout-minutes: 25
|
| 75 |
+
strategy:
|
| 76 |
+
matrix: ${{ fromJSON(needs.determine-build-context.outputs.build_matrix) }}
|
| 77 |
+
outputs:
|
| 78 |
+
image_ref: ${{ steps.determine-tags.outputs.n8n_primary_tag }}
|
| 79 |
+
primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.n8n_primary_tag }}
|
| 80 |
+
runners_primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.runners_primary_tag }}
|
| 81 |
+
runners_distroless_primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.runners_distroless_primary_tag }}
|
| 82 |
+
steps:
|
| 83 |
+
- name: Checkout code
|
| 84 |
+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 85 |
+
with:
|
| 86 |
+
fetch-depth: 0
|
| 87 |
+
|
| 88 |
+
- name: Setup and Build
|
| 89 |
+
uses: ./.github/actions/setup-nodejs
|
| 90 |
+
with:
|
| 91 |
+
build-command: pnpm build:n8n
|
| 92 |
+
enable-docker-cache: 'true'
|
| 93 |
+
|
| 94 |
+
- name: Determine Docker tags for all images
|
| 95 |
+
id: determine-tags
|
| 96 |
+
run: |
|
| 97 |
+
node .github/scripts/docker/docker-tags.mjs \
|
| 98 |
+
--all \
|
| 99 |
+
--version "${{ needs.determine-build-context.outputs.n8n_version }}" \
|
| 100 |
+
--platform "${{ matrix.docker_platform }}" \
|
| 101 |
+
${{ needs.determine-build-context.outputs.push_to_docker == 'true' && '--include-docker' || '' }}
|
| 102 |
+
|
| 103 |
+
echo "=== Generated Docker Tags ==="
|
| 104 |
+
cat "$GITHUB_OUTPUT" | grep "_tags=" | while IFS='=' read -r key value; do
|
| 105 |
+
echo "${key}: ${value%%,*}..." # Show first tag for brevity
|
| 106 |
+
done
|
| 107 |
+
|
| 108 |
+
- name: Login to Docker registries
|
| 109 |
+
if: needs.determine-build-context.outputs.push_enabled == 'true'
|
| 110 |
+
uses: ./.github/actions/docker-registry-login
|
| 111 |
+
with:
|
| 112 |
+
login-ghcr: true
|
| 113 |
+
login-dockerhub: ${{ needs.determine-build-context.outputs.push_to_docker == 'true' }}
|
| 114 |
+
dockerhub-username: ${{ secrets.DOCKER_USERNAME }}
|
| 115 |
+
dockerhub-password: ${{ secrets.DOCKER_PASSWORD }}
|
| 116 |
+
|
| 117 |
+
- name: Build and push n8n Docker image
|
| 118 |
+
uses: useblacksmith/build-push-action@30c71162f16ea2c27c3e21523255d209b8b538c1 # v2
|
| 119 |
+
with:
|
| 120 |
+
context: .
|
| 121 |
+
file: ./docker/images/n8n/Dockerfile
|
| 122 |
+
build-args: |
|
| 123 |
+
NODE_VERSION=${{ env.NODE_VERSION }}
|
| 124 |
+
N8N_VERSION=${{ needs.determine-build-context.outputs.n8n_version }}
|
| 125 |
+
N8N_RELEASE_TYPE=${{ needs.determine-build-context.outputs.release_type }}
|
| 126 |
+
platforms: ${{ matrix.docker_platform }}
|
| 127 |
+
provenance: true
|
| 128 |
+
sbom: true
|
| 129 |
+
push: ${{ needs.determine-build-context.outputs.push_enabled == 'true' }}
|
| 130 |
+
tags: ${{ steps.determine-tags.outputs.n8n_tags }}
|
| 131 |
+
|
| 132 |
+
- name: Build and push task runners Docker image (Alpine)
|
| 133 |
+
uses: useblacksmith/build-push-action@30c71162f16ea2c27c3e21523255d209b8b538c1 # v2
|
| 134 |
+
with:
|
| 135 |
+
context: .
|
| 136 |
+
file: ./docker/images/runners/Dockerfile
|
| 137 |
+
build-args: |
|
| 138 |
+
NODE_VERSION=${{ env.NODE_VERSION }}
|
| 139 |
+
N8N_VERSION=${{ needs.determine-build-context.outputs.n8n_version }}
|
| 140 |
+
N8N_RELEASE_TYPE=${{ needs.determine-build-context.outputs.release_type }}
|
| 141 |
+
platforms: ${{ matrix.docker_platform }}
|
| 142 |
+
provenance: true
|
| 143 |
+
sbom: true
|
| 144 |
+
push: ${{ needs.determine-build-context.outputs.push_enabled == 'true' }}
|
| 145 |
+
tags: ${{ steps.determine-tags.outputs.runners_tags }}
|
| 146 |
+
|
| 147 |
+
- name: Build and push task runners Docker image (distroless)
|
| 148 |
+
uses: useblacksmith/build-push-action@30c71162f16ea2c27c3e21523255d209b8b538c1 # v2
|
| 149 |
+
with:
|
| 150 |
+
context: .
|
| 151 |
+
file: ./docker/images/runners/Dockerfile.distroless
|
| 152 |
+
build-args: |
|
| 153 |
+
NODE_VERSION=${{ env.NODE_VERSION }}
|
| 154 |
+
N8N_VERSION=${{ needs.determine-build-context.outputs.n8n_version }}
|
| 155 |
+
N8N_RELEASE_TYPE=${{ needs.determine-build-context.outputs.release_type }}
|
| 156 |
+
platforms: ${{ matrix.docker_platform }}
|
| 157 |
+
provenance: true
|
| 158 |
+
sbom: true
|
| 159 |
+
push: ${{ needs.determine-build-context.outputs.push_enabled == 'true' }}
|
| 160 |
+
tags: ${{ steps.determine-tags.outputs.runners_distroless_tags }}
|
| 161 |
+
|
| 162 |
+
create_multi_arch_manifest:
|
| 163 |
+
name: Create Multi-Arch Manifest
|
| 164 |
+
needs: [determine-build-context, build-and-push-docker]
|
| 165 |
+
runs-on: ubuntu-latest
|
| 166 |
+
if: |
|
| 167 |
+
needs.build-and-push-docker.result == 'success' &&
|
| 168 |
+
needs.determine-build-context.outputs.push_enabled == 'true'
|
| 169 |
+
steps:
|
| 170 |
+
- name: Checkout
|
| 171 |
+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 172 |
+
|
| 173 |
+
- name: Set up Docker Buildx
|
| 174 |
+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
| 175 |
+
|
| 176 |
+
- name: Login to Docker registries
|
| 177 |
+
uses: ./.github/actions/docker-registry-login
|
| 178 |
+
with:
|
| 179 |
+
login-ghcr: true
|
| 180 |
+
login-dockerhub: ${{ needs.determine-build-context.outputs.push_to_docker == 'true' }}
|
| 181 |
+
dockerhub-username: ${{ secrets.DOCKER_USERNAME }}
|
| 182 |
+
dockerhub-password: ${{ secrets.DOCKER_PASSWORD }}
|
| 183 |
+
|
| 184 |
+
- name: Create GHCR multi-arch manifests
|
| 185 |
+
run: |
|
| 186 |
+
RELEASE_TYPE="${{ needs.determine-build-context.outputs.release_type }}"
|
| 187 |
+
|
| 188 |
+
# Function to create manifest for an image
|
| 189 |
+
create_manifest() {
|
| 190 |
+
local IMAGE_NAME=$1
|
| 191 |
+
local MANIFEST_TAG=$2
|
| 192 |
+
|
| 193 |
+
if [[ -z "$MANIFEST_TAG" ]]; then
|
| 194 |
+
echo "Skipping $IMAGE_NAME - no manifest tag"
|
| 195 |
+
return
|
| 196 |
+
fi
|
| 197 |
+
|
| 198 |
+
echo "Creating GHCR manifest for $IMAGE_NAME: $MANIFEST_TAG"
|
| 199 |
+
|
| 200 |
+
# For branch builds, only AMD64 is built
|
| 201 |
+
if [[ "$RELEASE_TYPE" == "branch" ]]; then
|
| 202 |
+
docker buildx imagetools create \
|
| 203 |
+
--tag "$MANIFEST_TAG" \
|
| 204 |
+
"${MANIFEST_TAG}-amd64"
|
| 205 |
+
else
|
| 206 |
+
docker buildx imagetools create \
|
| 207 |
+
--tag "$MANIFEST_TAG" \
|
| 208 |
+
"${MANIFEST_TAG}-amd64" \
|
| 209 |
+
"${MANIFEST_TAG}-arm64"
|
| 210 |
+
fi
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
# Create manifests for all images
|
| 214 |
+
create_manifest "n8n" "${{ needs.build-and-push-docker.outputs.primary_ghcr_manifest_tag }}"
|
| 215 |
+
create_manifest "runners" "${{ needs.build-and-push-docker.outputs.runners_primary_ghcr_manifest_tag }}"
|
| 216 |
+
create_manifest "runners-distroless" "${{ needs.build-and-push-docker.outputs.runners_distroless_primary_ghcr_manifest_tag }}"
|
| 217 |
+
|
| 218 |
+
- name: Create Docker Hub manifests
|
| 219 |
+
if: needs.determine-build-context.outputs.push_to_docker == 'true'
|
| 220 |
+
run: |
|
| 221 |
+
VERSION="${{ needs.determine-build-context.outputs.n8n_version }}"
|
| 222 |
+
DOCKER_BASE="${{ secrets.DOCKER_USERNAME }}"
|
| 223 |
+
|
| 224 |
+
# Create manifests for each image type
|
| 225 |
+
declare -A images=(
|
| 226 |
+
["n8n"]="${VERSION}"
|
| 227 |
+
["runners"]="${VERSION}"
|
| 228 |
+
["runners-distroless"]="${VERSION}-distroless"
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
for image in "${!images[@]}"; do
|
| 232 |
+
TAG_SUFFIX="${images[$image]}"
|
| 233 |
+
IMAGE_NAME="${image//-distroless/}" # Remove -distroless from image name
|
| 234 |
+
|
| 235 |
+
echo "Creating Docker Hub manifest for $image"
|
| 236 |
+
docker buildx imagetools create \
|
| 237 |
+
--tag "${DOCKER_BASE}/${IMAGE_NAME}:${TAG_SUFFIX}" \
|
| 238 |
+
"${DOCKER_BASE}/${IMAGE_NAME}:${TAG_SUFFIX}-amd64" \
|
| 239 |
+
"${DOCKER_BASE}/${IMAGE_NAME}:${TAG_SUFFIX}-arm64"
|
| 240 |
+
done
|
| 241 |
+
|
| 242 |
+
call-success-url:
|
| 243 |
+
name: Call Success URL
|
| 244 |
+
needs: [create_multi_arch_manifest]
|
| 245 |
+
runs-on: ubuntu-latest
|
| 246 |
+
if: needs.create_multi_arch_manifest.result == 'success' || needs.create_multi_arch_manifest.result == 'skipped'
|
| 247 |
+
steps:
|
| 248 |
+
- name: Call Success URL
|
| 249 |
+
env:
|
| 250 |
+
SUCCESS_URL: ${{ github.event.inputs.success_url }}
|
| 251 |
+
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.success_url != '' }}
|
| 252 |
+
run: |
|
| 253 |
+
echo "Calling success URL: ${{ env.SUCCESS_URL }}"
|
| 254 |
+
curl -v "${{ env.SUCCESS_URL }}" || echo "Failed to call success URL"
|
| 255 |
+
shell: bash
|
| 256 |
+
|
| 257 |
+
security-scan:
|
| 258 |
+
name: Security Scan
|
| 259 |
+
needs: [determine-build-context, build-and-push-docker, create_multi_arch_manifest]
|
| 260 |
+
if: |
|
| 261 |
+
success() &&
|
| 262 |
+
(needs.determine-build-context.outputs.release_type == 'stable' ||
|
| 263 |
+
needs.determine-build-context.outputs.release_type == 'nightly' ||
|
| 264 |
+
needs.determine-build-context.outputs.release_type == 'rc')
|
| 265 |
+
uses: ./.github/workflows/security-trivy-scan-callable.yml
|
| 266 |
+
with:
|
| 267 |
+
image_ref: ${{ needs.build-and-push-docker.outputs.image_ref }}
|
| 268 |
+
secrets: inherit
|
| 269 |
+
|
| 270 |
+
security-scan-runners:
|
| 271 |
+
name: Security Scan (runners)
|
| 272 |
+
needs: [determine-build-context, build-and-push-docker, create_multi_arch_manifest]
|
| 273 |
+
if: |
|
| 274 |
+
success() &&
|
| 275 |
+
(needs.determine-build-context.outputs.release_type == 'stable' ||
|
| 276 |
+
needs.determine-build-context.outputs.release_type == 'nightly' ||
|
| 277 |
+
needs.determine-build-context.outputs.release_type == 'rc')
|
| 278 |
+
uses: ./.github/workflows/security-trivy-scan-callable.yml
|
| 279 |
+
with:
|
| 280 |
+
image_ref: ${{ needs.build-and-push-docker.outputs.runners_primary_ghcr_manifest_tag }}
|
| 281 |
+
secrets: inherit
|
| 282 |
+
|
| 283 |
+
notify-on-failure:
|
| 284 |
+
name: Notify Cats on nightly build failure
|
| 285 |
+
runs-on: ubuntu-latest
|
| 286 |
+
needs: [build-and-push-docker]
|
| 287 |
+
if: needs.build-and-push-docker.result == 'failure' && github.event_name == 'schedule'
|
| 288 |
+
steps:
|
| 289 |
+
- uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
| 290 |
+
with:
|
| 291 |
+
status: ${{ needs.build-and-push-docker.result }}
|
| 292 |
+
channel: '#team-catalysts'
|
| 293 |
+
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
| 294 |
+
message: Nightly Docker build failed - ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
.github/workflows/docker-images-benchmark.yml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Benchmark Docker Image CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
push:
|
| 6 |
+
branches:
|
| 7 |
+
- master
|
| 8 |
+
paths:
|
| 9 |
+
- 'packages/@n8n/benchmark/**'
|
| 10 |
+
- 'pnpm-lock.yaml'
|
| 11 |
+
- 'pnpm-workspace.yaml'
|
| 12 |
+
- '.github/workflows/docker-images-benchmark.yml'
|
| 13 |
+
|
| 14 |
+
jobs:
|
| 15 |
+
build:
|
| 16 |
+
runs-on: ubuntu-latest
|
| 17 |
+
|
| 18 |
+
steps:
|
| 19 |
+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
| 20 |
+
|
| 21 |
+
- name: Set up QEMU
|
| 22 |
+
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0
|
| 23 |
+
|
| 24 |
+
- name: Set up Docker Buildx
|
| 25 |
+
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
| 26 |
+
|
| 27 |
+
- name: Login to GitHub Container Registry
|
| 28 |
+
uses: ./.github/actions/docker-registry-login
|
| 29 |
+
|
| 30 |
+
- name: Build
|
| 31 |
+
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
|
| 32 |
+
env:
|
| 33 |
+
DOCKER_BUILD_SUMMARY: false
|
| 34 |
+
with:
|
| 35 |
+
context: .
|
| 36 |
+
file: ./packages/@n8n/benchmark/Dockerfile
|
| 37 |
+
platforms: linux/amd64
|
| 38 |
+
provenance: false
|
| 39 |
+
push: true
|
| 40 |
+
tags: |
|
| 41 |
+
ghcr.io/${{ github.repository_owner }}/n8n-benchmark:latest
|
.github/workflows/linting-reusable.yml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Reusable linting workflow
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_call:
|
| 5 |
+
inputs:
|
| 6 |
+
ref:
|
| 7 |
+
description: GitHub ref to lint.
|
| 8 |
+
required: false
|
| 9 |
+
type: string
|
| 10 |
+
default: ''
|
| 11 |
+
nodeVersion:
|
| 12 |
+
description: Version of node to use.
|
| 13 |
+
required: false
|
| 14 |
+
type: string
|
| 15 |
+
default: 22.x
|
| 16 |
+
|
| 17 |
+
env:
|
| 18 |
+
NODE_OPTIONS: --max-old-space-size=7168
|
| 19 |
+
|
| 20 |
+
jobs:
|
| 21 |
+
lint:
|
| 22 |
+
name: Lint
|
| 23 |
+
runs-on: blacksmith-4vcpu-ubuntu-2204
|
| 24 |
+
steps:
|
| 25 |
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
| 26 |
+
with:
|
| 27 |
+
ref: ${{ inputs.ref }}
|
| 28 |
+
|
| 29 |
+
- name: Build and Test
|
| 30 |
+
uses: ./.github/actions/setup-nodejs
|
| 31 |
+
with:
|
| 32 |
+
build-command: pnpm lint
|
| 33 |
+
node-version: ${{ inputs.nodeVersion }}
|
.github/workflows/notify-pr-status.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Notify PR status changed
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request_review:
|
| 5 |
+
types: [submitted, dismissed]
|
| 6 |
+
pull_request:
|
| 7 |
+
types: [closed]
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
notify:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
if: >-
|
| 13 |
+
(github.event_name == 'pull_request_review' && github.event.review.state == 'approved') ||
|
| 14 |
+
(github.event_name == 'pull_request_review' && github.event.review.state == 'dismissed') ||
|
| 15 |
+
(github.event_name == 'pull_request' && github.event.pull_request.merged == true) ||
|
| 16 |
+
(github.event_name == 'pull_request' && github.event.pull_request.merged == false && github.event.action == 'closed')
|
| 17 |
+
steps:
|
| 18 |
+
- uses: fjogeleit/http-request-action@bf78da14118941f7e940279dd58f67e863cbeff6 # v1
|
| 19 |
+
if: ${{!contains(github.event.pull_request.labels.*.name, 'community')}}
|
| 20 |
+
name: Notify
|
| 21 |
+
env:
|
| 22 |
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
| 23 |
+
with:
|
| 24 |
+
url: ${{ secrets.N8N_NOTIFY_PR_STATUS_CHANGED_URL }}
|
| 25 |
+
method: 'POST'
|
| 26 |
+
customHeaders: '{ "x-api-token": "${{ secrets.N8N_NOTIFY_PR_STATUS_CHANGED_TOKEN }}" }'
|
| 27 |
+
data: '{ "event_name": "${{ github.event_name }}", "pr_url": "${{ env.PR_URL }}", "event": ${{ toJSON(github.event) }} }'
|