n8nac 2.2.1 → 2.3.0-next.55
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 +32 -4
- package/dist/commands/base.d.ts.map +1 -1
- package/dist/commands/base.js +17 -10
- package/dist/commands/base.js.map +1 -1
- package/dist/commands/promote.d.ts +83 -3
- package/dist/commands/promote.d.ts.map +1 -1
- package/dist/commands/promote.js +616 -67
- package/dist/commands/promote.js.map +1 -1
- package/dist/core/services/n8n-api-client.d.ts +2 -1
- package/dist/core/services/n8n-api-client.d.ts.map +1 -1
- package/dist/core/services/n8n-api-client.js +36 -5
- package/dist/core/services/n8n-api-client.js.map +1 -1
- package/dist/core/services/sync-manager.d.ts.map +1 -1
- package/dist/core/services/sync-manager.js +6 -5
- package/dist/core/services/sync-manager.js.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +18 -13
- package/dist/index.js.map +1 -1
- package/dist/services/config-service.d.ts +30 -5
- package/dist/services/config-service.d.ts.map +1 -1
- package/dist/services/config-service.js +340 -68
- package/dist/services/config-service.js.map +1 -1
- package/package.json +4 -4
|
@@ -145,6 +145,29 @@ export class ConfigService {
|
|
|
145
145
|
description: input.description,
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
|
+
ensureManagedInstanceTarget(input) {
|
|
149
|
+
const managedInstanceId = cleanRequired(input.managedInstanceId, 'Managed instance ID');
|
|
150
|
+
const config = this.ensureV4WorkspaceConfig();
|
|
151
|
+
const managedInstance = config.environmentTargets.find((target) => {
|
|
152
|
+
return target.kind === 'managed-instance' && target.managedInstanceId === managedInstanceId;
|
|
153
|
+
});
|
|
154
|
+
if (managedInstance)
|
|
155
|
+
return managedInstance;
|
|
156
|
+
const existingNames = new Set(config.environmentTargets.map((target) => target.name.toLowerCase()));
|
|
157
|
+
const baseName = cleanRequired(input.name, 'Instance name');
|
|
158
|
+
let name = baseName;
|
|
159
|
+
let counter = 2;
|
|
160
|
+
while (existingNames.has(name.toLowerCase())) {
|
|
161
|
+
name = `${baseName} ${counter}`;
|
|
162
|
+
counter += 1;
|
|
163
|
+
}
|
|
164
|
+
return this.addInstanceTarget({
|
|
165
|
+
name,
|
|
166
|
+
id: input.id,
|
|
167
|
+
managedInstanceId,
|
|
168
|
+
description: input.description,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
148
171
|
updateInstanceTarget(nameOrId, patch) {
|
|
149
172
|
const config = this.ensureV4WorkspaceConfig();
|
|
150
173
|
const target = this.findInstanceTarget(config, nameOrId);
|
|
@@ -193,13 +216,21 @@ export class ConfigService {
|
|
|
193
216
|
...config.environments.map((item) => item.id),
|
|
194
217
|
]);
|
|
195
218
|
this.assertUniqueName(name, config.environments, 'environment');
|
|
219
|
+
const syncSlug = this.uniqueEnvironmentSyncSlug(name, config.environments);
|
|
220
|
+
const workflowsPath = this.resolveInputWorkflowsPath({
|
|
221
|
+
workflowsPath: input.workflowsPath,
|
|
222
|
+
workflowDir: input.workflowDir,
|
|
223
|
+
syncFolder: input.syncFolder,
|
|
224
|
+
syncSlug,
|
|
225
|
+
}, config.environments, name);
|
|
196
226
|
const environment = {
|
|
197
227
|
id,
|
|
198
228
|
name,
|
|
229
|
+
syncSlug,
|
|
199
230
|
environmentTargetId: target.id,
|
|
200
231
|
projectId: cleanOptional(input.projectId),
|
|
201
232
|
projectName: cleanOptional(input.projectName),
|
|
202
|
-
|
|
233
|
+
workflowsPath,
|
|
203
234
|
folderSync: input.folderSync,
|
|
204
235
|
customNodesPath: input.customNodesPath,
|
|
205
236
|
description: input.description,
|
|
@@ -215,18 +246,30 @@ export class ConfigService {
|
|
|
215
246
|
updateEnvironment(nameOrId, patch) {
|
|
216
247
|
const config = this.ensureV4WorkspaceConfig();
|
|
217
248
|
const environment = this.findEnvironment(config, nameOrId);
|
|
249
|
+
const currentTarget = this.findInstanceTarget(config, environment.environmentTargetId);
|
|
218
250
|
const target = patch.environmentTarget ? this.findInstanceTarget(config, patch.environmentTarget) : undefined;
|
|
219
251
|
const nextName = cleanOptional(patch.name) || environment.name;
|
|
220
252
|
if (nextName.toLowerCase() !== environment.name.toLowerCase()) {
|
|
221
253
|
this.assertUniqueName(nextName, config.environments.filter((item) => item.id !== environment.id), 'environment');
|
|
222
254
|
}
|
|
255
|
+
const currentWorkflowsPath = cleanRequired(environment.workflowsPath, 'Workflows path');
|
|
256
|
+
const workflowsPathPatch = this.patchWorkflowsPath(environment, patch);
|
|
257
|
+
const workflowsPathChanged = workflowsPathPatch !== undefined
|
|
258
|
+
&& this.resolveWorkspacePath(workflowsPathPatch) !== this.resolveWorkspacePath(currentWorkflowsPath);
|
|
259
|
+
if (workflowsPathChanged) {
|
|
260
|
+
this.migrateWorkflowsPath(currentWorkflowsPath, workflowsPathPatch);
|
|
261
|
+
}
|
|
262
|
+
const legacyWorkflowDir = workflowsPathChanged ? undefined : this.resolvePreservedLegacyWorkflowsPath(environment, currentTarget);
|
|
223
263
|
const nextEnvironment = stripUndefined({
|
|
224
264
|
...environment,
|
|
225
265
|
name: nextName,
|
|
266
|
+
workflowsPath: workflowsPathPatch ?? environment.workflowsPath,
|
|
267
|
+
legacyWorkflowDir,
|
|
268
|
+
workflowDir: undefined,
|
|
269
|
+
syncFolder: undefined,
|
|
226
270
|
environmentTargetId: target?.id || environment.environmentTargetId,
|
|
227
271
|
projectId: patch.projectId !== undefined ? cleanOptional(patch.projectId) : environment.projectId,
|
|
228
272
|
projectName: patch.projectName !== undefined ? cleanOptional(patch.projectName) : environment.projectName,
|
|
229
|
-
syncFolder: patch.syncFolder !== undefined ? cleanRequired(patch.syncFolder, 'Sync folder') : environment.syncFolder,
|
|
230
273
|
folderSync: patch.folderSync ?? environment.folderSync,
|
|
231
274
|
customNodesPath: patch.customNodesPath ?? environment.customNodesPath,
|
|
232
275
|
description: patch.description ?? environment.description,
|
|
@@ -288,17 +331,25 @@ export class ConfigService {
|
|
|
288
331
|
const identity = await this.resolveN8nIdentity(resolved.host, resolved.apiKey, undefined, resolved.instanceIdentifier || resolved.environmentTargetId).catch(() => undefined);
|
|
289
332
|
const instanceIdentifier = identity?.instanceIdentifier || resolved.instanceIdentifier;
|
|
290
333
|
const instanceUserIdentifier = identity?.instanceUserIdentifier || resolved.instanceUserIdentifier;
|
|
334
|
+
const workflowsPath = this.resolveEnvironmentWorkflowsPath(resolved.environment, {
|
|
335
|
+
instanceIdentifier,
|
|
336
|
+
instanceUserIdentifier,
|
|
337
|
+
legacyInstanceIdentifier: resolved.environmentTarget.kind === 'external-instance'
|
|
338
|
+
? resolved.environmentTarget.instanceIdentifier
|
|
339
|
+
: undefined,
|
|
340
|
+
legacyInstanceUserIdentifier: resolved.environmentTarget.kind === 'external-instance'
|
|
341
|
+
? resolved.environmentTarget.instanceUserIdentifier
|
|
342
|
+
: undefined,
|
|
343
|
+
projectId: resolved.projectId,
|
|
344
|
+
projectName: resolved.projectName,
|
|
345
|
+
});
|
|
291
346
|
return {
|
|
292
347
|
...resolved,
|
|
348
|
+
workflowsPath,
|
|
349
|
+
syncFolder: workflowsPath,
|
|
293
350
|
instanceIdentifier,
|
|
294
351
|
instanceUserIdentifier,
|
|
295
|
-
workflowDir:
|
|
296
|
-
syncFolder: resolved.syncFolder,
|
|
297
|
-
environmentId: resolved.environmentId,
|
|
298
|
-
instanceIdentifier,
|
|
299
|
-
instanceUserIdentifier,
|
|
300
|
-
projectId: resolved.projectId,
|
|
301
|
-
}),
|
|
352
|
+
workflowDir: workflowsPath,
|
|
302
353
|
};
|
|
303
354
|
}
|
|
304
355
|
return resolved;
|
|
@@ -314,7 +365,6 @@ export class ConfigService {
|
|
|
314
365
|
}
|
|
315
366
|
const context = prepared.context;
|
|
316
367
|
const apiKey = resolved.apiKey || context.apiKey;
|
|
317
|
-
const syncFolder = resolved.syncFolder;
|
|
318
368
|
const projectId = resolved.projectId || context.projectId;
|
|
319
369
|
const projectName = resolved.projectName || context.projectName;
|
|
320
370
|
let instanceIdentifier = this.canonicalWorkflowInstanceIdentifier(context.n8nInstanceIdentifier)
|
|
@@ -328,6 +378,14 @@ export class ConfigService {
|
|
|
328
378
|
instanceIdentifier = identity?.instanceIdentifier || instanceIdentifier;
|
|
329
379
|
instanceUserIdentifier = identity?.instanceUserIdentifier || instanceUserIdentifier;
|
|
330
380
|
}
|
|
381
|
+
const workflowsPath = this.resolveEnvironmentWorkflowsPath(resolved.environment, {
|
|
382
|
+
instanceIdentifier,
|
|
383
|
+
instanceUserIdentifier,
|
|
384
|
+
legacyInstanceIdentifier: resolved.instance.instanceIdentifier,
|
|
385
|
+
legacyInstanceUserIdentifier: resolved.instance.instanceUserIdentifier,
|
|
386
|
+
projectId,
|
|
387
|
+
projectName,
|
|
388
|
+
});
|
|
331
389
|
return {
|
|
332
390
|
...resolved,
|
|
333
391
|
host: context.host,
|
|
@@ -341,14 +399,9 @@ export class ConfigService {
|
|
|
341
399
|
instanceUserIdentifier,
|
|
342
400
|
projectId,
|
|
343
401
|
projectName,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
environmentId: resolved.environmentId,
|
|
348
|
-
instanceIdentifier,
|
|
349
|
-
instanceUserIdentifier,
|
|
350
|
-
projectId,
|
|
351
|
-
}),
|
|
402
|
+
workflowsPath,
|
|
403
|
+
syncFolder: workflowsPath,
|
|
404
|
+
workflowDir: workflowsPath,
|
|
352
405
|
};
|
|
353
406
|
}
|
|
354
407
|
listInstanceConfigs() {
|
|
@@ -577,6 +630,9 @@ export class ConfigService {
|
|
|
577
630
|
const saved = this.manager.upsertInstance(input, {
|
|
578
631
|
setActive: options.setActive,
|
|
579
632
|
});
|
|
633
|
+
if (options.apiKey) {
|
|
634
|
+
this.manager.saveApiKey(saved.id, options.apiKey);
|
|
635
|
+
}
|
|
580
636
|
if (workspaceConfigIsV4) {
|
|
581
637
|
return this.toInstanceProfile(saved);
|
|
582
638
|
}
|
|
@@ -756,6 +812,8 @@ export class ConfigService {
|
|
|
756
812
|
syncFolder: asString(raw.syncFolder) || activeInstance?.syncFolder,
|
|
757
813
|
projectId: asString(raw.projectId) || activeInstance?.projectId,
|
|
758
814
|
projectName: asString(raw.projectName) || activeInstance?.projectName,
|
|
815
|
+
workflowsPath: asString(raw.workflowsPath) || activeInstance?.workflowsPath || asString(raw.workflowDir) || activeInstance?.workflowDir,
|
|
816
|
+
workflowDir: asString(raw.workflowDir) || activeInstance?.workflowDir || asString(raw.workflowsPath) || activeInstance?.workflowsPath,
|
|
759
817
|
customNodesPath: asString(raw.customNodesPath) || activeInstance?.customNodesPath,
|
|
760
818
|
folderSync: asBoolean(raw.folderSync) ?? activeInstance?.folderSync,
|
|
761
819
|
});
|
|
@@ -828,6 +886,8 @@ export class ConfigService {
|
|
|
828
886
|
const environmentId = this.uniqueWorkspaceId(singleInstanceMigration ? 'default' : profile.id || legacy.id || environmentName, usedIds);
|
|
829
887
|
usedIds.push(environmentId);
|
|
830
888
|
const syncFolder = legacy.syncFolder || plan.workspace.syncFolder || DEFAULT_SYNC_FOLDER;
|
|
889
|
+
const syncSlug = this.createEnvironmentSyncSlug(environmentName);
|
|
890
|
+
const legacyWorkflowDir = legacy.workflowsPath || legacy.workflowDir || plan.workspace.workflowsPath || plan.workspace.workflowDir;
|
|
831
891
|
environmentTargets.push({
|
|
832
892
|
id: targetId,
|
|
833
893
|
name: targetName,
|
|
@@ -842,7 +902,10 @@ export class ConfigService {
|
|
|
842
902
|
environmentTargetId: targetId,
|
|
843
903
|
projectId: legacy.projectId || plan.workspace.projectId,
|
|
844
904
|
projectName: legacy.projectName || plan.workspace.projectName,
|
|
905
|
+
workflowsPath: legacyWorkflowDir || path.join(syncFolder, syncSlug),
|
|
906
|
+
syncSlug,
|
|
845
907
|
syncFolder,
|
|
908
|
+
legacyWorkflowDir,
|
|
846
909
|
customNodesPath: legacy.customNodesPath || plan.workspace.customNodesPath,
|
|
847
910
|
folderSync: legacy.folderSync ?? plan.workspace.folderSync,
|
|
848
911
|
}));
|
|
@@ -1088,12 +1151,15 @@ export class ConfigService {
|
|
|
1088
1151
|
const environmentId = this.uniqueWorkspaceId(instance.id || environmentName, usedIds);
|
|
1089
1152
|
usedIds.push(environmentId);
|
|
1090
1153
|
const syncFolder = DEFAULT_SYNC_FOLDER;
|
|
1154
|
+
const syncSlug = this.createEnvironmentSyncSlug(environmentName);
|
|
1091
1155
|
existingEnvironment = stripUndefined({
|
|
1092
1156
|
id: environmentId,
|
|
1093
1157
|
name: environmentName,
|
|
1094
1158
|
environmentTargetId: targetId,
|
|
1095
1159
|
projectId: instance.defaultProject?.id || 'personal',
|
|
1096
1160
|
projectName: instance.defaultProject?.name || 'Personal',
|
|
1161
|
+
workflowsPath: path.join(syncFolder, syncSlug),
|
|
1162
|
+
syncSlug,
|
|
1097
1163
|
syncFolder,
|
|
1098
1164
|
});
|
|
1099
1165
|
environments.push(existingEnvironment);
|
|
@@ -1141,12 +1207,15 @@ export class ConfigService {
|
|
|
1141
1207
|
const environmentId = this.uniqueWorkspaceId(instance.id || environmentName, usedIds);
|
|
1142
1208
|
usedIds.push(environmentId);
|
|
1143
1209
|
const syncFolder = DEFAULT_SYNC_FOLDER;
|
|
1210
|
+
const syncSlug = this.createEnvironmentSyncSlug(environmentName);
|
|
1144
1211
|
existingEnvironment = stripUndefined({
|
|
1145
1212
|
id: environmentId,
|
|
1146
1213
|
name: environmentName,
|
|
1147
1214
|
environmentTargetId: existingTarget.id,
|
|
1148
1215
|
projectId: instance.defaultProject?.id || 'personal',
|
|
1149
1216
|
projectName: instance.defaultProject?.name || 'Personal',
|
|
1217
|
+
workflowsPath: path.join(syncFolder, syncSlug),
|
|
1218
|
+
syncSlug,
|
|
1150
1219
|
syncFolder,
|
|
1151
1220
|
});
|
|
1152
1221
|
environments.push(existingEnvironment);
|
|
@@ -1165,6 +1234,7 @@ export class ConfigService {
|
|
|
1165
1234
|
const projectId = instance.defaultProject?.id || 'personal';
|
|
1166
1235
|
const projectName = instance.defaultProject?.name || 'Personal';
|
|
1167
1236
|
const syncFolder = DEFAULT_SYNC_FOLDER;
|
|
1237
|
+
const syncSlug = this.createEnvironmentSyncSlug(environmentName);
|
|
1168
1238
|
environmentTargets.push({
|
|
1169
1239
|
id: targetId,
|
|
1170
1240
|
name: targetName,
|
|
@@ -1179,6 +1249,8 @@ export class ConfigService {
|
|
|
1179
1249
|
environmentTargetId: targetId,
|
|
1180
1250
|
projectId,
|
|
1181
1251
|
projectName,
|
|
1252
|
+
workflowsPath: path.join(syncFolder, syncSlug),
|
|
1253
|
+
syncSlug,
|
|
1182
1254
|
syncFolder,
|
|
1183
1255
|
}));
|
|
1184
1256
|
if (apiKey)
|
|
@@ -1262,6 +1334,7 @@ export class ConfigService {
|
|
|
1262
1334
|
name,
|
|
1263
1335
|
host,
|
|
1264
1336
|
syncFolder: asString(value.syncFolder) || asString(root.syncFolder),
|
|
1337
|
+
workflowsPath: asString(value.workflowsPath) || asString(root.workflowsPath),
|
|
1265
1338
|
projectId: asString(value.projectId) || asString(root.projectId),
|
|
1266
1339
|
projectName: asString(value.projectName) || asString(root.projectName),
|
|
1267
1340
|
instanceIdentifier: asString(value.instanceIdentifier) || asString(root.instanceIdentifier),
|
|
@@ -1291,6 +1364,30 @@ export class ConfigService {
|
|
|
1291
1364
|
return asString(root.apiKey);
|
|
1292
1365
|
}
|
|
1293
1366
|
readLegacyStoredApiKey(instanceId, host) {
|
|
1367
|
+
const readFromStore = (store) => {
|
|
1368
|
+
const instanceApiKey = asString(store?.instanceProfiles?.[instanceId]);
|
|
1369
|
+
if (instanceApiKey)
|
|
1370
|
+
return instanceApiKey;
|
|
1371
|
+
if (!host)
|
|
1372
|
+
return undefined;
|
|
1373
|
+
return asString(store?.hosts?.[this.normalizeHost(host)]);
|
|
1374
|
+
};
|
|
1375
|
+
for (const root of [process.env.XDG_CONFIG_HOME, process.env.N8N_MANAGER_HOME]) {
|
|
1376
|
+
if (!root)
|
|
1377
|
+
continue;
|
|
1378
|
+
const filePath = path.join(root, 'n8nac-nodejs', 'credentials.json');
|
|
1379
|
+
if (!fs.existsSync(filePath))
|
|
1380
|
+
continue;
|
|
1381
|
+
try {
|
|
1382
|
+
const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
1383
|
+
const apiKey = readFromStore(value && typeof value === 'object' && !Array.isArray(value) ? value : undefined);
|
|
1384
|
+
if (apiKey)
|
|
1385
|
+
return apiKey;
|
|
1386
|
+
}
|
|
1387
|
+
catch {
|
|
1388
|
+
// Try the Conf-backed store below.
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1294
1391
|
try {
|
|
1295
1392
|
const store = new Conf({
|
|
1296
1393
|
projectName: 'n8nac',
|
|
@@ -1298,13 +1395,8 @@ export class ConfigService {
|
|
|
1298
1395
|
configFileMode: 0o600,
|
|
1299
1396
|
});
|
|
1300
1397
|
const instanceProfiles = store.get('instanceProfiles');
|
|
1301
|
-
const instanceApiKey = asString(instanceProfiles?.[instanceId]);
|
|
1302
|
-
if (instanceApiKey)
|
|
1303
|
-
return instanceApiKey;
|
|
1304
|
-
if (!host)
|
|
1305
|
-
return undefined;
|
|
1306
1398
|
const hosts = store.get('hosts');
|
|
1307
|
-
return
|
|
1399
|
+
return readFromStore({ hosts, instanceProfiles });
|
|
1308
1400
|
}
|
|
1309
1401
|
catch {
|
|
1310
1402
|
return undefined;
|
|
@@ -1403,8 +1495,8 @@ export class ConfigService {
|
|
|
1403
1495
|
const rawInstanceTargets = rawTargets;
|
|
1404
1496
|
const rawEnvironments = raw.environments;
|
|
1405
1497
|
const environmentTargets = rawInstanceTargets.map((target, index) => this.sanitizeInstanceTarget(target, index));
|
|
1406
|
-
const environments = rawEnvironments.map((environment, index) => this.sanitizeEnvironment(environment, index));
|
|
1407
|
-
this.
|
|
1498
|
+
const environments = this.ensureEnvironmentWorkflowsPaths(rawEnvironments.map((environment, index) => this.sanitizeEnvironment(environment, index)));
|
|
1499
|
+
this.assertUniqueIds(environmentTargets, 'instance target');
|
|
1408
1500
|
this.assertUniqueIdsAndNames(environments, 'environment');
|
|
1409
1501
|
const targetIds = new Set(environmentTargets.map((target) => target.id));
|
|
1410
1502
|
for (const environment of environments) {
|
|
@@ -1454,8 +1546,9 @@ export class ConfigService {
|
|
|
1454
1546
|
name,
|
|
1455
1547
|
kind: 'external-instance',
|
|
1456
1548
|
url,
|
|
1457
|
-
instanceIdentifier: this.canonicalWorkflowInstanceIdentifier(target.instanceIdentifier || target.instance?.instanceIdentifier)
|
|
1458
|
-
|
|
1549
|
+
instanceIdentifier: this.canonicalWorkflowInstanceIdentifier(target.instanceIdentifier || target.instance?.instanceIdentifier)
|
|
1550
|
+
|| cleanOptional(target.instanceIdentifier || target.instance?.instanceIdentifier),
|
|
1551
|
+
instanceUserIdentifier: this.readStoredInstanceUserIdentifier(target.instanceUserIdentifier || target.instance?.instanceUserIdentifier || target.instanceIdentifier || target.instance?.instanceIdentifier) || cleanOptional(target.instanceUserIdentifier || target.instance?.instanceUserIdentifier),
|
|
1459
1552
|
verification: target.verification || target.instance?.verification,
|
|
1460
1553
|
description: cleanOptional(target.description),
|
|
1461
1554
|
});
|
|
@@ -1463,18 +1556,46 @@ export class ConfigService {
|
|
|
1463
1556
|
throw new Error(`Invalid v4 workspace config: instance target "${name}" has unsupported kind "${String(target.kind)}".`);
|
|
1464
1557
|
}
|
|
1465
1558
|
assertUniqueIdsAndNames(items, label) {
|
|
1466
|
-
|
|
1559
|
+
this.assertUniqueIds(items, label);
|
|
1467
1560
|
const names = new Set();
|
|
1468
1561
|
for (const item of items) {
|
|
1469
|
-
if (ids.has(item.id))
|
|
1470
|
-
throw new Error(`Invalid v4 workspace config: duplicate ${label} ID "${item.id}".`);
|
|
1471
|
-
ids.add(item.id);
|
|
1472
1562
|
const name = item.name.toLowerCase();
|
|
1473
1563
|
if (names.has(name))
|
|
1474
1564
|
throw new Error(`Invalid v4 workspace config: duplicate ${label} name "${item.name}".`);
|
|
1475
1565
|
names.add(name);
|
|
1476
1566
|
}
|
|
1477
1567
|
}
|
|
1568
|
+
assertUniqueIds(items, label) {
|
|
1569
|
+
const ids = new Set();
|
|
1570
|
+
for (const item of items) {
|
|
1571
|
+
if (ids.has(item.id))
|
|
1572
|
+
throw new Error(`Invalid v4 workspace config: duplicate ${label} ID "${item.id}".`);
|
|
1573
|
+
ids.add(item.id);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
ensureEnvironmentWorkflowsPaths(environments) {
|
|
1577
|
+
const usedSlugs = new Set();
|
|
1578
|
+
const normalized = environments.map((environment) => {
|
|
1579
|
+
const syncSlug = environment.syncSlug
|
|
1580
|
+
? this.createEnvironmentSyncSlug(environment.syncSlug)
|
|
1581
|
+
: undefined;
|
|
1582
|
+
if (syncSlug) {
|
|
1583
|
+
const key = syncSlug.toLowerCase();
|
|
1584
|
+
if (usedSlugs.has(key)) {
|
|
1585
|
+
throw new Error(`Invalid v4 workspace config: duplicate environment sync slug "${syncSlug}".`);
|
|
1586
|
+
}
|
|
1587
|
+
usedSlugs.add(key);
|
|
1588
|
+
}
|
|
1589
|
+
return { ...environment, syncSlug };
|
|
1590
|
+
});
|
|
1591
|
+
return normalized.map((environment) => {
|
|
1592
|
+
const syncSlug = environment.syncSlug || this.uniqueEnvironmentSyncSlug(environment.name, [], usedSlugs);
|
|
1593
|
+
usedSlugs.add(syncSlug.toLowerCase());
|
|
1594
|
+
const workflowsPath = environment.workflowsPath
|
|
1595
|
+
|| this.resolveInputWorkflowsPath({ syncFolder: environment.syncFolder, syncSlug }, environments, environment.name);
|
|
1596
|
+
return stripUndefined({ ...environment, syncSlug, workflowsPath, workflowDir: undefined });
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1478
1599
|
sanitizeEnvironment(environment, index) {
|
|
1479
1600
|
if (!environment || typeof environment !== 'object') {
|
|
1480
1601
|
throw new Error(`Invalid v4 workspace config: environment at index ${index} must be an object.`);
|
|
@@ -1482,16 +1603,20 @@ export class ConfigService {
|
|
|
1482
1603
|
const id = cleanOptional(environment.id);
|
|
1483
1604
|
const name = cleanOptional(environment.name) || id;
|
|
1484
1605
|
const environmentTargetId = cleanOptional(environment.environmentTargetId) || cleanOptional(environment.instanceTargetId);
|
|
1606
|
+
const workflowsPath = cleanOptional(environment.workflowsPath) || cleanOptional(environment.workflowDir);
|
|
1485
1607
|
const syncFolder = cleanOptional(environment.syncFolder);
|
|
1486
|
-
if (!id || !name || !environmentTargetId || !syncFolder) {
|
|
1487
|
-
throw new Error(`Invalid v4 workspace config: environment at index ${index} needs id, name, environmentTargetId, and
|
|
1608
|
+
if (!id || !name || !environmentTargetId || (!workflowsPath && !syncFolder)) {
|
|
1609
|
+
throw new Error(`Invalid v4 workspace config: environment at index ${index} needs id, name, environmentTargetId, and workflowsPath.`);
|
|
1488
1610
|
}
|
|
1489
1611
|
return stripUndefined({
|
|
1490
1612
|
id,
|
|
1491
1613
|
name,
|
|
1614
|
+
syncSlug: cleanOptional(environment.syncSlug),
|
|
1615
|
+
legacyWorkflowDir: cleanOptional(environment.legacyWorkflowDir),
|
|
1492
1616
|
environmentTargetId,
|
|
1493
1617
|
projectId: cleanOptional(environment.projectId),
|
|
1494
1618
|
projectName: cleanOptional(environment.projectName),
|
|
1619
|
+
workflowsPath,
|
|
1495
1620
|
syncFolder,
|
|
1496
1621
|
folderSync: typeof environment.folderSync === 'boolean' ? environment.folderSync : undefined,
|
|
1497
1622
|
customNodesPath: cleanOptional(environment.customNodesPath),
|
|
@@ -1510,14 +1635,18 @@ export class ConfigService {
|
|
|
1510
1635
|
const environmentTargets = managedInstanceId
|
|
1511
1636
|
? [{ id: 'default-instance', name: 'Default Instance', kind: 'managed-instance', managedInstanceId }]
|
|
1512
1637
|
: [];
|
|
1638
|
+
const legacyWorkflowDir = overrides.workflowDir;
|
|
1513
1639
|
const environments = managedInstanceId
|
|
1514
1640
|
? [stripUndefined({
|
|
1515
1641
|
id: 'default',
|
|
1516
1642
|
name: 'Default',
|
|
1643
|
+
syncSlug: this.createEnvironmentSyncSlug('Default'),
|
|
1517
1644
|
environmentTargetId: 'default-instance',
|
|
1518
1645
|
projectId: overrides.projectId,
|
|
1519
1646
|
projectName: overrides.projectName,
|
|
1647
|
+
workflowsPath: legacyWorkflowDir || path.join(overrides.syncFolder || DEFAULT_SYNC_FOLDER, this.createEnvironmentSyncSlug('Default')),
|
|
1520
1648
|
syncFolder: overrides.syncFolder || DEFAULT_SYNC_FOLDER,
|
|
1649
|
+
legacyWorkflowDir,
|
|
1521
1650
|
folderSync: overrides.folderSync,
|
|
1522
1651
|
customNodesPath: overrides.customNodesPath,
|
|
1523
1652
|
})]
|
|
@@ -1582,7 +1711,6 @@ export class ConfigService {
|
|
|
1582
1711
|
return stripUndefined({ instanceIdentifier, instanceUserIdentifier });
|
|
1583
1712
|
}
|
|
1584
1713
|
resolveEnvironmentFromTarget(environment, target, source) {
|
|
1585
|
-
const syncFolder = this.resolveWorkspacePath(environment.syncFolder);
|
|
1586
1714
|
if (target.kind === 'managed-instance') {
|
|
1587
1715
|
const instance = this.manager.getInstance(target.managedInstanceId);
|
|
1588
1716
|
if (!instance)
|
|
@@ -1594,6 +1722,15 @@ export class ConfigService {
|
|
|
1594
1722
|
const projectId = environment.projectId || instance.defaultProject?.id;
|
|
1595
1723
|
const projectName = environment.projectName || instance.defaultProject?.name;
|
|
1596
1724
|
const identity = this.resolveManagedEnvironmentIdentity(instance, host, apiKey);
|
|
1725
|
+
const workflowsPath = this.resolveEnvironmentWorkflowsPath(environment, {
|
|
1726
|
+
instanceIdentifier: identity.instanceIdentifier,
|
|
1727
|
+
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
1728
|
+
legacyInstanceIdentifier: instance.instanceIdentifier,
|
|
1729
|
+
legacyInstanceUserIdentifier: instance.instanceUserIdentifier,
|
|
1730
|
+
projectId,
|
|
1731
|
+
projectName,
|
|
1732
|
+
});
|
|
1733
|
+
const syncFolder = workflowsPath;
|
|
1597
1734
|
return {
|
|
1598
1735
|
environment,
|
|
1599
1736
|
environmentTarget: target,
|
|
@@ -1611,18 +1748,13 @@ export class ConfigService {
|
|
|
1611
1748
|
apiKeySource: envApiKey ? 'env' : globalApiKey ? 'global' : 'missing',
|
|
1612
1749
|
apiKeyAvailable: Boolean(apiKey),
|
|
1613
1750
|
accessStatus: this.deriveAccessStatus({ host, apiKey, projectId, projectName, verification: envApiKey ? undefined : instance.verification }),
|
|
1751
|
+
workflowsPath,
|
|
1614
1752
|
syncFolder,
|
|
1615
1753
|
projectId,
|
|
1616
1754
|
projectName,
|
|
1617
1755
|
instanceIdentifier: identity.instanceIdentifier,
|
|
1618
1756
|
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
1619
|
-
workflowDir:
|
|
1620
|
-
syncFolder,
|
|
1621
|
-
environmentId: environment.id,
|
|
1622
|
-
instanceIdentifier: identity.instanceIdentifier,
|
|
1623
|
-
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
1624
|
-
projectId,
|
|
1625
|
-
}),
|
|
1757
|
+
workflowDir: workflowsPath,
|
|
1626
1758
|
folderSync: environment.folderSync ?? false,
|
|
1627
1759
|
customNodesPath: environment.customNodesPath,
|
|
1628
1760
|
sources: {
|
|
@@ -1639,6 +1771,15 @@ export class ConfigService {
|
|
|
1639
1771
|
const globalApiKey = this.getApiKey(host);
|
|
1640
1772
|
const apiKey = envApiKey || workspaceApiKey || globalApiKey;
|
|
1641
1773
|
const identity = this.resolveExternalEnvironmentIdentity(target, apiKey);
|
|
1774
|
+
const workflowsPath = this.resolveEnvironmentWorkflowsPath(environment, {
|
|
1775
|
+
instanceIdentifier: identity.instanceIdentifier,
|
|
1776
|
+
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
1777
|
+
legacyInstanceIdentifier: target.instanceIdentifier,
|
|
1778
|
+
legacyInstanceUserIdentifier: target.instanceUserIdentifier,
|
|
1779
|
+
projectId: environment.projectId,
|
|
1780
|
+
projectName: environment.projectName,
|
|
1781
|
+
});
|
|
1782
|
+
const syncFolder = workflowsPath;
|
|
1642
1783
|
return {
|
|
1643
1784
|
environment,
|
|
1644
1785
|
environmentTarget: target,
|
|
@@ -1654,18 +1795,13 @@ export class ConfigService {
|
|
|
1654
1795
|
apiKeySource: envApiKey ? 'env' : workspaceApiKey ? 'workspace-local' : globalApiKey ? 'global' : 'missing',
|
|
1655
1796
|
apiKeyAvailable: Boolean(apiKey),
|
|
1656
1797
|
accessStatus: this.deriveAccessStatus({ host, apiKey, projectId: environment.projectId, projectName: environment.projectName, verification: target.verification }),
|
|
1798
|
+
workflowsPath,
|
|
1657
1799
|
syncFolder,
|
|
1658
1800
|
projectId: environment.projectId,
|
|
1659
1801
|
projectName: environment.projectName,
|
|
1660
1802
|
instanceIdentifier: identity.instanceIdentifier,
|
|
1661
1803
|
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
1662
|
-
workflowDir:
|
|
1663
|
-
syncFolder,
|
|
1664
|
-
environmentId: environment.id,
|
|
1665
|
-
instanceIdentifier: identity.instanceIdentifier,
|
|
1666
|
-
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
1667
|
-
projectId: environment.projectId,
|
|
1668
|
-
}),
|
|
1804
|
+
workflowDir: workflowsPath,
|
|
1669
1805
|
folderSync: environment.folderSync ?? false,
|
|
1670
1806
|
customNodesPath: environment.customNodesPath,
|
|
1671
1807
|
sources: {
|
|
@@ -1681,8 +1817,10 @@ export class ConfigService {
|
|
|
1681
1817
|
`N8NAC_ENV_${envVarSlug(environment.id)}_API_KEY`,
|
|
1682
1818
|
`N8NAC_ENV_${envVarSlug(environment.name)}_API_KEY`,
|
|
1683
1819
|
`N8NAC_TARGET_${envVarSlug(target.id)}_API_KEY`,
|
|
1684
|
-
`N8NAC_TARGET_${envVarSlug(target.name)}_API_KEY`,
|
|
1685
1820
|
];
|
|
1821
|
+
if (this.isUniqueInstanceTargetName(target)) {
|
|
1822
|
+
candidates.push(`N8NAC_TARGET_${envVarSlug(target.name)}_API_KEY`);
|
|
1823
|
+
}
|
|
1686
1824
|
for (const key of candidates) {
|
|
1687
1825
|
const value = process.env[key]?.trim().replace(/^['"]|['"]$/g, '');
|
|
1688
1826
|
if (value)
|
|
@@ -1693,8 +1831,10 @@ export class ConfigService {
|
|
|
1693
1831
|
readTargetEnvApiKey(target) {
|
|
1694
1832
|
const candidates = [
|
|
1695
1833
|
`N8NAC_TARGET_${envVarSlug(target.id)}_API_KEY`,
|
|
1696
|
-
`N8NAC_TARGET_${envVarSlug(target.name)}_API_KEY`,
|
|
1697
1834
|
];
|
|
1835
|
+
if (this.isUniqueInstanceTargetName(target)) {
|
|
1836
|
+
candidates.push(`N8NAC_TARGET_${envVarSlug(target.name)}_API_KEY`);
|
|
1837
|
+
}
|
|
1698
1838
|
for (const key of candidates) {
|
|
1699
1839
|
const value = process.env[key]?.trim().replace(/^["']|["']$/g, '');
|
|
1700
1840
|
if (value)
|
|
@@ -1702,6 +1842,11 @@ export class ConfigService {
|
|
|
1702
1842
|
}
|
|
1703
1843
|
return undefined;
|
|
1704
1844
|
}
|
|
1845
|
+
isUniqueInstanceTargetName(target) {
|
|
1846
|
+
const name = target.name.toLowerCase();
|
|
1847
|
+
const config = this.ensureV4WorkspaceConfig();
|
|
1848
|
+
return config.environmentTargets.filter((item) => item.name.toLowerCase() === name).length === 1;
|
|
1849
|
+
}
|
|
1705
1850
|
resolveExistingGlobalInstanceRef(managedInstanceId) {
|
|
1706
1851
|
const cleaned = cleanRequired(managedInstanceId, 'Global instance reference');
|
|
1707
1852
|
const instance = this.manager.getInstance(cleaned);
|
|
@@ -1713,12 +1858,13 @@ export class ConfigService {
|
|
|
1713
1858
|
environmentToLocalConfig(environment) {
|
|
1714
1859
|
return stripUndefined({
|
|
1715
1860
|
host: environment.host,
|
|
1716
|
-
|
|
1861
|
+
workflowsPath: environment.workflowsPath,
|
|
1862
|
+
workflowDir: environment.workflowsPath,
|
|
1863
|
+
syncFolder: environment.workflowsPath,
|
|
1717
1864
|
projectId: environment.projectId,
|
|
1718
1865
|
projectName: environment.projectName,
|
|
1719
1866
|
instanceIdentifier: environment.instanceIdentifier,
|
|
1720
1867
|
instanceUserIdentifier: environment.instanceUserIdentifier,
|
|
1721
|
-
workflowDir: environment.workflowDir,
|
|
1722
1868
|
customNodesPath: environment.customNodesPath,
|
|
1723
1869
|
folderSync: environment.folderSync,
|
|
1724
1870
|
});
|
|
@@ -1740,7 +1886,8 @@ export class ConfigService {
|
|
|
1740
1886
|
managedInstanceId: resolved.managedInstanceId,
|
|
1741
1887
|
instanceName: resolved.activeInstanceName,
|
|
1742
1888
|
url: resolved.sourceKind === 'external-instance' ? resolved.host : undefined,
|
|
1743
|
-
|
|
1889
|
+
workflowsPathResolved: resolved.workflowsPath,
|
|
1890
|
+
workflowDir: resolved.workflowsPath,
|
|
1744
1891
|
instanceIdentifier: resolved.instanceIdentifier,
|
|
1745
1892
|
instanceUserIdentifier: resolved.instanceUserIdentifier,
|
|
1746
1893
|
apiKeyAvailable: resolved.apiKeyAvailable,
|
|
@@ -1809,12 +1956,13 @@ export class ConfigService {
|
|
|
1809
1956
|
id: environment.activeInstanceId || environment.environmentTargetId,
|
|
1810
1957
|
name: environment.activeInstanceName || environment.environmentTargetName,
|
|
1811
1958
|
host: environment.host,
|
|
1812
|
-
|
|
1959
|
+
workflowsPath: environment.workflowsPath,
|
|
1960
|
+
workflowDir: environment.workflowsPath,
|
|
1961
|
+
syncFolder: environment.workflowsPath,
|
|
1813
1962
|
projectId: environment.projectId,
|
|
1814
1963
|
projectName: environment.projectName,
|
|
1815
1964
|
instanceIdentifier: environment.instanceIdentifier,
|
|
1816
1965
|
instanceUserIdentifier: environment.instanceUserIdentifier,
|
|
1817
|
-
workflowDir: environment.workflowDir,
|
|
1818
1966
|
customNodesPath: environment.customNodesPath,
|
|
1819
1967
|
folderSync: environment.folderSync,
|
|
1820
1968
|
});
|
|
@@ -1838,12 +1986,13 @@ export class ConfigService {
|
|
|
1838
1986
|
host: environment.host,
|
|
1839
1987
|
baseUrl: environment.host,
|
|
1840
1988
|
apiKey: environment.apiKey,
|
|
1841
|
-
|
|
1989
|
+
workflowsPath: environment.workflowsPath,
|
|
1990
|
+
workflowDir: environment.workflowsPath,
|
|
1991
|
+
syncFolder: environment.workflowsPath,
|
|
1842
1992
|
projectId: environment.projectId,
|
|
1843
1993
|
projectName: environment.projectName,
|
|
1844
1994
|
instanceIdentifier: environment.instanceIdentifier,
|
|
1845
1995
|
instanceUserIdentifier: environment.instanceUserIdentifier,
|
|
1846
|
-
workflowDir: environment.workflowDir,
|
|
1847
1996
|
folderSync: environment.folderSync ?? false,
|
|
1848
1997
|
customNodesPath: environment.customNodesPath,
|
|
1849
1998
|
environmentId: environment.environmentId,
|
|
@@ -1868,6 +2017,22 @@ export class ConfigService {
|
|
|
1868
2017
|
counter += 1;
|
|
1869
2018
|
return `${base}-${counter}`;
|
|
1870
2019
|
}
|
|
2020
|
+
uniqueEnvironmentSyncSlug(baseName, environments, usedSlugs = new Set()) {
|
|
2021
|
+
for (const environment of environments) {
|
|
2022
|
+
if (environment.syncSlug)
|
|
2023
|
+
usedSlugs.add(this.createEnvironmentSyncSlug(environment.syncSlug).toLowerCase());
|
|
2024
|
+
}
|
|
2025
|
+
const base = this.createEnvironmentSyncSlug(baseName);
|
|
2026
|
+
if (!usedSlugs.has(base.toLowerCase()))
|
|
2027
|
+
return base;
|
|
2028
|
+
let counter = 2;
|
|
2029
|
+
while (usedSlugs.has(`${base}-${counter}`.toLowerCase()))
|
|
2030
|
+
counter += 1;
|
|
2031
|
+
return `${base}-${counter}`;
|
|
2032
|
+
}
|
|
2033
|
+
createEnvironmentSyncSlug(value) {
|
|
2034
|
+
return this.slugId(value);
|
|
2035
|
+
}
|
|
1871
2036
|
uniqueDisplayName(baseName, existingNames) {
|
|
1872
2037
|
const base = cleanRequired(baseName, 'Name');
|
|
1873
2038
|
let name = base;
|
|
@@ -2017,12 +2182,15 @@ export class ConfigService {
|
|
|
2017
2182
|
return {
|
|
2018
2183
|
...this.toInstanceProfile(context.instance),
|
|
2019
2184
|
host: context.host,
|
|
2185
|
+
workflowsPath: context.workflowsPath
|
|
2186
|
+
|| context.workflowDir,
|
|
2020
2187
|
syncFolder: context.syncFolder,
|
|
2021
2188
|
projectId: context.projectId,
|
|
2022
2189
|
projectName: context.projectName,
|
|
2023
2190
|
instanceIdentifier,
|
|
2024
2191
|
instanceUserIdentifier,
|
|
2025
|
-
workflowDir: context.
|
|
2192
|
+
workflowDir: context.workflowsPath
|
|
2193
|
+
|| context.workflowDir
|
|
2026
2194
|
|| this.buildWorkflowDir(context.syncFolder, instanceIdentifier, context.projectName),
|
|
2027
2195
|
customNodesPath: context.customNodesPath,
|
|
2028
2196
|
folderSync: context.folderSync,
|
|
@@ -2034,9 +2202,10 @@ export class ConfigService {
|
|
|
2034
2202
|
toLocalConfig(profile) {
|
|
2035
2203
|
if (!profile)
|
|
2036
2204
|
return {};
|
|
2037
|
-
const workflowDir = profile.workflowDir || this.buildWorkflowDir(profile.syncFolder, profile.instanceIdentifier, profile.projectName);
|
|
2205
|
+
const workflowDir = profile.workflowsPath || profile.workflowDir || this.buildWorkflowDir(profile.syncFolder, profile.instanceIdentifier, profile.projectName);
|
|
2038
2206
|
return stripUndefined({
|
|
2039
2207
|
host: profile.host,
|
|
2208
|
+
workflowsPath: profile.workflowsPath,
|
|
2040
2209
|
syncFolder: profile.syncFolder,
|
|
2041
2210
|
projectId: profile.projectId,
|
|
2042
2211
|
projectName: profile.projectName,
|
|
@@ -2123,16 +2292,119 @@ export class ConfigService {
|
|
|
2123
2292
|
? path.join(syncFolder, instanceIdentifier, createProjectSlug(projectName))
|
|
2124
2293
|
: undefined;
|
|
2125
2294
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2295
|
+
resolveInputWorkflowsPath(input, environments, name) {
|
|
2296
|
+
const explicit = cleanOptional(input.workflowsPath) || cleanOptional(input.workflowDir);
|
|
2297
|
+
if (explicit)
|
|
2298
|
+
return explicit;
|
|
2299
|
+
const syncFolder = cleanOptional(input.syncFolder) || DEFAULT_SYNC_FOLDER;
|
|
2300
|
+
const syncSlug = input.syncSlug || this.uniqueEnvironmentSyncSlug(name, environments);
|
|
2301
|
+
return path.join(syncFolder, syncSlug);
|
|
2302
|
+
}
|
|
2303
|
+
patchWorkflowsPath(environment, patch) {
|
|
2304
|
+
const explicit = patch.workflowsPath !== undefined
|
|
2305
|
+
? cleanRequired(patch.workflowsPath, 'Workflows path')
|
|
2306
|
+
: patch.workflowDir !== undefined
|
|
2307
|
+
? cleanRequired(patch.workflowDir, 'Workflow directory')
|
|
2308
|
+
: undefined;
|
|
2309
|
+
if (explicit !== undefined)
|
|
2310
|
+
return explicit;
|
|
2311
|
+
if (patch.syncFolder === undefined)
|
|
2128
2312
|
return undefined;
|
|
2313
|
+
return this.resolveInputWorkflowsPath({
|
|
2314
|
+
syncFolder: cleanRequired(patch.syncFolder, 'Sync folder'),
|
|
2315
|
+
syncSlug: environment.syncSlug,
|
|
2316
|
+
}, [], environment.name);
|
|
2317
|
+
}
|
|
2318
|
+
resolveEnvironmentWorkflowsPath(environment, identity = {}) {
|
|
2319
|
+
const configured = this.resolveWorkspacePath(cleanRequired(environment.workflowsPath, 'Workflows path'));
|
|
2320
|
+
if (fs.existsSync(configured))
|
|
2321
|
+
return configured;
|
|
2322
|
+
const configuredLegacyDir = environment.legacyWorkflowDir
|
|
2323
|
+
? this.resolveWorkspacePath(environment.legacyWorkflowDir)
|
|
2324
|
+
: undefined;
|
|
2325
|
+
if (configuredLegacyDir && fs.existsSync(configuredLegacyDir))
|
|
2326
|
+
return configuredLegacyDir;
|
|
2327
|
+
const legacy = this.getLegacyEnvironmentWorkflowDirs({
|
|
2328
|
+
syncFolder: environment.syncFolder ? this.resolveWorkspacePath(environment.syncFolder) : undefined,
|
|
2329
|
+
environmentId: environment.id,
|
|
2330
|
+
instanceIdentifier: identity.instanceIdentifier,
|
|
2331
|
+
instanceUserIdentifier: identity.instanceUserIdentifier,
|
|
2332
|
+
legacyInstanceIdentifier: identity.legacyInstanceIdentifier,
|
|
2333
|
+
legacyInstanceUserIdentifier: identity.legacyInstanceUserIdentifier,
|
|
2334
|
+
projectId: identity.projectId,
|
|
2335
|
+
projectName: identity.projectName,
|
|
2336
|
+
}).find((directory) => fs.existsSync(directory));
|
|
2337
|
+
return legacy || configured;
|
|
2338
|
+
}
|
|
2339
|
+
resolvePreservedLegacyWorkflowsPath(environment, target) {
|
|
2340
|
+
const configured = this.resolveWorkspacePath(cleanRequired(environment.workflowsPath, 'Workflows path'));
|
|
2341
|
+
const resolved = this.resolveEnvironmentFromTarget(environment, target, 'explicit');
|
|
2342
|
+
const workflowsPath = resolved.workflowsPath;
|
|
2343
|
+
if (!workflowsPath || workflowsPath === configured || !fs.existsSync(workflowsPath)) {
|
|
2344
|
+
return environment.legacyWorkflowDir;
|
|
2345
|
+
}
|
|
2346
|
+
return path.isAbsolute(workflowsPath)
|
|
2347
|
+
? path.relative(this.workspaceRoot, workflowsPath)
|
|
2348
|
+
: workflowsPath;
|
|
2349
|
+
}
|
|
2350
|
+
migrateWorkflowsPath(previousPath, nextPath) {
|
|
2351
|
+
const previous = this.resolveWorkspacePath(previousPath);
|
|
2352
|
+
const next = this.resolveWorkspacePath(nextPath);
|
|
2353
|
+
if (previous === next || !fs.existsSync(previous))
|
|
2354
|
+
return;
|
|
2355
|
+
const relative = path.relative(previous, next);
|
|
2356
|
+
const reverseRelative = path.relative(next, previous);
|
|
2357
|
+
if (!relative || (!relative.startsWith('..') && !path.isAbsolute(relative)) || !reverseRelative || (!reverseRelative.startsWith('..') && !path.isAbsolute(reverseRelative))) {
|
|
2358
|
+
throw new Error('Cannot migrate workflowsPath into itself or one of its child directories.');
|
|
2129
2359
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2360
|
+
if (fs.existsSync(next) && !this.isDirectoryEmpty(next)) {
|
|
2361
|
+
throw new Error(`Cannot migrate workflowsPath to "${nextPath}" because the destination is not empty.`);
|
|
2362
|
+
}
|
|
2363
|
+
fs.mkdirSync(path.dirname(next), { recursive: true });
|
|
2364
|
+
if (fs.existsSync(next))
|
|
2365
|
+
fs.rmSync(next, { recursive: true, force: true });
|
|
2366
|
+
try {
|
|
2367
|
+
fs.renameSync(previous, next);
|
|
2368
|
+
}
|
|
2369
|
+
catch (error) {
|
|
2370
|
+
if (error?.code !== 'EXDEV')
|
|
2371
|
+
throw error;
|
|
2372
|
+
fs.cpSync(previous, next, { recursive: true, errorOnExist: false });
|
|
2373
|
+
fs.rmSync(previous, { recursive: true, force: true });
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
isDirectoryEmpty(directory) {
|
|
2377
|
+
try {
|
|
2378
|
+
return fs.statSync(directory).isDirectory() && fs.readdirSync(directory).length === 0;
|
|
2379
|
+
}
|
|
2380
|
+
catch {
|
|
2381
|
+
return false;
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
getLegacyEnvironmentWorkflowDirs(input) {
|
|
2385
|
+
const dirs = [];
|
|
2386
|
+
if (!input.syncFolder)
|
|
2387
|
+
return dirs;
|
|
2388
|
+
if (input.environmentId && input.instanceIdentifier && input.instanceUserIdentifier && input.projectId) {
|
|
2389
|
+
dirs.push(path.join(input.syncFolder, createWorkflowDirNameV1({
|
|
2390
|
+
environmentId: input.environmentId,
|
|
2391
|
+
instanceIdentifier: input.instanceIdentifier,
|
|
2392
|
+
instanceUserIdentifier: input.instanceUserIdentifier,
|
|
2393
|
+
projectId: input.projectId,
|
|
2394
|
+
})));
|
|
2395
|
+
}
|
|
2396
|
+
if (input.environmentId && input.legacyInstanceIdentifier && input.legacyInstanceUserIdentifier && input.projectId) {
|
|
2397
|
+
dirs.push(path.join(input.syncFolder, createWorkflowDirNameV1({
|
|
2398
|
+
environmentId: input.environmentId,
|
|
2399
|
+
instanceIdentifier: input.legacyInstanceIdentifier,
|
|
2400
|
+
instanceUserIdentifier: input.legacyInstanceUserIdentifier,
|
|
2401
|
+
projectId: input.projectId,
|
|
2402
|
+
})));
|
|
2403
|
+
}
|
|
2404
|
+
if (input.instanceIdentifier && input.projectName) {
|
|
2405
|
+
dirs.push(path.join(input.syncFolder, input.instanceIdentifier, createProjectSlug(input.projectName)));
|
|
2406
|
+
}
|
|
2407
|
+
return [...new Set(dirs)];
|
|
2136
2408
|
}
|
|
2137
2409
|
findConfigRoot(startDir) {
|
|
2138
2410
|
let currentDir = path.resolve(startDir);
|