n8n-nodes-version 0.3.1 → 0.5.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 CHANGED
@@ -56,9 +56,12 @@ A schedule-based trigger with advanced filtering.
56
56
 
57
57
  **Configuration:**
58
58
 
59
- - **Cron Expression**: Define check frequency (e.g., `0 * * * *`).
59
+ **Configuration:**
60
+
61
+ - **Trigger On**: Choose between `Interval` (simple presets) or `Custom Cron Expression`.
60
62
  - **Trigger Condition**: Choose between `Update Available` or `Always`.
61
63
  - **SemVer Level**: Filter triggers by `Major`, `Minor`, or `Patch` update relevance.
64
+ - **Fetch Changelog**: Optionally fetch and include release notes in the trigger output.
62
65
  - **Ignore Prereleases**: Option to skip alpha, beta, and RC versions.
63
66
 
64
67
  ---
@@ -3,8 +3,6 @@ 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");
8
6
  class N8nVersion {
9
7
  constructor() {
10
8
  this.description = {
@@ -12,8 +10,8 @@ class N8nVersion {
12
10
  name: 'n8nVersion',
13
11
  icon: 'file:n8n-version.svg',
14
12
  group: ['transform'],
15
- version: 2,
16
- description: 'Manage n8n versions, backups, and compatibility audits',
13
+ version: 3,
14
+ description: 'Manage n8n versions and check changelogs',
17
15
  defaults: {
18
16
  name: 'n8n Version',
19
17
  },
@@ -30,14 +28,6 @@ class N8nVersion {
30
28
  name: 'Version Info',
31
29
  value: 'version',
32
30
  },
33
- {
34
- name: 'Compatibility',
35
- value: 'compatibility',
36
- },
37
- {
38
- name: 'Backup',
39
- value: 'backup',
40
- },
41
31
  ],
42
32
  default: 'version',
43
33
  },
@@ -67,46 +57,6 @@ class N8nVersion {
67
57
  ],
68
58
  default: 'getInfo',
69
59
  },
70
- {
71
- displayName: 'Operation',
72
- name: 'operation',
73
- type: 'options',
74
- noDataExpression: true,
75
- displayOptions: {
76
- show: {
77
- resource: ['compatibility'],
78
- },
79
- },
80
- options: [
81
- {
82
- name: 'Audit Nodes',
83
- value: 'audit',
84
- description: 'Audit community nodes for compatibility',
85
- action: 'Audit community nodes',
86
- },
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
60
  // Version Options
111
61
  {
112
62
  displayName: 'Version',
@@ -128,66 +78,35 @@ class N8nVersion {
128
78
  name: 'Current',
129
79
  value: 'current',
130
80
  },
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'],
81
+ {
82
+ name: 'Custom',
83
+ value: 'custom',
155
84
  },
156
- },
157
- description: 'The n8n version to check compatibility against. If empty, uses latest.',
85
+ ],
158
86
  },
159
- // Backup Options
160
87
  {
161
- displayName: 'Repo URL',
162
- name: 'repoUrl',
88
+ displayName: 'Custom Version',
89
+ name: 'customVersion',
163
90
  type: 'string',
164
91
  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',
92
+ placeholder: '1.0.0',
177
93
  displayOptions: {
178
94
  show: {
179
- resource: ['backup'],
95
+ resource: ['version'],
96
+ operation: ['getChangelog'],
97
+ version: ['custom'],
180
98
  },
181
99
  },
182
100
  },
183
101
  {
184
- displayName: 'Local Path',
185
- name: 'localPath',
186
- type: 'string',
187
- default: '/tmp/n8n-backup',
102
+ displayName: 'Strip Links',
103
+ name: 'stripLinks',
104
+ type: 'boolean',
105
+ default: false,
188
106
  displayOptions: {
189
107
  show: {
190
- resource: ['backup'],
108
+ resource: ['version'],
109
+ operation: ['getChangelog'],
191
110
  },
192
111
  },
193
112
  },
@@ -210,33 +129,30 @@ class N8nVersion {
210
129
  else if (operation === 'getChangelog') {
211
130
  const versionType = this.getNodeParameter('version', i);
212
131
  const stripLinks = this.getNodeParameter('stripLinks', i);
213
- let versionToFetch = versionType === 'latest'
214
- ? await (0, VersionUtils_1.getLatestN8nVersion)()
215
- : (0, VersionUtils_1.getCurrentN8nVersion)();
132
+ let versionToFetch;
133
+ if (versionType === 'latest') {
134
+ versionToFetch = await (0, VersionUtils_1.getLatestN8nVersion)();
135
+ }
136
+ else if (versionType === 'current') {
137
+ versionToFetch = (0, VersionUtils_1.getCurrentN8nVersion)();
138
+ }
139
+ else {
140
+ versionToFetch = this.getNodeParameter('customVersion', i);
141
+ }
216
142
  const changelogInfo = await (0, VersionUtils_1.getChangelog)(versionToFetch);
217
- json = { ...changelogInfo };
143
+ // Simplify output as requested
144
+ json = {
145
+ version: changelogInfo.version,
146
+ changelog: changelogInfo.changelog,
147
+ };
148
+ if (changelogInfo.error) {
149
+ json.error = changelogInfo.error;
150
+ }
218
151
  if (stripLinks && json.changelog) {
219
152
  json.changelog = json.changelog.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1');
220
153
  }
221
154
  }
222
155
  }
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();
239
- }
240
156
  returnData.push({
241
157
  json,
242
158
  pairedItem: { item: i },
@@ -1,8 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.N8nVersionTrigger = void 0;
4
- const n8n_workflow_1 = require("n8n-workflow");
5
- const cron_1 = require("cron");
6
4
  const VersionUtils_1 = require("../VersionUtils");
7
5
  class N8nVersionTrigger {
8
6
  constructor() {
@@ -11,8 +9,8 @@ class N8nVersionTrigger {
11
9
  name: 'n8nVersionTrigger',
12
10
  icon: 'file:n8n-version.svg',
13
11
  group: ['trigger'],
14
- version: 2,
15
- description: 'Triggers workflow based on n8n version status using cron schedule',
12
+ version: 3,
13
+ description: 'Triggers workflow based on n8n version status',
16
14
  defaults: {
17
15
  name: 'n8n Version Trigger',
18
16
  },
@@ -20,13 +18,25 @@ class N8nVersionTrigger {
20
18
  outputs: ['main'],
21
19
  properties: [
22
20
  {
23
- displayName: 'Cron Expression',
24
- name: 'cronExpression',
25
- type: 'string',
26
- default: '0 * * * *',
27
- required: true,
28
- placeholder: '0 * * * *',
29
- description: 'Cron expression for when to check version. Examples: "0 * * * *" (hourly), "0 0 * * *" (daily), "0 0 * * 0" (weekly).',
21
+ displayName: 'Trigger Frequency',
22
+ name: 'interval',
23
+ type: 'options',
24
+ options: [
25
+ {
26
+ name: 'Every Minute',
27
+ value: 60,
28
+ },
29
+ {
30
+ name: 'Every Hour',
31
+ value: 3600,
32
+ },
33
+ {
34
+ name: 'Every Day',
35
+ value: 86400,
36
+ },
37
+ ],
38
+ default: 3600,
39
+ description: 'How often to check for updates (in seconds)',
30
40
  },
31
41
  {
32
42
  displayName: 'Trigger Condition',
@@ -84,68 +94,124 @@ class N8nVersionTrigger {
84
94
  default: true,
85
95
  description: 'Whether to ignore versions containing alpha, beta, rc, etc',
86
96
  },
97
+ // Changelog Options
98
+ {
99
+ displayName: 'Fetch Changelog',
100
+ name: 'fetchChangelog',
101
+ type: 'boolean',
102
+ default: false,
103
+ description: 'Whether to fetch release notes when triggering',
104
+ },
105
+ {
106
+ displayName: 'Changelog Version',
107
+ name: 'changelogVersion',
108
+ type: 'options',
109
+ default: 'latest',
110
+ displayOptions: {
111
+ show: {
112
+ fetchChangelog: [true],
113
+ },
114
+ },
115
+ options: [
116
+ {
117
+ name: 'Latest Version',
118
+ value: 'latest',
119
+ description: 'Fetch changelog for the latest version from npm',
120
+ },
121
+ {
122
+ name: 'Current Version',
123
+ value: 'current',
124
+ description: 'Fetch changelog for the currently installed version',
125
+ },
126
+ ],
127
+ },
128
+ {
129
+ displayName: 'Strip Links',
130
+ name: 'stripLinks',
131
+ type: 'boolean',
132
+ default: false,
133
+ displayOptions: {
134
+ show: {
135
+ fetchChangelog: [true],
136
+ },
137
+ },
138
+ },
87
139
  ],
88
140
  };
89
141
  }
90
142
  async trigger() {
91
- const cronExpression = this.getNodeParameter('cronExpression');
143
+ const intervalSeconds = this.getNodeParameter('interval', 3600);
92
144
  const triggerCondition = this.getNodeParameter('triggerCondition');
93
145
  const semverFilter = this.getNodeParameter('semverFilter', 'patch');
94
146
  const ignorePrerelease = this.getNodeParameter('ignorePrerelease', true);
95
- let job;
96
- try {
97
- job = new cron_1.CronJob(cronExpression, async () => {
98
- try {
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
- }
103
- let shouldTrigger = false;
104
- if (triggerCondition === 'always') {
147
+ const fetchChangelog = this.getNodeParameter('fetchChangelog', false);
148
+ const changelogVersion = this.getNodeParameter('changelogVersion', 'latest');
149
+ const stripLinks = this.getNodeParameter('stripLinks', false);
150
+ // Initial check immediately? usually triggers wait.
151
+ const executeTrigger = async () => {
152
+ try {
153
+ const { isLatest, currentVersion, latestVersion, updateType, error } = await (0, VersionUtils_1.checkIsLatest)();
154
+ if (ignorePrerelease && (latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.includes('-'))) {
155
+ return;
156
+ }
157
+ let shouldTrigger = false;
158
+ if (triggerCondition === 'always') {
159
+ shouldTrigger = true;
160
+ }
161
+ else if (triggerCondition === 'update' && !isLatest) {
162
+ if (semverFilter === 'patch') {
105
163
  shouldTrigger = true;
106
164
  }
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
- }
165
+ else if (semverFilter === 'minor' && (updateType === 'minor' || updateType === 'major')) {
166
+ shouldTrigger = true;
117
167
  }
118
- if (shouldTrigger) {
119
- this.emit([
120
- [
121
- {
122
- json: {
123
- currentVersion,
124
- latestVersion,
125
- isLatest,
126
- updateType,
127
- error: error || undefined,
128
- triggeredAt: new Date().toISOString(),
129
- triggerCondition,
130
- },
131
- },
132
- ],
133
- ]);
168
+ else if (semverFilter === 'major' && updateType === 'major') {
169
+ shouldTrigger = true;
134
170
  }
135
171
  }
136
- catch (err) {
137
- console.error('[VersionTrigger] Error during cron execution:', err);
172
+ if (shouldTrigger) {
173
+ const json = {
174
+ currentVersion,
175
+ latestVersion,
176
+ isLatest,
177
+ updateType,
178
+ error: error || undefined,
179
+ triggeredAt: new Date().toISOString(),
180
+ triggerCondition,
181
+ };
182
+ if (fetchChangelog) {
183
+ let versionToFetch = changelogVersion === 'latest'
184
+ ? (latestVersion || await (0, VersionUtils_1.getLatestN8nVersion)())
185
+ : currentVersion;
186
+ if (versionToFetch && versionToFetch !== 'unknown') {
187
+ const changelogInfo = await (0, VersionUtils_1.getChangelog)(versionToFetch);
188
+ json.changelog = changelogInfo.changelog;
189
+ json.changelogVersion = changelogInfo.version;
190
+ if (changelogInfo.error)
191
+ json.changelogError = changelogInfo.error;
192
+ if (stripLinks && json.changelog) {
193
+ json.changelog = json.changelog.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1');
194
+ }
195
+ }
196
+ }
197
+ this.emit([
198
+ [
199
+ {
200
+ json,
201
+ },
202
+ ],
203
+ ]);
138
204
  }
139
- }, null, true);
140
- }
141
- catch (err) {
142
- const errorMessage = err instanceof Error ? err.message : String(err);
143
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid cron expression: ${errorMessage}. Please use standard cron format (e.g., "0 * * * *" for hourly).`);
144
- }
145
- async function closeFunction() {
146
- if (job) {
147
- job.stop();
148
205
  }
206
+ catch (err) {
207
+ console.error('[VersionTrigger] Error during check:', err);
208
+ }
209
+ };
210
+ // Run one check immediately? Standards usually say triggers start by waiting, or execute once if "Execute Trigger" logic.
211
+ // `setInterval` waits first.
212
+ const intervalId = setInterval(executeTrigger, intervalSeconds * 1000);
213
+ async function closeFunction() {
214
+ clearInterval(intervalId);
149
215
  }
150
216
  return {
151
217
  closeFunction,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "n8n-nodes-version",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
- "description": "n8n nodes to check version, monitor updates, and fetch changelogs",
5
+ "description": "n8n node to check for new n8n versions, view changelogs, and inspect workflows",
6
6
  "keywords": [
7
7
  "n8n-community-node-package"
8
8
  ],
@@ -1,78 +0,0 @@
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;
@@ -1,63 +0,0 @@
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;