Skip to content

Commit d07f504

Browse files
committed
Merge branch 'master' into upgrade-google-gax
2 parents b31cb62 + 4459c7e commit d07f504

File tree

646 files changed

+7128
-2113
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

646 files changed

+7128
-2113
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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+
- '!**/*.md'
39+
excluded_source_branch_patterns:
40+
description: "Newline-separated list of glob patterns for source branches to EXCLUDE."
41+
required: false
42+
type: string
43+
default: |
44+
release/*
45+
master
46+
47+
outputs:
48+
should_run:
49+
description: "Outputs 'true' if all eligibility checks pass, otherwise 'false'."
50+
value: ${{ jobs.evaluate_conditions.outputs.run_decision }}
51+
52+
jobs:
53+
evaluate_conditions:
54+
runs-on: ubuntu-latest
55+
outputs:
56+
run_decision: ${{ steps.evaluate.outputs.should_run }}
57+
steps:
58+
- name: Check out current commit
59+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
60+
with:
61+
ref: ${{ github.event.pull_request.head.sha }}
62+
63+
- name: Determine changed files
64+
uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2
65+
id: changed
66+
with:
67+
filters: ${{ inputs.paths_filter_patterns }}
68+
predicate-quantifier: 'every'
69+
70+
- name: Evaluate Conditions & Set Output
71+
id: evaluate
72+
env:
73+
IS_FORK: ${{ github.event.pull_request.head.repo.fork }}
74+
IS_APPROVED: ${{ inputs.is_pr_approved_by_maintainer }}
75+
FILES_CHANGED: ${{ steps.changed.outputs.not_ignored == 'true' }}
76+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
77+
EXCLUDED_PATTERNS: ${{ inputs.excluded_source_branch_patterns }}
78+
run: |
79+
if [[ "$IS_FORK" == "true" ]]; then
80+
is_community="true"
81+
else
82+
is_community="false"
83+
fi
84+
85+
source_branch_excluded="false"
86+
while IFS= read -r pattern; do
87+
if [[ -n "$pattern" && "$HEAD_REF" == $pattern ]]; then
88+
source_branch_excluded="true"
89+
break
90+
fi
91+
done <<< "$EXCLUDED_PATTERNS"
92+
93+
echo "--- Checking Conditions ---"
94+
echo "Is NOT Community PR: $([[ "$is_community" == "false" ]] && echo true || echo false)"
95+
echo "Files Changed: $FILES_CHANGED"
96+
echo "Source Branch Excluded: $source_branch_excluded"
97+
echo "Is Approved: $IS_APPROVED"
98+
echo "-------------------------"
99+
100+
if [[ "$is_community" == "false" && \
101+
"$FILES_CHANGED" == "true" && \
102+
"$source_branch_excluded" == "false" && \
103+
"$IS_APPROVED" == "true" ]]; then
104+
echo "Decision: Conditions met. Setting should_run=true."
105+
echo "should_run=true" >> $GITHUB_OUTPUT
106+
else
107+
echo "Decision: Conditions not met. Setting should_run=false."
108+
echo "should_run=false" >> $GITHUB_OUTPUT
109+
fi

.github/workflows/e2e-tests-pr.yml

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,80 +9,71 @@ concurrency:
99
cancel-in-progress: true
1010

1111
jobs:
12-
get-metadata:
13-
name: Get Metadata
14-
runs-on: ubuntu-latest
12+
eligibility_check:
13+
name: Check Eligibility for Test Run
1514
if: github.event.review.state == 'approved'
16-
steps:
17-
- name: Check out current commit
18-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
19-
with:
20-
ref: ${{ github.event.pull_request.head.sha }}
21-
fetch-depth: 2
22-
23-
- name: Determine changed files
24-
uses: tomi/paths-filter-action@32c62f5ca100c1110406e3477d5b3ecef4666fec # v3.0.2
25-
id: changed
26-
with:
27-
filters: |
28-
not_ignored:
29-
- '!.devcontainer/**'
30-
- '!.github/*'
31-
- '!.github/scripts/*'
32-
- '!.github/workflows/benchmark-*'
33-
- '!.github/workflows/check-*'
34-
- '!.vscode/**'
35-
- '!docker/**'
36-
- '!packages/@n8n/benchmark/**'
37-
- '!**/*.md'
38-
predicate-quantifier: 'every'
39-
40-
outputs:
41-
# The workflow should run when:
42-
# - It has changes to files that are not ignored
43-
# - It is not a community PR
44-
# - It is targeting master or a release branch
45-
should_run: ${{ steps.changed.outputs.not_ignored == 'true' && !contains(github.event.pull_request.labels.*.name, 'community') && (github.event.pull_request.base.ref == 'master' || startsWith(github.event.pull_request.base.ref, 'release/')) }}
15+
uses: ./.github/workflows/check-run-eligibility.yml
16+
with:
17+
is_pr_approved_by_maintainer: true
4618

4719
run-e2e-tests:
4820
name: E2E [Electron/Node 18]
4921
uses: ./.github/workflows/e2e-reusable.yml
50-
needs: [get-metadata]
51-
if: ${{ github.event.review.state == 'approved' && needs.get-metadata.outputs.should_run == 'true' }}
22+
needs: [eligibility_check]
23+
if: needs.eligibility_check.outputs.should_run == 'true'
5224
with:
5325
pr_number: ${{ github.event.pull_request.number }}
5426
user: ${{ github.event.pull_request.user.login || 'PR User' }}
5527
secrets:
5628
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
5729

5830
post-e2e-tests:
59-
runs-on: ubuntu-latest
6031
name: E2E [Electron/Node 18] - Checks
61-
needs: [get-metadata, run-e2e-tests]
62-
if: always()
32+
runs-on: ubuntu-latest
33+
needs: [eligibility_check, run-e2e-tests]
34+
if: always() && needs.eligibility_check.result != 'skipped'
6335
steps:
64-
- name: E2E success comment
65-
if: ${{ needs.get-metadata.outputs.should_run == 'true' && needs.run-e2e-tests.outputs.tests_passed == 'true' }}
66-
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
67-
with:
68-
issue-number: ${{ github.event.pull_request.number }}
69-
body: |
70-
:white_check_mark: All Cypress E2E specs passed
71-
token: ${{ secrets.GITHUB_TOKEN }}
36+
- name: Determine Outcome and Comment Message
37+
id: determine_outcome
38+
run: |
39+
JOB_OUTCOME="success"
40+
COMMENT_BODY=""
41+
SHOULD_POST_COMMENT="false"
7242
73-
- name: E2E fail comment
74-
if: needs.run-e2e-tests.result == 'failure'
75-
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
43+
if [[ "${{ needs.eligibility_check.outputs.should_run }}" == "false" ]]; then
44+
COMMENT_BODY="ℹ️ E2E tests were not run for this PR based on the eligibility criteria."
45+
SHOULD_POST_COMMENT="true"
46+
JOB_OUTCOME="success"
47+
elif [[ "${{ needs.run-e2e-tests.result }}" == "success" ]]; then
48+
COMMENT_BODY=":white_check_mark: All Cypress E2E specs passed"
49+
SHOULD_POST_COMMENT="true"
50+
JOB_OUTCOME="success"
51+
elif [[ "${{ needs.run-e2e-tests.result }}" == "failure" ]]; then
52+
COMMENT_BODY=":warning: Some Cypress E2E specs are failing, please fix them before merging"
53+
SHOULD_POST_COMMENT="true"
54+
JOB_OUTCOME="failure"
55+
else
56+
COMMENT_BODY="ℹ️ E2E tests were scheduled but did not complete as expected (Result: ${{ needs.run-e2e-tests.result }})."
57+
SHOULD_POST_COMMENT="true"
58+
JOB_OUTCOME="failure"
59+
fi
60+
61+
echo "comment_body=$COMMENT_BODY" >> $GITHUB_OUTPUT
62+
echo "should_post_comment=$SHOULD_POST_COMMENT" >> $GITHUB_OUTPUT
63+
echo "job_outcome=$JOB_OUTCOME" >> $GITHUB_OUTPUT
64+
65+
- name: Create or Update PR Comment
66+
if: steps.determine_outcome.outputs.should_post_comment == 'true'
67+
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043
7668
with:
7769
issue-number: ${{ github.event.pull_request.number }}
78-
body: |
79-
:warning: Some Cypress E2E specs are failing, please fix them before merging
70+
body: ${{ steps.determine_outcome.outputs.comment_body }}
8071
token: ${{ secrets.GITHUB_TOKEN }}
8172

82-
- name: Success job if community PR
83-
if: ${{ contains(github.event.pull_request.labels.*.name, 'community') }}
84-
run: exit 0
85-
86-
- name: Fail job if run-e2e-tests failed
87-
if: ${{ (github.event.review.state != 'approved' && github.event.review.state != 'commented') || needs.run-e2e-tests.result == 'failure' }}
88-
run: exit 1
73+
- name: Finalize Job Status
74+
run: |
75+
if [[ "${{ steps.determine_outcome.outputs.job_outcome }}" == "failure" ]]; then
76+
exit 1
77+
else
78+
exit 0
79+
fi

.github/workflows/test-workflows-pr-approved.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ on:
66

77
permissions:
88
contents: read
9-
pull-requests: read
109

1110
jobs:
12-
run_workflow_tests_after_approval:
13-
name: Run Tests on Approved PR
11+
eligibility_check:
12+
name: Check Eligibility for Test Run
1413
if: github.event.review.state == 'approved'
14+
uses: ./.github/workflows/check-run-eligibility.yml
15+
with:
16+
is_pr_approved_by_maintainer: true
17+
18+
run_workflow_tests:
19+
name: Run Tests on Approved Internal PR
20+
needs: [eligibility_check]
21+
if: needs.eligibility_check.outputs.should_run == 'true'
1522
uses: ./.github/workflows/test-workflows-callable.yml
1623
with:
1724
git_ref: ${{ github.event.pull_request.head.sha }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ CHANGELOG-*.md
2424
build-storybook.log
2525
*.junit.xml
2626
junit.xml
27-
test-results.json
27+
test-results.json
28+
*.0x

cypress/composables/folders.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,14 @@ export function getMoveToFolderInput() {
206206
return getMoveToFolderDropdown().find('input');
207207
}
208208

209+
export function getProjectSharingInput() {
210+
return cy.getByTestId('project-sharing-select');
211+
}
212+
213+
export function getProjectSharingOption(name: string) {
214+
return cy.getByTestId('project-sharing-info').contains(name);
215+
}
216+
209217
export function getEmptyFolderDropdownMessage(text: string) {
210218
return cy.get('.el-select-dropdown__empty').contains(text);
211219
}
@@ -500,7 +508,12 @@ function deleteFolderAndMoveContents(folderName: string, destinationName: string
500508
function moveFolder(folderName: string, destinationName: string) {
501509
cy.intercept('PATCH', '/rest/projects/**').as('moveFolder');
502510
getMoveFolderModal().should('be.visible');
503-
getMoveFolderModal().find('h1').first().contains(`Move "${folderName}" to another folder`);
511+
getMoveFolderModal().find('h1').first().contains(`Move folder ${folderName}`);
512+
513+
// The dropdown focuses after a small delay (once modal's slide in animation is done).
514+
// On the component we listen for an event, but here the wait should be very predictable.
515+
cy.wait(500);
516+
504517
// Try to find current folder in the dropdown
505518
// This tests that auto-focus worked as expected
506519
cy.focused().type(folderName, { delay: 50 });
@@ -514,3 +527,27 @@ function moveFolder(folderName: string, destinationName: string) {
514527
getMoveFolderConfirmButton().should('be.enabled').click();
515528
cy.wait('@moveFolder');
516529
}
530+
531+
export function transferWorkflow(
532+
workflowName: string,
533+
projectName: string,
534+
destinationFolder?: string,
535+
) {
536+
getMoveFolderModal().should('be.visible');
537+
getMoveFolderModal().find('h1').first().contains(`Move workflow ${workflowName}`);
538+
539+
cy.wait(500);
540+
541+
getProjectSharingInput().should('be.visible').click();
542+
cy.focused().type(projectName, { delay: 50 });
543+
getProjectSharingOption(projectName).should('be.visible').click();
544+
545+
if (destinationFolder) {
546+
getMoveToFolderInput().click();
547+
// Select destination folder
548+
cy.focused().type(destinationFolder, { delay: 50 });
549+
getMoveToFolderOption(destinationFolder).should('be.visible').click();
550+
}
551+
552+
getMoveFolderConfirmButton().should('be.enabled').click();
553+
}

cypress/composables/ndv.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function getInputTbodyCell(row: number, col: number) {
7373
}
7474

7575
export function getInputRunSelector() {
76-
return getInputPanel().findChildByTestId('run-selector');
76+
return cy.get('[data-test-id="ndv-input-panel"] [data-test-id="run-selector"]');
7777
}
7878

7979
export function getInputPanelItemsCount() {
@@ -105,7 +105,7 @@ export function getOutputTbodyCell(row: number, col: number) {
105105
}
106106

107107
export function getOutputRunSelector() {
108-
return getOutputPanel().findChildByTestId('run-selector');
108+
return cy.get('[data-test-id="output-panel"] [data-test-id="run-selector"]');
109109
}
110110

111111
export function getOutputRunSelectorInput() {

cypress/composables/projects.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const workflowPage = new WorkflowPage();
55
const credentialsModal = new CredentialsModal();
66

77
export const getHomeButton = () => cy.getByTestId('project-home-menu-item');
8+
export const getPersonalProjectsButton = () => cy.getByTestId('project-personal-menu-item');
89
export const getMenuItems = () => cy.getByTestId('project-menu-item');
910
export const getAddProjectButton = () => {
1011
cy.getByTestId('universal-add').should('be.visible').click();
@@ -62,6 +63,8 @@ export const addProjectMember = (email: string, role?: string) => {
6263
};
6364
export const getResourceMoveModal = () => cy.getByTestId('project-move-resource-modal');
6465
export const getProjectMoveSelect = () => cy.getByTestId('project-move-resource-modal-select');
66+
export const getProjectSharingSelect = () => cy.getByTestId('project-sharing-select');
67+
export const getMoveToFolderSelect = () => cy.getByTestId('move-to-folder-dropdown');
6568

6669
export function createProject(name: string) {
6770
getAddProjectButton().click();

cypress/e2e/30-langchain.cy.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ describe('Langchain Integration', () => {
381381
workflowPage.actions.deselectAll();
382382

383383
workflowPage.actions.executeNode('Populate VS');
384-
workflow.getNodesWithSpinner().should('not.exist');
384+
workflow.waitForSuccessBannerToAppear();
385385

386386
const assertInputOutputText = (text: string, assertion: 'exist' | 'not.exist') => {
387387
ndv.getOutputPanel().contains(text).should(assertion);
@@ -390,9 +390,6 @@ describe('Langchain Integration', () => {
390390

391391
workflowPage.actions.openNode('Character Text Splitter');
392392

393-
// Wait for the input panel to switch to Debugging mode
394-
ndv.getInputPanelItemsCount().should('not.exist');
395-
396393
ndv.getOutputRunSelector().should('exist');
397394
ndv.getInputRunSelector().should('exist');
398395
ndv.getInputRunSelector().find('input').should('include.value', '3 of 3');

0 commit comments

Comments
 (0)