Skip to content

Commit f9b1953

Browse files
authored
Merge branch 'master' into ai-2358-bug-data-table-name-conflict-causes-builder-to-loop-and
2 parents febdfa7 + 8810097 commit f9b1953

File tree

316 files changed

+6965
-3461
lines changed

Some content is hidden

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

316 files changed

+6965
-3461
lines changed

.claude/plugins/n8n/skills/create-community-node-lint-rule/SKILL.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ description: >-
44
Use when adding a lint rule, creating a community node lint, or working on
55
eslint-plugin-community-nodes. Guides rule implementation, tests, docs, and
66
plugin registration.
7-
user_invocable: true
87
---
98

109
# Create Community Node Lint Rule

.claude/plugins/n8n/skills/create-pr/SKILL.md

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,17 @@ Creates GitHub PRs with titles that pass n8n's `check-pr-title` CI validation.
7272
git push -u origin HEAD
7373
```
7474

75-
6. **Create PR** using gh CLI with the template from `.github/pull_request_template.md`:
75+
6. **Create PR** using gh CLI. Read `.github/pull_request_template.md` as the
76+
body structure, then populate each section with actual content before
77+
creating the PR:
78+
- **Summary**: describe what the PR does and how to test it
79+
- **Related tickets**: add the Linear ticket URL (`https://linear.app/n8n/issue/[TICKET-ID]`) and any GitHub issue links
80+
- **Checklist**: keep as-is from the template
81+
- Add a "🤖 PR Summary generated by AI" at the end of the body
82+
7683
```bash
7784
gh pr create --draft --title "<type>(<scope>): <summary>" --body "$(cat <<'EOF'
78-
## Summary
79-
80-
<Describe what the PR does and how to test. Photos and videos are recommended.>
81-
82-
## Related Linear tickets, Github issues, and Community forum posts
83-
84-
<!-- Link to Linear ticket: https://linear.app/n8n/issue/[TICKET-ID] -->
85-
<!-- Use "closes #<issue-number>", "fixes #<issue-number>", or "resolves #<issue-number>" to automatically close issues -->
86-
87-
## Review / Merge checklist
88-
89-
- [ ] PR title and summary are descriptive. ([conventions](../blob/master/.github/pull_request_title_conventions.md))
90-
- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created.
91-
- [ ] Tests included.
92-
- [ ] PR Labeled with `release/backport` (if the PR is an urgent fix that needs to be backported)
85+
<populated body based on pull_request_template.md>
9386
EOF
9487
)"
9588
```
@@ -111,6 +104,7 @@ Based on `.github/pull_request_template.md`:
111104
112105
### Checklist
113106
All items should be addressed before merging:
107+
- The human author of the PR has checked the "I have seen this code, I have run this code, and I take responsibility for this code." checkbox
114108
- PR title follows conventions
115109
- Docs updated or follow-up ticket created
116110
- Tests included (bugs need regression tests, features need coverage)
@@ -191,5 +185,8 @@ Describe **what the code does**, not what threat it prevents.
191185
| Linear ref | URL with slug (leaks title) | URL without slug or ticket ID only |
192186
| Test name | `'should prevent SQL injection'` | `'should sanitize query parameters'` |
193187
188+
194189
**Before pushing a security fix, verify:** no branch name, commit, PR title,
195190
PR body, Linear URL, test name, or code comment hints at the vulnerability.
191+
192+
**When in doubt, check the Linear issue for possible extra precautions**

.claude/plugins/n8n/skills/reproduce-bug/SKILL.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
description: Reproduce a bug from a Linear ticket with a failing test. Expects the full ticket context (title, description, comments) to be provided as input.
3-
user_invocable: true
43
---
54

65
# Bug Reproduction Framework

.github/pull_request_template.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ Photos and videos are recommended.
1010
<!--
1111
Include links to **Linear ticket** or Github issue or Community forum post.
1212
Important in order to close *automatically* and provide context to reviewers.
13-
https://linear.app/n8n/issue/
13+
https://linear.app/n8n/issue/[TICKET-ID]
1414
-->
1515
<!-- Use "closes #<issue-number>", "fixes #<issue-number>", or "resolves #<issue-number>" to automatically close issues when the PR is merged. -->
1616

1717

1818
## Review / Merge checklist
1919

20+
- [ ] I have seen this code, I have run this code, and I take responsibility for this code.
2021
- [ ] PR title and summary are descriptive. ([conventions](../blob/master/.github/pull_request_title_conventions.md)) <!--
2122
**Remember, the title automatically goes into the changelog.
2223
Use `(no-changelog)` otherwise.**

.github/scripts/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
{
22
"name": "workflow-scripts",
33
"scripts": {
4-
"test": "node --test --experimental-test-module-mocks ./*.test.mjs"
4+
"test": "node --test --experimental-test-module-mocks ./*.test.mjs ./quality/*.test.mjs"
55
},
66
"dependencies": {
77
"@actions/github": "9.0.0",
88
"@octokit/core": "7.0.6",
99
"conventional-changelog": "7.2.0",
1010
"debug": "4.4.3",
1111
"glob": "13.0.6",
12+
"minimatch": "10.2.4",
1213
"semver": "7.7.4",
1314
"tempfile": "6.0.1"
1415
},

.github/scripts/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* Checks that the PR description contains a checked ownership acknowledgement checkbox.
3+
*
4+
* Exit codes:
5+
* 0 – Checkbox is present and checked
6+
* 1 – Checkbox is missing or unchecked
7+
*/
8+
9+
import { initGithub, getEventFromGithubEventPath } from '../github-helpers.mjs';
10+
11+
const BOT_MARKER = '<!-- pr-ownership-check -->';
12+
13+
/**
14+
* Returns true if the PR body contains a checked ownership acknowledgement checkbox.
15+
*
16+
* @param {string | null | undefined} body
17+
* @returns {boolean}
18+
*/
19+
export function isOwnershipCheckboxChecked(body) {
20+
return /\[x\]\s+I have seen this code,\s+I have run this code,\s+and I take responsibility for this code/i.test(
21+
body ?? '',
22+
);
23+
}
24+
25+
async function main() {
26+
const event = getEventFromGithubEventPath();
27+
const pr = event.pull_request;
28+
const { octokit, owner, repo } = initGithub();
29+
30+
const { data: comments } = await octokit.rest.issues.listComments({
31+
owner,
32+
repo,
33+
issue_number: pr.number,
34+
per_page: 100,
35+
});
36+
const botComment = comments.find((c) => c.body.includes(BOT_MARKER));
37+
38+
if (!isOwnershipCheckboxChecked(pr.body)) {
39+
const message = [
40+
BOT_MARKER,
41+
'## ⚠️ Ownership acknowledgement required',
42+
'',
43+
'Please add or check the following item in your PR description before this can be merged:',
44+
'',
45+
'```',
46+
'- [x] I have seen this code, I have run this code, and I take responsibility for this code.',
47+
'```',
48+
].join('\n');
49+
50+
if (botComment) {
51+
await octokit.rest.issues.updateComment({
52+
owner,
53+
repo,
54+
comment_id: botComment.id,
55+
body: message,
56+
});
57+
} else {
58+
await octokit.rest.issues.createComment({
59+
owner,
60+
repo,
61+
issue_number: pr.number,
62+
body: message,
63+
});
64+
}
65+
66+
console.log(
67+
'::error::Ownership checkbox is not checked. Add it to your PR description and check it.',
68+
);
69+
process.exit(1);
70+
} else if (botComment) {
71+
await octokit.rest.issues.deleteComment({
72+
owner,
73+
repo,
74+
comment_id: botComment.id,
75+
});
76+
}
77+
}
78+
79+
if (import.meta.url === `file://${process.argv[1]}`) {
80+
await main();
81+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { describe, it, before, mock } from 'node:test';
2+
import assert from 'node:assert/strict';
3+
4+
/**
5+
* Run with:
6+
* node --test --experimental-test-module-mocks .github/scripts/quality/check-ownership-checkbox.test.mjs
7+
*/
8+
9+
mock.module('../github-helpers.mjs', {
10+
namedExports: {
11+
initGithub: () => {},
12+
getEventFromGithubEventPath: () => {},
13+
},
14+
});
15+
16+
let isOwnershipCheckboxChecked;
17+
before(async () => {
18+
({ isOwnershipCheckboxChecked } = await import('./check-ownership-checkbox.mjs'));
19+
});
20+
21+
describe('isOwnershipCheckboxChecked', () => {
22+
it('returns true for a checked checkbox with exact text', () => {
23+
const body =
24+
'- [x] I have seen this code, I have run this code, and I take responsibility for this code.';
25+
assert.ok(isOwnershipCheckboxChecked(body));
26+
});
27+
28+
it('returns true for uppercase [X]', () => {
29+
const body =
30+
'- [X] I have seen this code, I have run this code, and I take responsibility for this code.';
31+
assert.ok(isOwnershipCheckboxChecked(body));
32+
});
33+
34+
it('returns false for an unchecked checkbox [ ]', () => {
35+
const body =
36+
'- [ ] I have seen this code, I have run this code, and I take responsibility for this code.';
37+
assert.equal(isOwnershipCheckboxChecked(body), false);
38+
});
39+
40+
it('returns false when the checkbox is absent', () => {
41+
const body = '## Summary\n\nThis PR does some things.\n';
42+
assert.equal(isOwnershipCheckboxChecked(body), false);
43+
});
44+
45+
it('returns false for null body', () => {
46+
assert.equal(isOwnershipCheckboxChecked(null), false);
47+
});
48+
49+
it('returns false for undefined body', () => {
50+
assert.equal(isOwnershipCheckboxChecked(undefined), false);
51+
});
52+
53+
it('returns false for empty body', () => {
54+
assert.equal(isOwnershipCheckboxChecked(''), false);
55+
});
56+
57+
it('returns true when checkbox appears among other content', () => {
58+
const body = [
59+
'## Summary',
60+
'',
61+
'Some description here.',
62+
'',
63+
'## Checklist',
64+
'- [x] Tests included',
65+
'- [x] I have seen this code, I have run this code, and I take responsibility for this code.',
66+
'- [ ] Docs updated',
67+
].join('\n');
68+
assert.ok(isOwnershipCheckboxChecked(body));
69+
});
70+
71+
it('returns false when only other checkboxes are checked', () => {
72+
const body = [
73+
'- [x] Tests included',
74+
'- [x] Docs updated',
75+
'- [ ] I have seen this code, I have run this code, and I take responsibility for this code.',
76+
].join('\n');
77+
assert.equal(isOwnershipCheckboxChecked(body), false);
78+
});
79+
80+
it('is case-insensitive for the checkbox marker', () => {
81+
const lower =
82+
'- [x] i have seen this code, i have run this code, and i take responsibility for this code.';
83+
assert.ok(isOwnershipCheckboxChecked(lower));
84+
});
85+
});

0 commit comments

Comments
 (0)