n8n-nodes-version 0.2.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 +80 -0
- package/dist/index.js +6 -0
- package/dist/nodes/N8nChangelog/N8nChangelog.node.js +178 -0
- package/dist/nodes/N8nChangelog/n8n-version.svg +1 -0
- package/dist/nodes/N8nVersion/N8nVersion.node.js +158 -0
- package/dist/nodes/N8nVersion/n8n-version.svg +1 -0
- package/dist/nodes/N8nVersionTrigger/N8nVersionTrigger.node.js +114 -0
- package/dist/nodes/N8nVersionTrigger/n8n-version.svg +1 -0
- package/dist/nodes/VersionUtils.js +185 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# n8n-version-node
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
|
|
5
|
+
>
|
|
6
|
+
> [Icon](https://icons8.com/icon/9sDRgODM0vNc/refresh) is from [icons8.com](https://icons8.com/)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Check current installed n8n version
|
|
11
|
+
- Fetch latest version from [npm](https://www.npmjs.com/package/n8n)
|
|
12
|
+
- Compare versions and determine if current is latest
|
|
13
|
+
- Fetch release notes from GitHub
|
|
14
|
+
- Trigger workflows based on version status
|
|
15
|
+
|
|
16
|
+
## Nodes
|
|
17
|
+
|
|
18
|
+
### Version Checker
|
|
19
|
+
|
|
20
|
+
Checks the current n8n version and provides various options:
|
|
21
|
+
|
|
22
|
+
**Options:**
|
|
23
|
+
|
|
24
|
+
- **Return Latest Version Info** (default: `true`): Fetches latest version from npm and compares with current
|
|
25
|
+
- **Fetch Changelog** (default: `false`): Retrieves release notes from GitHub
|
|
26
|
+
- **Changelog Version**: Choose between current or latest version
|
|
27
|
+
- **Strip Links from Changelog**: Remove markdown links from changelog text
|
|
28
|
+
|
|
29
|
+
**Output Example:**
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"currentVersion": "1.0.0",
|
|
34
|
+
"latestVersion": "1.1.0",
|
|
35
|
+
"isLatest": false,
|
|
36
|
+
"changelog": "## Bug Fixes\n- Fixed issue with...",
|
|
37
|
+
"changelogVersion": "1.1.0"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Version Trigger
|
|
42
|
+
|
|
43
|
+
Triggers workflows based on n8n version status using cron scheduling.
|
|
44
|
+
|
|
45
|
+
**Properties:**
|
|
46
|
+
|
|
47
|
+
- **Cron Expression**: Schedule for version checks (e.g., `0 * * * *` for hourly)
|
|
48
|
+
- **Trigger Condition**:
|
|
49
|
+
- **Update Available**: Trigger when new version exists
|
|
50
|
+
- **On Latest Version**: Trigger when already up-to-date
|
|
51
|
+
- **Always**: Trigger on every check
|
|
52
|
+
|
|
53
|
+
**Use Cases:**
|
|
54
|
+
|
|
55
|
+
- Automated update notifications
|
|
56
|
+
- Monitoring version status
|
|
57
|
+
- Scheduled version audits
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install n8n-nodes-version
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Watch mode
|
|
69
|
+
npm run dev
|
|
70
|
+
|
|
71
|
+
# Build
|
|
72
|
+
npm run build
|
|
73
|
+
|
|
74
|
+
# Lint
|
|
75
|
+
npm run lint
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file is essentially empty in the project provided, but typically would contain exports.
|
|
3
|
+
// If n8n looks for an entry point, it usually just scans package.json 'n8n' field.
|
|
4
|
+
// However, 'main' is set to 'dist/index.js', so we should ensure source exists if tsconfig expects it.
|
|
5
|
+
// Since tsconfig rootDir is ./, we should create an index.ts if it doesn't exist, or just rely on nodes.
|
|
6
|
+
// But to be safe and clean, let's create a simple index.ts that exports nothing or acts as placeholder.
|
|
@@ -0,0 +1,178 @@
|
|
|
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.N8nChangelog = void 0;
|
|
7
|
+
const https_1 = __importDefault(require("https"));
|
|
8
|
+
/**
|
|
9
|
+
* Fetch changelog from GitHub releases
|
|
10
|
+
*/
|
|
11
|
+
function getChangelog(version) {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
try {
|
|
14
|
+
const options = {
|
|
15
|
+
hostname: 'api.github.com',
|
|
16
|
+
path: `/repos/n8n-io/n8n/releases/tags/n8n@${version}`,
|
|
17
|
+
headers: {
|
|
18
|
+
'User-Agent': 'n8n-changelog-node',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
https_1.default
|
|
22
|
+
.get(options, (res) => {
|
|
23
|
+
let data = '';
|
|
24
|
+
res.on('data', (chunk) => (data += chunk));
|
|
25
|
+
res.on('end', () => {
|
|
26
|
+
try {
|
|
27
|
+
if (res.statusCode === 404) {
|
|
28
|
+
resolve({ body: '', error: `Release n8n@${version} not found` });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (res.statusCode !== 200) {
|
|
32
|
+
resolve({ body: '', error: `GitHub API returned status ${res.statusCode}` });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const parsed = JSON.parse(data);
|
|
36
|
+
resolve({ body: parsed.body || '' });
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
console.error('[Changelog] Failed to parse GitHub response:', err);
|
|
40
|
+
resolve({ body: '', error: 'Failed to parse GitHub response' });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
})
|
|
44
|
+
.on('error', (err) => {
|
|
45
|
+
console.error('[Changelog] Failed to fetch from GitHub:', err);
|
|
46
|
+
resolve({ body: '', error: `Failed to fetch from GitHub: ${err.message}` });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error('[Changelog] Unexpected error:', err);
|
|
51
|
+
resolve({ body: '', error: `Unexpected error: ${err}` });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Strip markdown links from text
|
|
57
|
+
*/
|
|
58
|
+
function stripMarkdownLinks(text) {
|
|
59
|
+
return text.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1');
|
|
60
|
+
}
|
|
61
|
+
class N8nChangelog {
|
|
62
|
+
constructor() {
|
|
63
|
+
this.description = {
|
|
64
|
+
displayName: 'n8n Changelog',
|
|
65
|
+
name: 'n8nChangelog',
|
|
66
|
+
icon: 'file:n8n-version.svg',
|
|
67
|
+
group: ['transform'],
|
|
68
|
+
version: 1,
|
|
69
|
+
description: 'Fetches changelog for a specific n8n version from GitHub',
|
|
70
|
+
defaults: {
|
|
71
|
+
name: 'n8n Changelog',
|
|
72
|
+
},
|
|
73
|
+
inputs: ['main'],
|
|
74
|
+
outputs: ['main'],
|
|
75
|
+
properties: [
|
|
76
|
+
{
|
|
77
|
+
displayName: 'Version Source',
|
|
78
|
+
name: 'versionSource',
|
|
79
|
+
type: 'options',
|
|
80
|
+
options: [
|
|
81
|
+
{
|
|
82
|
+
name: 'From Parameter',
|
|
83
|
+
value: 'parameter',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'From Input',
|
|
87
|
+
value: 'input',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
default: 'parameter',
|
|
91
|
+
description: 'Where to get the version from',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
displayName: 'Version',
|
|
95
|
+
name: 'version',
|
|
96
|
+
type: 'string',
|
|
97
|
+
default: '',
|
|
98
|
+
placeholder: '1.0.0',
|
|
99
|
+
description: 'The n8n version to fetch changelog for (e.g., "1.0.0")',
|
|
100
|
+
displayOptions: {
|
|
101
|
+
show: {
|
|
102
|
+
versionSource: ['parameter'],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
displayName: 'Version Field',
|
|
108
|
+
name: 'versionField',
|
|
109
|
+
type: 'string',
|
|
110
|
+
default: 'latestVersion',
|
|
111
|
+
description: 'The field name containing the version in input data',
|
|
112
|
+
displayOptions: {
|
|
113
|
+
show: {
|
|
114
|
+
versionSource: ['input'],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
displayName: 'Include Links',
|
|
120
|
+
name: 'includeLinks',
|
|
121
|
+
type: 'boolean',
|
|
122
|
+
default: true,
|
|
123
|
+
description: 'Whether to keep markdown links in the changelog text',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async execute() {
|
|
129
|
+
const items = this.getInputData();
|
|
130
|
+
const returnData = [];
|
|
131
|
+
for (let i = 0; i < items.length; i++) {
|
|
132
|
+
try {
|
|
133
|
+
const versionSource = this.getNodeParameter('versionSource', i);
|
|
134
|
+
const includeLinks = this.getNodeParameter('includeLinks', i);
|
|
135
|
+
let version;
|
|
136
|
+
if (versionSource === 'input') {
|
|
137
|
+
const versionField = this.getNodeParameter('versionField', i);
|
|
138
|
+
version = items[i].json[versionField];
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
version = this.getNodeParameter('version', i);
|
|
142
|
+
}
|
|
143
|
+
if (!version) {
|
|
144
|
+
returnData.push({
|
|
145
|
+
json: {
|
|
146
|
+
error: 'No version provided',
|
|
147
|
+
changelog: '',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const { body, error } = await getChangelog(version);
|
|
153
|
+
let changelog = body;
|
|
154
|
+
if (!includeLinks && changelog) {
|
|
155
|
+
changelog = stripMarkdownLinks(changelog);
|
|
156
|
+
}
|
|
157
|
+
returnData.push({
|
|
158
|
+
json: {
|
|
159
|
+
version,
|
|
160
|
+
changelog,
|
|
161
|
+
error: error || undefined,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.error('[Changelog] Error processing item:', err);
|
|
167
|
+
returnData.push({
|
|
168
|
+
json: {
|
|
169
|
+
error: `Failed to process: ${err}`,
|
|
170
|
+
changelog: '',
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return [returnData];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.N8nChangelog = N8nChangelog;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="256px" height="256px" fill-rule="nonzero"><g fill="#f188a2" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M20,3c-5.514,0 -10,4.486 -10,10v20.17188l-3.58594,-3.58594c-0.37701,-0.38755 -0.89487,-0.60597 -1.43555,-0.60547c-0.81349,0.00101 -1.54533,0.49459 -1.85108,1.24844c-0.30574,0.75385 -0.12447,1.61777 0.4585,2.18515l7,7c0.78106,0.78074 2.04706,0.78074 2.82812,0l7,-7c0.52248,-0.50163 0.73295,-1.24653 0.55024,-1.94742c-0.18271,-0.70088 -0.73006,-1.24823 -1.43094,-1.43094c-0.70088,-0.18271 -1.44578,0.02776 -1.94742,0.55024l-3.58594,3.58594v-20.17187c0,-3.309 2.691,-6 6,-6h10.46875l0.26563,-4zM37.9707,10c-0.52023,0.00778 -1.01695,0.21796 -1.38477,0.58594l-7,7c-0.52248,0.50163 -0.73295,1.24653 -0.55024,1.94742c0.18271,0.70088 0.73006,1.24823 1.43094,1.43094c0.70088,0.18271 1.44578,-0.02776 1.94742,-0.55024l3.58594,-3.58594v20.17188c0,3.309 -2.691,6 -6,6h-10.49023l-0.25391,4h10.74414c5.514,0 10,-4.486 10,-10v-20.17187l3.58594,3.58594c0.50163,0.52248 1.24653,0.73295 1.94742,0.55024c0.70088,-0.18271 1.24823,-0.73006 1.43094,-1.43094c0.18271,-0.70088 -0.02776,-1.44578 -0.55024,-1.94742l-7,-7c-0.38217,-0.38233 -0.90283,-0.5937 -1.44336,-0.58594z"></path></g></g></svg>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.N8nVersion = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const VersionUtils_1 = require("../VersionUtils");
|
|
6
|
+
class N8nVersion {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.description = {
|
|
9
|
+
displayName: 'Version Checker',
|
|
10
|
+
name: 'versionChecker',
|
|
11
|
+
icon: 'file:n8n-version.svg',
|
|
12
|
+
group: ['transform'],
|
|
13
|
+
version: 1,
|
|
14
|
+
description: 'Check current n8n version and optionally fetch changelog from GitHub',
|
|
15
|
+
defaults: {
|
|
16
|
+
name: 'Version Checker',
|
|
17
|
+
},
|
|
18
|
+
inputs: ['main'],
|
|
19
|
+
outputs: ['main'],
|
|
20
|
+
properties: [
|
|
21
|
+
{
|
|
22
|
+
displayName: 'Options',
|
|
23
|
+
name: 'options',
|
|
24
|
+
type: 'collection',
|
|
25
|
+
placeholder: 'Add Option',
|
|
26
|
+
default: {},
|
|
27
|
+
options: [
|
|
28
|
+
{
|
|
29
|
+
displayName: 'Return Latest Version Info',
|
|
30
|
+
name: 'returnIsLatest',
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
default: true,
|
|
33
|
+
description: 'Whether to check and return if the current version is the latest available on npm',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
displayName: 'Fetch Changelog',
|
|
37
|
+
name: 'fetchChangelog',
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
default: false,
|
|
40
|
+
description: 'Whether to fetch release notes from GitHub. Note: Subject to GitHub API rate limits.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Changelog Version',
|
|
44
|
+
name: 'changelogVersion',
|
|
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
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
displayName: 'Strip Links from Changelog',
|
|
68
|
+
name: 'stripLinks',
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
default: false,
|
|
71
|
+
description: 'Whether to remove markdown links from the changelog text',
|
|
72
|
+
displayOptions: {
|
|
73
|
+
show: {
|
|
74
|
+
fetchChangelog: [true],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async execute() {
|
|
84
|
+
const items = this.getInputData();
|
|
85
|
+
const returnData = [];
|
|
86
|
+
for (let i = 0; i < items.length; i++) {
|
|
87
|
+
try {
|
|
88
|
+
const options = this.getNodeParameter('options', i, {});
|
|
89
|
+
const returnIsLatest = options.returnIsLatest !== false;
|
|
90
|
+
const fetchChangelog = options.fetchChangelog === true;
|
|
91
|
+
const changelogVersion = options.changelogVersion || 'latest';
|
|
92
|
+
const stripLinks = options.stripLinks === true;
|
|
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;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
json.currentVersion = (0, VersionUtils_1.getCurrentN8nVersion)();
|
|
105
|
+
}
|
|
106
|
+
if (fetchChangelog) {
|
|
107
|
+
let versionToFetch;
|
|
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') {
|
|
121
|
+
const changelogInfo = await (0, VersionUtils_1.getChangelog)(versionToFetch);
|
|
122
|
+
let changelog = (0, VersionUtils_1.cleanChangelog)(changelogInfo.changelog);
|
|
123
|
+
if (stripLinks && changelog) {
|
|
124
|
+
changelog = (0, VersionUtils_1.stripMarkdownLinks)(changelog);
|
|
125
|
+
}
|
|
126
|
+
json.changelog = changelog;
|
|
127
|
+
json.changelogVersion = changelogInfo.version;
|
|
128
|
+
if (changelogInfo.error) {
|
|
129
|
+
json.changelogError = changelogInfo.error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
json.changelogError = 'Unable to determine version for changelog fetch';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
returnData.push({
|
|
137
|
+
json,
|
|
138
|
+
pairedItem: { item: i },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (this.continueOnFail()) {
|
|
143
|
+
returnData.push({
|
|
144
|
+
json: {
|
|
145
|
+
error: error instanceof Error ? error.message : String(error),
|
|
146
|
+
},
|
|
147
|
+
pairedItem: { item: i },
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
152
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to process version information: ${errorMessage}`, { itemIndex: i });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return [returnData];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.N8nVersion = N8nVersion;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="256px" height="256px" fill-rule="nonzero"><g fill="#f188a2" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M20,3c-5.514,0 -10,4.486 -10,10v20.17188l-3.58594,-3.58594c-0.37701,-0.38755 -0.89487,-0.60597 -1.43555,-0.60547c-0.81349,0.00101 -1.54533,0.49459 -1.85108,1.24844c-0.30574,0.75385 -0.12447,1.61777 0.4585,2.18515l7,7c0.78106,0.78074 2.04706,0.78074 2.82812,0l7,-7c0.52248,-0.50163 0.73295,-1.24653 0.55024,-1.94742c-0.18271,-0.70088 -0.73006,-1.24823 -1.43094,-1.43094c-0.70088,-0.18271 -1.44578,0.02776 -1.94742,0.55024l-3.58594,3.58594v-20.17187c0,-3.309 2.691,-6 6,-6h10.46875l0.26563,-4zM37.9707,10c-0.52023,0.00778 -1.01695,0.21796 -1.38477,0.58594l-7,7c-0.52248,0.50163 -0.73295,1.24653 -0.55024,1.94742c0.18271,0.70088 0.73006,1.24823 1.43094,1.43094c0.70088,0.18271 1.44578,-0.02776 1.94742,-0.55024l3.58594,-3.58594v20.17188c0,3.309 -2.691,6 -6,6h-10.49023l-0.25391,4h10.74414c5.514,0 10,-4.486 10,-10v-20.17187l3.58594,3.58594c0.50163,0.52248 1.24653,0.73295 1.94742,0.55024c0.70088,-0.18271 1.24823,-0.73006 1.43094,-1.43094c0.18271,-0.70088 -0.02776,-1.44578 -0.55024,-1.94742l-7,-7c-0.38217,-0.38233 -0.90283,-0.5937 -1.44336,-0.58594z"></path></g></g></svg>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.N8nVersionTrigger = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const cron_1 = require("cron");
|
|
6
|
+
const VersionUtils_1 = require("../VersionUtils");
|
|
7
|
+
class N8nVersionTrigger {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.description = {
|
|
10
|
+
displayName: 'Version Trigger',
|
|
11
|
+
name: 'versionTrigger',
|
|
12
|
+
icon: 'file:n8n-version.svg',
|
|
13
|
+
group: ['trigger'],
|
|
14
|
+
version: 1,
|
|
15
|
+
description: 'Triggers workflow based on n8n version status using cron schedule',
|
|
16
|
+
defaults: {
|
|
17
|
+
name: 'Version Trigger',
|
|
18
|
+
},
|
|
19
|
+
inputs: [],
|
|
20
|
+
outputs: ['main'],
|
|
21
|
+
properties: [
|
|
22
|
+
{
|
|
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).',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
displayName: 'Trigger Condition',
|
|
33
|
+
name: 'triggerCondition',
|
|
34
|
+
type: 'options',
|
|
35
|
+
default: 'update',
|
|
36
|
+
required: true,
|
|
37
|
+
description: 'When should the workflow be triggered',
|
|
38
|
+
options: [
|
|
39
|
+
{
|
|
40
|
+
name: 'Update Available',
|
|
41
|
+
value: 'update',
|
|
42
|
+
description: 'Trigger only when a new version is available',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'On Latest Version',
|
|
46
|
+
value: 'latest',
|
|
47
|
+
description: 'Trigger only when already on the latest version',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Always',
|
|
51
|
+
value: 'always',
|
|
52
|
+
description: 'Trigger on every check regardless of version status',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async trigger() {
|
|
60
|
+
const cronExpression = this.getNodeParameter('cronExpression');
|
|
61
|
+
const triggerCondition = this.getNodeParameter('triggerCondition');
|
|
62
|
+
let job;
|
|
63
|
+
try {
|
|
64
|
+
job = new cron_1.CronJob(cronExpression, async () => {
|
|
65
|
+
try {
|
|
66
|
+
const { isLatest, currentVersion, latestVersion, error } = await (0, VersionUtils_1.checkIsLatest)();
|
|
67
|
+
let shouldTrigger = false;
|
|
68
|
+
if (triggerCondition === 'always') {
|
|
69
|
+
shouldTrigger = true;
|
|
70
|
+
}
|
|
71
|
+
else if (triggerCondition === 'update') {
|
|
72
|
+
shouldTrigger = !isLatest;
|
|
73
|
+
}
|
|
74
|
+
else if (triggerCondition === 'latest') {
|
|
75
|
+
shouldTrigger = isLatest === true;
|
|
76
|
+
}
|
|
77
|
+
if (shouldTrigger) {
|
|
78
|
+
this.emit([
|
|
79
|
+
[
|
|
80
|
+
{
|
|
81
|
+
json: {
|
|
82
|
+
currentVersion,
|
|
83
|
+
latestVersion,
|
|
84
|
+
isLatest,
|
|
85
|
+
error: error || undefined,
|
|
86
|
+
triggeredAt: new Date().toISOString(),
|
|
87
|
+
triggerCondition,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.error('[VersionTrigger] Error during cron execution:', err);
|
|
96
|
+
// Don't stop the cron job on errors, just log them
|
|
97
|
+
}
|
|
98
|
+
}, null, true);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
102
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid cron expression: ${errorMessage}. Please use standard cron format (e.g., "0 * * * *" for hourly).`);
|
|
103
|
+
}
|
|
104
|
+
async function closeFunction() {
|
|
105
|
+
if (job) {
|
|
106
|
+
job.stop();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
closeFunction,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.N8nVersionTrigger = N8nVersionTrigger;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="256px" height="256px" fill-rule="nonzero"><g fill="#f188a2" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M20,3c-5.514,0 -10,4.486 -10,10v20.17188l-3.58594,-3.58594c-0.37701,-0.38755 -0.89487,-0.60597 -1.43555,-0.60547c-0.81349,0.00101 -1.54533,0.49459 -1.85108,1.24844c-0.30574,0.75385 -0.12447,1.61777 0.4585,2.18515l7,7c0.78106,0.78074 2.04706,0.78074 2.82812,0l7,-7c0.52248,-0.50163 0.73295,-1.24653 0.55024,-1.94742c-0.18271,-0.70088 -0.73006,-1.24823 -1.43094,-1.43094c-0.70088,-0.18271 -1.44578,0.02776 -1.94742,0.55024l-3.58594,3.58594v-20.17187c0,-3.309 2.691,-6 6,-6h10.46875l0.26563,-4zM37.9707,10c-0.52023,0.00778 -1.01695,0.21796 -1.38477,0.58594l-7,7c-0.52248,0.50163 -0.73295,1.24653 -0.55024,1.94742c0.18271,0.70088 0.73006,1.24823 1.43094,1.43094c0.70088,0.18271 1.44578,-0.02776 1.94742,-0.55024l3.58594,-3.58594v20.17188c0,3.309 -2.691,6 -6,6h-10.49023l-0.25391,4h10.74414c5.514,0 10,-4.486 10,-10v-20.17187l3.58594,3.58594c0.50163,0.52248 1.24653,0.73295 1.94742,0.55024c0.70088,-0.18271 1.24823,-0.73006 1.43094,-1.43094c0.18271,-0.70088 -0.02776,-1.44578 -0.55024,-1.94742l-7,-7c-0.38217,-0.38233 -0.90283,-0.5937 -1.44336,-0.58594z"></path></g></g></svg>
|
|
@@ -0,0 +1,185 @@
|
|
|
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.stripMarkdownLinks = exports.cleanChangelog = exports.getChangelog = exports.checkIsLatest = exports.getCurrentN8nVersion = exports.getLatestN8nVersion = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const https_1 = __importDefault(require("https"));
|
|
9
|
+
/**
|
|
10
|
+
* Fetches the latest n8n version from npm registry
|
|
11
|
+
* @returns Promise resolving to version string or 'unknown' on error
|
|
12
|
+
*/
|
|
13
|
+
function getLatestN8nVersion() {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
try {
|
|
16
|
+
https_1.default
|
|
17
|
+
.get('https://registry.npmjs.org/n8n/stable', (res) => {
|
|
18
|
+
let data = '';
|
|
19
|
+
res.on('data', (chunk) => (data += chunk));
|
|
20
|
+
res.on('end', () => {
|
|
21
|
+
var _a;
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(data);
|
|
24
|
+
resolve((_a = parsed.version) !== null && _a !== void 0 ? _a : 'unknown');
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.error('[VersionUtils] Failed to parse npm registry response:', err);
|
|
28
|
+
resolve('unknown');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
})
|
|
32
|
+
.on('error', (err) => {
|
|
33
|
+
console.error('[VersionUtils] Failed to fetch latest version from npm:', err);
|
|
34
|
+
resolve('unknown');
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
console.error('[VersionUtils] Unexpected error in getLatestN8nVersion:', err);
|
|
39
|
+
resolve('unknown');
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
exports.getLatestN8nVersion = getLatestN8nVersion;
|
|
44
|
+
/**
|
|
45
|
+
* Gets the currently installed n8n version
|
|
46
|
+
* Tries multiple methods: N8N_VERSION env var, n8n --version command, env var scan
|
|
47
|
+
* @returns Current version string or 'unknown' if not found
|
|
48
|
+
*/
|
|
49
|
+
function getCurrentN8nVersion() {
|
|
50
|
+
let currentVersion = process.env.N8N_VERSION;
|
|
51
|
+
// ENV first
|
|
52
|
+
if (!currentVersion) {
|
|
53
|
+
try {
|
|
54
|
+
const stdout = (0, child_process_1.execSync)('n8n --version').toString();
|
|
55
|
+
if (stdout) {
|
|
56
|
+
currentVersion = stdout.trim();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error('[VersionUtils] Failed to execute n8n --version:', err);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ENV scan fallback
|
|
64
|
+
if (!currentVersion) {
|
|
65
|
+
const versionKey = Object.keys(process.env).find((key) => key.includes('VERSION') && key.includes('N8N'));
|
|
66
|
+
if (versionKey) {
|
|
67
|
+
currentVersion = process.env[versionKey];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return currentVersion || 'unknown';
|
|
71
|
+
}
|
|
72
|
+
exports.getCurrentN8nVersion = getCurrentN8nVersion;
|
|
73
|
+
/**
|
|
74
|
+
* Checks if the current n8n version is the latest available
|
|
75
|
+
* @returns Object with currentVersion, latestVersion, and isLatest flag
|
|
76
|
+
*/
|
|
77
|
+
async function checkIsLatest() {
|
|
78
|
+
const currentVersion = getCurrentN8nVersion();
|
|
79
|
+
const latestVersion = await getLatestN8nVersion();
|
|
80
|
+
const isLatest = currentVersion !== 'unknown' &&
|
|
81
|
+
latestVersion !== 'unknown' &&
|
|
82
|
+
currentVersion === latestVersion;
|
|
83
|
+
return {
|
|
84
|
+
currentVersion,
|
|
85
|
+
latestVersion,
|
|
86
|
+
isLatest,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
exports.checkIsLatest = checkIsLatest;
|
|
90
|
+
/**
|
|
91
|
+
* Fetches changelog from GitHub releases for a specific n8n version
|
|
92
|
+
* @param version - The n8n version to fetch changelog for (e.g., "1.0.0")
|
|
93
|
+
* @returns Promise resolving to changelog info with body and optional error
|
|
94
|
+
*/
|
|
95
|
+
function getChangelog(version) {
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
try {
|
|
98
|
+
const options = {
|
|
99
|
+
hostname: 'api.github.com',
|
|
100
|
+
path: `/repos/n8n-io/n8n/releases/tags/n8n@${version}`,
|
|
101
|
+
headers: {
|
|
102
|
+
'User-Agent': 'n8n-version-node',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
https_1.default
|
|
106
|
+
.get(options, (res) => {
|
|
107
|
+
let data = '';
|
|
108
|
+
res.on('data', (chunk) => (data += chunk));
|
|
109
|
+
res.on('end', () => {
|
|
110
|
+
try {
|
|
111
|
+
if (res.statusCode === 404) {
|
|
112
|
+
resolve({
|
|
113
|
+
version,
|
|
114
|
+
changelog: '',
|
|
115
|
+
error: `Release n8n@${version} not found on GitHub`,
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (res.statusCode !== 200) {
|
|
120
|
+
resolve({
|
|
121
|
+
version,
|
|
122
|
+
changelog: '',
|
|
123
|
+
error: `GitHub API returned status ${res.statusCode}`,
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const parsed = JSON.parse(data);
|
|
128
|
+
resolve({
|
|
129
|
+
version,
|
|
130
|
+
changelog: cleanChangelog(parsed.body || ''),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
console.error('[VersionUtils] Failed to parse GitHub response:', err);
|
|
135
|
+
resolve({
|
|
136
|
+
version,
|
|
137
|
+
changelog: '',
|
|
138
|
+
error: 'Failed to parse GitHub response',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
})
|
|
143
|
+
.on('error', (err) => {
|
|
144
|
+
console.error('[VersionUtils] Failed to fetch from GitHub:', err);
|
|
145
|
+
resolve({
|
|
146
|
+
version,
|
|
147
|
+
changelog: '',
|
|
148
|
+
error: `Failed to fetch from GitHub: ${err.message}`,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
console.error('[VersionUtils] Unexpected error in getChangelog:', err);
|
|
154
|
+
resolve({
|
|
155
|
+
version,
|
|
156
|
+
changelog: '',
|
|
157
|
+
error: `Unexpected error: ${err}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
exports.getChangelog = getChangelog;
|
|
163
|
+
/**
|
|
164
|
+
* Cleans changelog text by normalizing line breaks and removing artifacts
|
|
165
|
+
* @param text - Raw changelog text
|
|
166
|
+
* @returns Cleaned text
|
|
167
|
+
*/
|
|
168
|
+
function cleanChangelog(text) {
|
|
169
|
+
if (!text)
|
|
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();
|
|
175
|
+
}
|
|
176
|
+
exports.cleanChangelog = cleanChangelog;
|
|
177
|
+
/**
|
|
178
|
+
* Strips markdown links from text, converting [text](url) to text
|
|
179
|
+
* @param text - Text containing markdown links
|
|
180
|
+
* @returns Text with links stripped
|
|
181
|
+
*/
|
|
182
|
+
function stripMarkdownLinks(text) {
|
|
183
|
+
return text.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1');
|
|
184
|
+
}
|
|
185
|
+
exports.stripMarkdownLinks = stripMarkdownLinks;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-version",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "n8n nodes to check version, monitor updates, and fetch changelogs",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package"
|
|
7
|
+
],
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "",
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Community",
|
|
12
|
+
"email": "example@example.com"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && gulp build:icons",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"lint": "tslint -p tsconfig.json -c tslint.json",
|
|
18
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"main": "dist/index.js",
|
|
22
|
+
"n8n": {
|
|
23
|
+
"nodes": [
|
|
24
|
+
"dist/nodes/N8nVersion/N8nVersion.node.js",
|
|
25
|
+
"dist/nodes/N8nVersionTrigger/N8nVersionTrigger.node.js"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/cron": "^2.0.1",
|
|
33
|
+
"@types/node": "^14.14.31",
|
|
34
|
+
"gulp": "^4.0.2",
|
|
35
|
+
"n8n-core": "^0.116.0",
|
|
36
|
+
"n8n-workflow": "^0.114.0",
|
|
37
|
+
"tslint": "^6.1.2",
|
|
38
|
+
"typescript": "^4.1.3"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"n8n-workflow": "*"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"cron": "^4.4.0"
|
|
45
|
+
}
|
|
46
|
+
}
|