n8n-nodes-version 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -20
- package/dist/nodes/CompatibilityUtils.js +78 -0
- package/dist/nodes/GitUtils.js +63 -0
- package/dist/nodes/N8nVersion/N8nVersion.node.js +200 -97
- package/dist/nodes/N8nVersionTrigger/N8nVersionTrigger.node.js +57 -16
- package/dist/nodes/VersionUtils.js +79 -9
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -13,10 +13,11 @@ n8n nodes to check the current installed version, monitor for updates, and fetch
|
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
16
|
+
- **Advanced Version Analysis**: Identifies Major, Minor, and Patch updates using SemVer diffing.
|
|
17
|
+
- **Enhanced Changelog**: Automatically extracts "Breaking Changes" and "Deprecations" from GitHub release notes.
|
|
18
|
+
- **Compatibility Auditing**: Scans local `node_modules` for community nodes and checks compatibility against target n8n versions.
|
|
19
|
+
- **Automated Git Backups**: Exports workflows and commits them to a GitHub repository automatically.
|
|
20
|
+
- **Granular Triggers**: Filter version updates by SemVer relevance (e.g., only trigger on Major updates).
|
|
20
21
|
|
|
21
22
|
## Installation
|
|
22
23
|
|
|
@@ -39,30 +40,26 @@ npm install n8n-nodes-version
|
|
|
39
40
|
|
|
40
41
|
## Nodes
|
|
41
42
|
|
|
42
|
-
### Version
|
|
43
|
+
### n8n Version Node (v2)
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
Manages version information, compatibility audits, and backups.
|
|
45
46
|
|
|
46
|
-
**
|
|
47
|
+
**Resources & Operations:**
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"latestVersion": "1.1.0",
|
|
52
|
-
"isLatest": false,
|
|
53
|
-
"changelog": "## Bug Fixes\n- Fixed issue with...",
|
|
54
|
-
"changelogVersion": "1.1.0"
|
|
55
|
-
}
|
|
56
|
-
```
|
|
49
|
+
- **Version Info**: Get version status (`getInfo`) or fetch release notes (`getChangelog`).
|
|
50
|
+
- **Compatibility**: Audit installed community nodes (`audit`).
|
|
51
|
+
- **Backup**: Export and push all workflows to Git (`gitBackup`).
|
|
57
52
|
|
|
58
|
-
### Version Trigger
|
|
53
|
+
### n8n Version Trigger (v2)
|
|
59
54
|
|
|
60
|
-
A schedule-based trigger
|
|
55
|
+
A schedule-based trigger with advanced filtering.
|
|
61
56
|
|
|
62
57
|
**Configuration:**
|
|
63
58
|
|
|
64
|
-
- **Cron Expression**: Define
|
|
65
|
-
- **Trigger Condition**: Choose between `Update Available
|
|
59
|
+
- **Cron Expression**: Define check frequency (e.g., `0 * * * *`).
|
|
60
|
+
- **Trigger Condition**: Choose between `Update Available` or `Always`.
|
|
61
|
+
- **SemVer Level**: Filter triggers by `Major`, `Minor`, or `Patch` update relevance.
|
|
62
|
+
- **Ignore Prereleases**: Option to skip alpha, beta, and RC versions.
|
|
66
63
|
|
|
67
64
|
---
|
|
68
65
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.auditAllNodes = exports.auditNode = exports.getInstalledCommunityNodes = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
/**
|
|
10
|
+
* Scans node_modules for installed community nodes
|
|
11
|
+
*/
|
|
12
|
+
function getInstalledCommunityNodes() {
|
|
13
|
+
const communityNodes = [];
|
|
14
|
+
try {
|
|
15
|
+
const nodeModulesPath = path_1.default.join(process.cwd(), 'node_modules');
|
|
16
|
+
if (!fs_1.default.existsSync(nodeModulesPath))
|
|
17
|
+
return [];
|
|
18
|
+
const dirs = fs_1.default.readdirSync(nodeModulesPath);
|
|
19
|
+
for (const dir of dirs) {
|
|
20
|
+
if (dir.startsWith('n8n-nodes-') || (dir.startsWith('@') && fs_1.default.readdirSync(path_1.default.join(nodeModulesPath, dir)).some(sub => sub.startsWith('n8n-nodes-')))) {
|
|
21
|
+
// Handle scoped packages too
|
|
22
|
+
if (dir.startsWith('@')) {
|
|
23
|
+
const scopedDirs = fs_1.default.readdirSync(path_1.default.join(nodeModulesPath, dir));
|
|
24
|
+
for (const scopedDir of scopedDirs) {
|
|
25
|
+
if (scopedDir.startsWith('n8n-nodes-')) {
|
|
26
|
+
communityNodes.push(`${dir}/${scopedDir}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
communityNodes.push(dir);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error('[CompatibilityUtils] Error scanning node_modules:', err);
|
|
38
|
+
}
|
|
39
|
+
return communityNodes;
|
|
40
|
+
}
|
|
41
|
+
exports.getInstalledCommunityNodes = getInstalledCommunityNodes;
|
|
42
|
+
/**
|
|
43
|
+
* Audits a specific node for compatibility with a target n8n version
|
|
44
|
+
*/
|
|
45
|
+
async function auditNode(nodeName, targetN8nVersion) {
|
|
46
|
+
// This is a simplified mock. In a real scenario, this would check npm or a compatibility registry.
|
|
47
|
+
// For now, we'll return a basic structure.
|
|
48
|
+
const packageJsonPath = path_1.default.join(process.cwd(), 'node_modules', nodeName, 'package.json');
|
|
49
|
+
let currentVersion = 'unknown';
|
|
50
|
+
try {
|
|
51
|
+
if (fs_1.default.existsSync(packageJsonPath)) {
|
|
52
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
|
|
53
|
+
currentVersion = pkg.version;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error(`[CompatibilityUtils] Error reading package.json for ${nodeName}:`, err);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
name: nodeName,
|
|
61
|
+
currentVersion,
|
|
62
|
+
isCompatible: true,
|
|
63
|
+
actionRequired: 'none',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
exports.auditNode = auditNode;
|
|
67
|
+
/**
|
|
68
|
+
* Runs a full audit on all community nodes
|
|
69
|
+
*/
|
|
70
|
+
async function auditAllNodes(targetN8nVersion) {
|
|
71
|
+
const nodes = getInstalledCommunityNodes();
|
|
72
|
+
const results = [];
|
|
73
|
+
for (const node of nodes) {
|
|
74
|
+
results.push(await auditNode(node, targetN8nVersion));
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
exports.auditAllNodes = auditAllNodes;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.exportWorkflowsToGit = exports.commitAndPush = exports.ensureGitRepo = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
/**
|
|
11
|
+
* Ensures the local backup directory exists and is a git repo
|
|
12
|
+
*/
|
|
13
|
+
function ensureGitRepo(config) {
|
|
14
|
+
if (!fs_1.default.existsSync(config.localPath)) {
|
|
15
|
+
fs_1.default.mkdirSync(config.localPath, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
(0, child_process_1.execSync)('git rev-parse --is-inside-work-tree', { cwd: config.localPath });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
// Not a repo, clone it
|
|
22
|
+
(0, child_process_1.execSync)(`git clone ${config.repoUrl} .`, { cwd: config.localPath });
|
|
23
|
+
(0, child_process_1.execSync)(`git checkout ${config.branch} || git checkout -b ${config.branch}`, { cwd: config.localPath });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.ensureGitRepo = ensureGitRepo;
|
|
27
|
+
/**
|
|
28
|
+
* Commits and pushes changes to the repository
|
|
29
|
+
*/
|
|
30
|
+
function commitAndPush(config) {
|
|
31
|
+
const message = config.commitMessage || `Backup: ${new Date().toISOString()}`;
|
|
32
|
+
try {
|
|
33
|
+
(0, child_process_1.execSync)('git add .', { cwd: config.localPath });
|
|
34
|
+
// Only commit if there are changes
|
|
35
|
+
const status = (0, child_process_1.execSync)('git status --porcelain', { cwd: config.localPath }).toString();
|
|
36
|
+
if (status) {
|
|
37
|
+
(0, child_process_1.execSync)(`git commit -m "${message}"`, { cwd: config.localPath });
|
|
38
|
+
(0, child_process_1.execSync)(`git push origin ${config.branch}`, { cwd: config.localPath });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error('[GitUtils] Git operation failed:', err);
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.commitAndPush = commitAndPush;
|
|
47
|
+
/**
|
|
48
|
+
* Exports all workflows from n8n to the local git path
|
|
49
|
+
* This assumes access to the n8n CLI or API
|
|
50
|
+
*/
|
|
51
|
+
async function exportWorkflowsToGit(localPath) {
|
|
52
|
+
// In a real n8n environment, this could use the n8n CLI: n8n export:workflow --all
|
|
53
|
+
// Or fetch via API and write to files.
|
|
54
|
+
// For this node, we'll assume the n8n CLI is available.
|
|
55
|
+
try {
|
|
56
|
+
(0, child_process_1.execSync)(`n8n export:workflow --all --output="${path_1.default.join(localPath, 'workflows/')}"`);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error('[GitUtils] Failed to export workflows:', err);
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.exportWorkflowsToGit = exportWorkflowsToGit;
|
|
@@ -3,79 +3,193 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.N8nVersion = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const VersionUtils_1 = require("../VersionUtils");
|
|
6
|
+
const CompatibilityUtils_1 = require("../CompatibilityUtils");
|
|
7
|
+
const GitUtils_1 = require("../GitUtils");
|
|
6
8
|
class N8nVersion {
|
|
7
9
|
constructor() {
|
|
8
10
|
this.description = {
|
|
9
|
-
displayName: 'Version
|
|
10
|
-
name: '
|
|
11
|
+
displayName: 'n8n Version Node',
|
|
12
|
+
name: 'n8nVersion',
|
|
11
13
|
icon: 'file:n8n-version.svg',
|
|
12
14
|
group: ['transform'],
|
|
13
|
-
version:
|
|
14
|
-
description: '
|
|
15
|
+
version: 2,
|
|
16
|
+
description: 'Manage n8n versions, backups, and compatibility audits',
|
|
15
17
|
defaults: {
|
|
16
|
-
name: 'Version
|
|
18
|
+
name: 'n8n Version',
|
|
17
19
|
},
|
|
18
20
|
inputs: ['main'],
|
|
19
21
|
outputs: ['main'],
|
|
20
22
|
properties: [
|
|
21
23
|
{
|
|
22
|
-
displayName: '
|
|
23
|
-
name: '
|
|
24
|
-
type: '
|
|
25
|
-
|
|
26
|
-
default: {},
|
|
24
|
+
displayName: 'Resource',
|
|
25
|
+
name: 'resource',
|
|
26
|
+
type: 'options',
|
|
27
|
+
noDataExpression: true,
|
|
27
28
|
options: [
|
|
28
29
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
type: 'boolean',
|
|
32
|
-
default: true,
|
|
33
|
-
description: 'Whether to check and return if the current version is the latest available on npm',
|
|
30
|
+
name: 'Version Info',
|
|
31
|
+
value: 'version',
|
|
34
32
|
},
|
|
35
33
|
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
type: 'boolean',
|
|
39
|
-
default: false,
|
|
40
|
-
description: 'Whether to fetch release notes from GitHub. Note: Subject to GitHub API rate limits.',
|
|
34
|
+
name: 'Compatibility',
|
|
35
|
+
value: 'compatibility',
|
|
41
36
|
},
|
|
42
37
|
{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
type: 'options',
|
|
46
|
-
default: 'latest',
|
|
47
|
-
description: 'Which version to fetch the changelog for',
|
|
48
|
-
displayOptions: {
|
|
49
|
-
show: {
|
|
50
|
-
fetchChangelog: [true],
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
options: [
|
|
54
|
-
{
|
|
55
|
-
name: 'Latest Version',
|
|
56
|
-
value: 'latest',
|
|
57
|
-
description: 'Fetch changelog for the latest version from npm',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'Current Version',
|
|
61
|
-
value: 'current',
|
|
62
|
-
description: 'Fetch changelog for the currently installed version',
|
|
63
|
-
},
|
|
64
|
-
],
|
|
38
|
+
name: 'Backup',
|
|
39
|
+
value: 'backup',
|
|
65
40
|
},
|
|
41
|
+
],
|
|
42
|
+
default: 'version',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
displayName: 'Operation',
|
|
46
|
+
name: 'operation',
|
|
47
|
+
type: 'options',
|
|
48
|
+
noDataExpression: true,
|
|
49
|
+
displayOptions: {
|
|
50
|
+
show: {
|
|
51
|
+
resource: ['version'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
options: [
|
|
55
|
+
{
|
|
56
|
+
name: 'Get Info',
|
|
57
|
+
value: 'getInfo',
|
|
58
|
+
description: 'Get current and latest version info',
|
|
59
|
+
action: 'Get version info',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'Get Changelog',
|
|
63
|
+
value: 'getChangelog',
|
|
64
|
+
description: 'Fetch release notes from GitHub',
|
|
65
|
+
action: 'Get changelog',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
default: 'getInfo',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
displayName: 'Operation',
|
|
72
|
+
name: 'operation',
|
|
73
|
+
type: 'options',
|
|
74
|
+
noDataExpression: true,
|
|
75
|
+
displayOptions: {
|
|
76
|
+
show: {
|
|
77
|
+
resource: ['compatibility'],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
options: [
|
|
66
81
|
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
description: 'Whether to remove markdown links from the changelog text',
|
|
72
|
-
displayOptions: {
|
|
73
|
-
show: {
|
|
74
|
-
fetchChangelog: [true],
|
|
75
|
-
},
|
|
76
|
-
},
|
|
82
|
+
name: 'Audit Nodes',
|
|
83
|
+
value: 'audit',
|
|
84
|
+
description: 'Audit community nodes for compatibility',
|
|
85
|
+
action: 'Audit community nodes',
|
|
77
86
|
},
|
|
78
87
|
],
|
|
88
|
+
default: 'audit',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
displayName: 'Operation',
|
|
92
|
+
name: 'operation',
|
|
93
|
+
type: 'options',
|
|
94
|
+
noDataExpression: true,
|
|
95
|
+
displayOptions: {
|
|
96
|
+
show: {
|
|
97
|
+
resource: ['backup'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
name: 'Backup to Git',
|
|
103
|
+
value: 'gitBackup',
|
|
104
|
+
description: 'Backup all workflows to a Git repository',
|
|
105
|
+
action: 'Backup workflows to git',
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
default: 'gitBackup',
|
|
109
|
+
},
|
|
110
|
+
// Version Options
|
|
111
|
+
{
|
|
112
|
+
displayName: 'Version',
|
|
113
|
+
name: 'version',
|
|
114
|
+
type: 'options',
|
|
115
|
+
default: 'latest',
|
|
116
|
+
displayOptions: {
|
|
117
|
+
show: {
|
|
118
|
+
resource: ['version'],
|
|
119
|
+
operation: ['getChangelog'],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
options: [
|
|
123
|
+
{
|
|
124
|
+
name: 'Latest',
|
|
125
|
+
value: 'latest',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'Current',
|
|
129
|
+
value: 'current',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
displayName: 'Strip Links',
|
|
135
|
+
name: 'stripLinks',
|
|
136
|
+
type: 'boolean',
|
|
137
|
+
default: false,
|
|
138
|
+
displayOptions: {
|
|
139
|
+
show: {
|
|
140
|
+
resource: ['version'],
|
|
141
|
+
operation: ['getChangelog'],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
// Compatibility Options
|
|
146
|
+
{
|
|
147
|
+
displayName: 'Target n8n Version',
|
|
148
|
+
name: 'targetVersion',
|
|
149
|
+
type: 'string',
|
|
150
|
+
default: '',
|
|
151
|
+
placeholder: 'e.g. 1.0.0',
|
|
152
|
+
displayOptions: {
|
|
153
|
+
show: {
|
|
154
|
+
resource: ['compatibility'],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
description: 'The n8n version to check compatibility against. If empty, uses latest.',
|
|
158
|
+
},
|
|
159
|
+
// Backup Options
|
|
160
|
+
{
|
|
161
|
+
displayName: 'Repo URL',
|
|
162
|
+
name: 'repoUrl',
|
|
163
|
+
type: 'string',
|
|
164
|
+
default: '',
|
|
165
|
+
required: true,
|
|
166
|
+
displayOptions: {
|
|
167
|
+
show: {
|
|
168
|
+
resource: ['backup'],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
displayName: 'Branch',
|
|
174
|
+
name: 'branch',
|
|
175
|
+
type: 'string',
|
|
176
|
+
default: 'main',
|
|
177
|
+
displayOptions: {
|
|
178
|
+
show: {
|
|
179
|
+
resource: ['backup'],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
displayName: 'Local Path',
|
|
185
|
+
name: 'localPath',
|
|
186
|
+
type: 'string',
|
|
187
|
+
default: '/tmp/n8n-backup',
|
|
188
|
+
displayOptions: {
|
|
189
|
+
show: {
|
|
190
|
+
resource: ['backup'],
|
|
191
|
+
},
|
|
192
|
+
},
|
|
79
193
|
},
|
|
80
194
|
],
|
|
81
195
|
};
|
|
@@ -83,55 +197,45 @@ class N8nVersion {
|
|
|
83
197
|
async execute() {
|
|
84
198
|
const items = this.getInputData();
|
|
85
199
|
const returnData = [];
|
|
200
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
201
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
86
202
|
for (let i = 0; i < items.length; i++) {
|
|
87
203
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const json = {};
|
|
94
|
-
if (returnIsLatest) {
|
|
95
|
-
const versions = await (0, VersionUtils_1.checkIsLatest)();
|
|
96
|
-
json.currentVersion = versions.currentVersion;
|
|
97
|
-
json.latestVersion = versions.latestVersion;
|
|
98
|
-
json.isLatest = versions.isLatest;
|
|
99
|
-
if (versions.error) {
|
|
100
|
-
json.versionError = versions.error;
|
|
204
|
+
let json = {};
|
|
205
|
+
if (resource === 'version') {
|
|
206
|
+
if (operation === 'getInfo') {
|
|
207
|
+
const versions = await (0, VersionUtils_1.checkIsLatest)();
|
|
208
|
+
json = { ...versions };
|
|
101
209
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (changelogVersion === 'latest') {
|
|
109
|
-
if (returnIsLatest && json.latestVersion) {
|
|
110
|
-
versionToFetch = json.latestVersion;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
const latestVersion = await (0, VersionUtils_1.getLatestN8nVersion)();
|
|
114
|
-
versionToFetch = latestVersion;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
versionToFetch = json.currentVersion;
|
|
119
|
-
}
|
|
120
|
-
if (versionToFetch && versionToFetch !== 'unknown') {
|
|
210
|
+
else if (operation === 'getChangelog') {
|
|
211
|
+
const versionType = this.getNodeParameter('version', i);
|
|
212
|
+
const stripLinks = this.getNodeParameter('stripLinks', i);
|
|
213
|
+
let versionToFetch = versionType === 'latest'
|
|
214
|
+
? await (0, VersionUtils_1.getLatestN8nVersion)()
|
|
215
|
+
: (0, VersionUtils_1.getCurrentN8nVersion)();
|
|
121
216
|
const changelogInfo = await (0, VersionUtils_1.getChangelog)(versionToFetch);
|
|
122
|
-
|
|
123
|
-
if (stripLinks && changelog) {
|
|
124
|
-
changelog = (
|
|
125
|
-
}
|
|
126
|
-
json.changelog = changelog;
|
|
127
|
-
json.changelogVersion = changelogInfo.version;
|
|
128
|
-
if (changelogInfo.error) {
|
|
129
|
-
json.changelogError = changelogInfo.error;
|
|
217
|
+
json = { ...changelogInfo };
|
|
218
|
+
if (stripLinks && json.changelog) {
|
|
219
|
+
json.changelog = json.changelog.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1');
|
|
130
220
|
}
|
|
131
221
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
222
|
+
}
|
|
223
|
+
else if (resource === 'compatibility') {
|
|
224
|
+
const targetVersion = this.getNodeParameter('targetVersion', i) || await (0, VersionUtils_1.getLatestN8nVersion)();
|
|
225
|
+
const auditResults = await (0, CompatibilityUtils_1.auditAllNodes)(targetVersion);
|
|
226
|
+
json.results = auditResults;
|
|
227
|
+
json.targetVersion = targetVersion;
|
|
228
|
+
}
|
|
229
|
+
else if (resource === 'backup') {
|
|
230
|
+
const repoUrl = this.getNodeParameter('repoUrl', i);
|
|
231
|
+
const branch = this.getNodeParameter('branch', i);
|
|
232
|
+
const localPath = this.getNodeParameter('localPath', i);
|
|
233
|
+
const config = { repoUrl, branch, localPath };
|
|
234
|
+
(0, GitUtils_1.ensureGitRepo)(config);
|
|
235
|
+
await (0, GitUtils_1.exportWorkflowsToGit)(localPath);
|
|
236
|
+
(0, GitUtils_1.commitAndPush)(config);
|
|
237
|
+
json.success = true;
|
|
238
|
+
json.timestamp = new Date().toISOString();
|
|
135
239
|
}
|
|
136
240
|
returnData.push({
|
|
137
241
|
json,
|
|
@@ -148,8 +252,7 @@ class N8nVersion {
|
|
|
148
252
|
});
|
|
149
253
|
continue;
|
|
150
254
|
}
|
|
151
|
-
|
|
152
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to process version information: ${errorMessage}`, { itemIndex: i });
|
|
255
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
|
|
153
256
|
}
|
|
154
257
|
}
|
|
155
258
|
return [returnData];
|
|
@@ -7,14 +7,14 @@ const VersionUtils_1 = require("../VersionUtils");
|
|
|
7
7
|
class N8nVersionTrigger {
|
|
8
8
|
constructor() {
|
|
9
9
|
this.description = {
|
|
10
|
-
displayName: 'Version Trigger',
|
|
11
|
-
name: '
|
|
10
|
+
displayName: 'n8n Version Trigger',
|
|
11
|
+
name: 'n8nVersionTrigger',
|
|
12
12
|
icon: 'file:n8n-version.svg',
|
|
13
13
|
group: ['trigger'],
|
|
14
|
-
version:
|
|
14
|
+
version: 2,
|
|
15
15
|
description: 'Triggers workflow based on n8n version status using cron schedule',
|
|
16
16
|
defaults: {
|
|
17
|
-
name: 'Version Trigger',
|
|
17
|
+
name: 'n8n Version Trigger',
|
|
18
18
|
},
|
|
19
19
|
inputs: [],
|
|
20
20
|
outputs: ['main'],
|
|
@@ -41,11 +41,6 @@ class N8nVersionTrigger {
|
|
|
41
41
|
value: 'update',
|
|
42
42
|
description: 'Trigger only when a new version is available',
|
|
43
43
|
},
|
|
44
|
-
{
|
|
45
|
-
name: 'On Latest Version',
|
|
46
|
-
value: 'latest',
|
|
47
|
-
description: 'Trigger only when already on the latest version',
|
|
48
|
-
},
|
|
49
44
|
{
|
|
50
45
|
name: 'Always',
|
|
51
46
|
value: 'always',
|
|
@@ -53,26 +48,72 @@ class N8nVersionTrigger {
|
|
|
53
48
|
},
|
|
54
49
|
],
|
|
55
50
|
},
|
|
51
|
+
{
|
|
52
|
+
displayName: 'SemVer Level',
|
|
53
|
+
name: 'semverFilter',
|
|
54
|
+
type: 'options',
|
|
55
|
+
default: 'patch',
|
|
56
|
+
displayOptions: {
|
|
57
|
+
show: {
|
|
58
|
+
triggerCondition: ['update'],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
options: [
|
|
62
|
+
{
|
|
63
|
+
name: 'Major',
|
|
64
|
+
value: 'major',
|
|
65
|
+
description: 'Only trigger for major version updates (X.0.0)',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'Minor',
|
|
69
|
+
value: 'minor',
|
|
70
|
+
description: 'Trigger for major and minor version updates (X.Y.0)',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'Patch',
|
|
74
|
+
value: 'patch',
|
|
75
|
+
description: 'Trigger for all updates (X.Y.Z)',
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
description: 'Minimum relevance level of the update to trigger the workflow',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
displayName: 'Ignore Prereleases',
|
|
82
|
+
name: 'ignorePrerelease',
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
default: true,
|
|
85
|
+
description: 'Whether to ignore versions containing alpha, beta, rc, etc',
|
|
86
|
+
},
|
|
56
87
|
],
|
|
57
88
|
};
|
|
58
89
|
}
|
|
59
90
|
async trigger() {
|
|
60
91
|
const cronExpression = this.getNodeParameter('cronExpression');
|
|
61
92
|
const triggerCondition = this.getNodeParameter('triggerCondition');
|
|
93
|
+
const semverFilter = this.getNodeParameter('semverFilter', 'patch');
|
|
94
|
+
const ignorePrerelease = this.getNodeParameter('ignorePrerelease', true);
|
|
62
95
|
let job;
|
|
63
96
|
try {
|
|
64
97
|
job = new cron_1.CronJob(cronExpression, async () => {
|
|
65
98
|
try {
|
|
66
|
-
const { isLatest, currentVersion, latestVersion, error } = await (0, VersionUtils_1.checkIsLatest)();
|
|
99
|
+
const { isLatest, currentVersion, latestVersion, updateType, error } = await (0, VersionUtils_1.checkIsLatest)();
|
|
100
|
+
if (ignorePrerelease && (latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.includes('-'))) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
67
103
|
let shouldTrigger = false;
|
|
68
104
|
if (triggerCondition === 'always') {
|
|
69
105
|
shouldTrigger = true;
|
|
70
106
|
}
|
|
71
|
-
else if (triggerCondition === 'update') {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
else if (triggerCondition === 'update' && !isLatest) {
|
|
108
|
+
if (semverFilter === 'patch') {
|
|
109
|
+
shouldTrigger = true;
|
|
110
|
+
}
|
|
111
|
+
else if (semverFilter === 'minor' && (updateType === 'minor' || updateType === 'major')) {
|
|
112
|
+
shouldTrigger = true;
|
|
113
|
+
}
|
|
114
|
+
else if (semverFilter === 'major' && updateType === 'major') {
|
|
115
|
+
shouldTrigger = true;
|
|
116
|
+
}
|
|
76
117
|
}
|
|
77
118
|
if (shouldTrigger) {
|
|
78
119
|
this.emit([
|
|
@@ -82,6 +123,7 @@ class N8nVersionTrigger {
|
|
|
82
123
|
currentVersion,
|
|
83
124
|
latestVersion,
|
|
84
125
|
isLatest,
|
|
126
|
+
updateType,
|
|
85
127
|
error: error || undefined,
|
|
86
128
|
triggeredAt: new Date().toISOString(),
|
|
87
129
|
triggerCondition,
|
|
@@ -93,7 +135,6 @@ class N8nVersionTrigger {
|
|
|
93
135
|
}
|
|
94
136
|
catch (err) {
|
|
95
137
|
console.error('[VersionTrigger] Error during cron execution:', err);
|
|
96
|
-
// Don't stop the cron job on errors, just log them
|
|
97
138
|
}
|
|
98
139
|
}, null, true);
|
|
99
140
|
}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.stripMarkdownLinks = exports.cleanChangelog = exports.getChangelog = exports.checkIsLatest = exports.getCurrentN8nVersion = exports.getLatestN8nVersion = void 0;
|
|
6
|
+
exports.stripMarkdownLinks = exports.cleanChangelog = exports.parseChangelog = exports.getChangelog = exports.checkIsLatest = exports.getSemverDiff = exports.getCurrentN8nVersion = exports.getLatestN8nVersion = void 0;
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const https_1 = __importDefault(require("https"));
|
|
9
9
|
/**
|
|
@@ -70,9 +70,26 @@ function getCurrentN8nVersion() {
|
|
|
70
70
|
return currentVersion || 'unknown';
|
|
71
71
|
}
|
|
72
72
|
exports.getCurrentN8nVersion = getCurrentN8nVersion;
|
|
73
|
+
/**
|
|
74
|
+
* Compares two semantic versions and returns the difference type
|
|
75
|
+
*/
|
|
76
|
+
function getSemverDiff(v1, v2) {
|
|
77
|
+
if (v1 === v2 || v1 === 'unknown' || v2 === 'unknown')
|
|
78
|
+
return 'none';
|
|
79
|
+
const p1 = v1.split('.').map(Number);
|
|
80
|
+
const p2 = v2.split('.').map(Number);
|
|
81
|
+
if (p2[0] > p1[0])
|
|
82
|
+
return 'major';
|
|
83
|
+
if (p2[1] > p1[1])
|
|
84
|
+
return 'minor';
|
|
85
|
+
if (p2[2] > p1[2])
|
|
86
|
+
return 'patch';
|
|
87
|
+
return 'none';
|
|
88
|
+
}
|
|
89
|
+
exports.getSemverDiff = getSemverDiff;
|
|
73
90
|
/**
|
|
74
91
|
* Checks if the current n8n version is the latest available
|
|
75
|
-
* @returns Object with currentVersion, latestVersion,
|
|
92
|
+
* @returns Object with currentVersion, latestVersion, isLatest flag, and updateType
|
|
76
93
|
*/
|
|
77
94
|
async function checkIsLatest() {
|
|
78
95
|
const currentVersion = getCurrentN8nVersion();
|
|
@@ -80,10 +97,12 @@ async function checkIsLatest() {
|
|
|
80
97
|
const isLatest = currentVersion !== 'unknown' &&
|
|
81
98
|
latestVersion !== 'unknown' &&
|
|
82
99
|
currentVersion === latestVersion;
|
|
100
|
+
const updateType = getSemverDiff(currentVersion, latestVersion);
|
|
83
101
|
return {
|
|
84
102
|
currentVersion,
|
|
85
103
|
latestVersion,
|
|
86
104
|
isLatest,
|
|
105
|
+
updateType,
|
|
87
106
|
};
|
|
88
107
|
}
|
|
89
108
|
exports.checkIsLatest = checkIsLatest;
|
|
@@ -112,6 +131,8 @@ function getChangelog(version) {
|
|
|
112
131
|
resolve({
|
|
113
132
|
version,
|
|
114
133
|
changelog: '',
|
|
134
|
+
breakingChanges: [],
|
|
135
|
+
deprecated: [],
|
|
115
136
|
error: `Release n8n@${version} not found on GitHub`,
|
|
116
137
|
});
|
|
117
138
|
return;
|
|
@@ -120,14 +141,19 @@ function getChangelog(version) {
|
|
|
120
141
|
resolve({
|
|
121
142
|
version,
|
|
122
143
|
changelog: '',
|
|
144
|
+
breakingChanges: [],
|
|
145
|
+
deprecated: [],
|
|
123
146
|
error: `GitHub API returned status ${res.statusCode}`,
|
|
124
147
|
});
|
|
125
148
|
return;
|
|
126
149
|
}
|
|
127
150
|
const parsed = JSON.parse(data);
|
|
151
|
+
const { changelog, breakingChanges, deprecated } = parseChangelog(parsed.body || '');
|
|
128
152
|
resolve({
|
|
129
153
|
version,
|
|
130
|
-
changelog
|
|
154
|
+
changelog,
|
|
155
|
+
breakingChanges,
|
|
156
|
+
deprecated,
|
|
131
157
|
});
|
|
132
158
|
}
|
|
133
159
|
catch (err) {
|
|
@@ -135,6 +161,8 @@ function getChangelog(version) {
|
|
|
135
161
|
resolve({
|
|
136
162
|
version,
|
|
137
163
|
changelog: '',
|
|
164
|
+
breakingChanges: [],
|
|
165
|
+
deprecated: [],
|
|
138
166
|
error: 'Failed to parse GitHub response',
|
|
139
167
|
});
|
|
140
168
|
}
|
|
@@ -145,6 +173,8 @@ function getChangelog(version) {
|
|
|
145
173
|
resolve({
|
|
146
174
|
version,
|
|
147
175
|
changelog: '',
|
|
176
|
+
breakingChanges: [],
|
|
177
|
+
deprecated: [],
|
|
148
178
|
error: `Failed to fetch from GitHub: ${err.message}`,
|
|
149
179
|
});
|
|
150
180
|
});
|
|
@@ -154,24 +184,64 @@ function getChangelog(version) {
|
|
|
154
184
|
resolve({
|
|
155
185
|
version,
|
|
156
186
|
changelog: '',
|
|
187
|
+
breakingChanges: [],
|
|
188
|
+
deprecated: [],
|
|
157
189
|
error: `Unexpected error: ${err}`,
|
|
158
190
|
});
|
|
159
191
|
}
|
|
160
192
|
});
|
|
161
193
|
}
|
|
162
194
|
exports.getChangelog = getChangelog;
|
|
195
|
+
/**
|
|
196
|
+
* Cleans changelog text and extracts breaking changes and deprecations
|
|
197
|
+
* @param text - Raw changelog text
|
|
198
|
+
* @returns Cleaned text and extracted sections
|
|
199
|
+
*/
|
|
200
|
+
function parseChangelog(text) {
|
|
201
|
+
if (!text)
|
|
202
|
+
return { changelog: '', breakingChanges: [], deprecated: [] };
|
|
203
|
+
const lines = text.replace(/\r\n/g, '\n').split('\n');
|
|
204
|
+
const breakingChanges = [];
|
|
205
|
+
const deprecated = [];
|
|
206
|
+
let currentSection = 'none';
|
|
207
|
+
for (const line of lines) {
|
|
208
|
+
const lowerLine = line.toLowerCase();
|
|
209
|
+
if (lowerLine.includes('breaking change')) {
|
|
210
|
+
currentSection = 'breaking';
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
else if (lowerLine.includes('deprecated')) {
|
|
214
|
+
currentSection = 'deprecated';
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
else if (line.startsWith('#')) {
|
|
218
|
+
currentSection = 'none';
|
|
219
|
+
}
|
|
220
|
+
if (currentSection === 'breaking' && line.trim().startsWith('-')) {
|
|
221
|
+
breakingChanges.push(line.trim().substring(1).trim());
|
|
222
|
+
}
|
|
223
|
+
else if (currentSection === 'deprecated' && line.trim().startsWith('-')) {
|
|
224
|
+
deprecated.push(line.trim().substring(1).trim());
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const cleaned = text
|
|
228
|
+
.replace(/\r\n/g, '\n')
|
|
229
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
230
|
+
.trim();
|
|
231
|
+
return {
|
|
232
|
+
changelog: cleaned,
|
|
233
|
+
breakingChanges,
|
|
234
|
+
deprecated,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
exports.parseChangelog = parseChangelog;
|
|
163
238
|
/**
|
|
164
239
|
* Cleans changelog text by normalizing line breaks and removing artifacts
|
|
165
240
|
* @param text - Raw changelog text
|
|
166
241
|
* @returns Cleaned text
|
|
167
242
|
*/
|
|
168
243
|
function cleanChangelog(text) {
|
|
169
|
-
|
|
170
|
-
return '';
|
|
171
|
-
return text
|
|
172
|
-
.replace(/\r\n/g, '\n') // Normalize Windows line endings
|
|
173
|
-
.replace(/\n{3,}/g, '\n\n') // Max 2 consecutive newlines
|
|
174
|
-
.trim();
|
|
244
|
+
return parseChangelog(text).changelog;
|
|
175
245
|
}
|
|
176
246
|
exports.cleanChangelog = cleanChangelog;
|
|
177
247
|
/**
|