@pipelab/core-node 1.0.0-beta.17 → 1.0.0-beta.19
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/CHANGELOG.md +18 -0
- package/dist/config-Bi0ORcTK.mjs +2 -0
- package/dist/config-CFgGRD9U.mjs +265 -0
- package/dist/config-CFgGRD9U.mjs.map +1 -0
- package/dist/index.d.mts +39 -24
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +875 -427
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/config.test.ts +81 -25
- package/src/config.ts +79 -43
- package/src/context.ts +59 -4
- package/src/handlers/build-history.ts +47 -90
- package/src/handlers/config.ts +255 -94
- package/src/handlers/engine.ts +28 -24
- package/src/handlers/history.ts +0 -40
- package/src/handlers/index.ts +2 -0
- package/src/handlers/migration.ts +598 -0
- package/src/index.ts +9 -2
- package/src/plugins-registry.ts +17 -19
- package/src/runner.ts +2 -7
- package/src/server.ts +1 -3
- package/src/utils/github.test.ts +3 -5
- package/src/utils/remote.test.ts +15 -6
- package/src/utils/remote.ts +8 -5
- package/src/utils/storage.ts +2 -1
- package/src/utils.ts +8 -8
- package/dist/config-CEkOf95p.mjs +0 -2
- package/src/migrations.ts +0 -60
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
+
import { a as setupPipelineConfigFileByName, c as setupSettingsConfigFile, d as extractTarGz, f as extractZip, h as zipFolder, i as setupConnectionsConfigFile, l as downloadFile, m as runWithLiveLogs, n as deletePipelineConfigFileByPath, o as setupPipelineConfigFileByPath, p as getFolderSize, s as setupProjectsConfigFile, t as deletePipelineConfigFileByName, u as ensure } from "./config-CFgGRD9U.mjs";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import path, { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
4
|
import { homedir, platform } from "node:os";
|
|
4
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import fs, {
|
|
6
|
+
import fs, { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
6
7
|
import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
7
8
|
import { DEFAULT_NODE_VERSION, DEFAULT_PNPM_VERSION, SandboxFolder, getUiDevServerMissingWarning, uiDevPort, websocketPort } from "@pipelab/constants";
|
|
8
9
|
import { WebSocket, WebSocketServer as WebSocketServer$1 } from "ws";
|
|
9
10
|
import http from "node:http";
|
|
10
11
|
import { nanoid } from "nanoid";
|
|
11
|
-
import { SubscriptionRequiredError, WebSocketError,
|
|
12
|
+
import { SubscriptionRequiredError, WebSocketError, appSettingsMigrator, connectionsMigrator, fileRepoMigrations, isRequired, isSupabaseAvailable, isWebSocketRequestMessage, processGraph, savedFileMigrator, supabase, transformUrl, useLogger, usePlugins } from "@pipelab/shared";
|
|
12
13
|
import { execa } from "execa";
|
|
13
|
-
import tar from "tar";
|
|
14
|
-
import yauzl from "yauzl";
|
|
15
|
-
import archiver from "archiver";
|
|
16
|
-
import { pipeline } from "node:stream/promises";
|
|
17
14
|
import checkDiskSpace from "check-disk-space";
|
|
18
15
|
import dns from "node:dns/promises";
|
|
19
16
|
import pacote from "pacote";
|
|
@@ -80,6 +77,11 @@ function findProjectRoot(startDir) {
|
|
|
80
77
|
return null;
|
|
81
78
|
}
|
|
82
79
|
const projectRoot = findProjectRoot(_dirname);
|
|
80
|
+
const CacheFolder = {
|
|
81
|
+
Actions: "actions",
|
|
82
|
+
Pipelines: "pipelines",
|
|
83
|
+
Pacote: "pacote"
|
|
84
|
+
};
|
|
83
85
|
var PipelabContext = class {
|
|
84
86
|
userDataPath;
|
|
85
87
|
releaseTag;
|
|
@@ -96,20 +98,50 @@ var PipelabContext = class {
|
|
|
96
98
|
getConfigPath(...subpaths) {
|
|
97
99
|
return join(this.userDataPath, "config", ...subpaths);
|
|
98
100
|
}
|
|
101
|
+
getSettingsPath() {
|
|
102
|
+
return this.getConfigPath("settings.json");
|
|
103
|
+
}
|
|
104
|
+
getConnectionsPath() {
|
|
105
|
+
return this.getConfigPath("connections.json");
|
|
106
|
+
}
|
|
107
|
+
getProjectsPath() {
|
|
108
|
+
return this.getConfigPath("projects.json");
|
|
109
|
+
}
|
|
110
|
+
_cachedSettings = null;
|
|
111
|
+
_cachedSettingsTime = 0;
|
|
112
|
+
getSettings() {
|
|
113
|
+
const settingsPath = this.getSettingsPath();
|
|
114
|
+
if (!existsSync(settingsPath)) return null;
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
if (this._cachedSettings && now - this._cachedSettingsTime < 2e3) return this._cachedSettings;
|
|
117
|
+
try {
|
|
118
|
+
const content = readFileSync(settingsPath, "utf8");
|
|
119
|
+
this._cachedSettings = JSON.parse(content);
|
|
120
|
+
this._cachedSettingsTime = now;
|
|
121
|
+
return this._cachedSettings;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return this._cachedSettings;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
99
126
|
getTempPath(...subpaths) {
|
|
100
|
-
return join(this.userDataPath, "temp", ...subpaths);
|
|
127
|
+
return join(this.getSettings()?.tempFolder || join(this.userDataPath, "temp"), ...subpaths);
|
|
101
128
|
}
|
|
102
129
|
async createTempFolder(prefix = "pipelab-") {
|
|
103
130
|
const baseDir = this.getTempPath();
|
|
104
131
|
await mkdir(baseDir, { recursive: true });
|
|
105
132
|
return await mkdtemp(join(await realpath(baseDir), prefix));
|
|
106
133
|
}
|
|
107
|
-
getCachePath(...subpaths) {
|
|
108
|
-
|
|
134
|
+
getCachePath(folder, ...subpaths) {
|
|
135
|
+
const base = this.getSettings()?.cacheFolder || join(this.userDataPath, "cache");
|
|
136
|
+
if (!folder) return base;
|
|
137
|
+
return join(base, folder, ...subpaths);
|
|
109
138
|
}
|
|
110
139
|
getPnpmPath(...subpaths) {
|
|
111
140
|
return join(this.userDataPath, "pnpm", ...subpaths);
|
|
112
141
|
}
|
|
142
|
+
getBuildHistoryPath(...subpaths) {
|
|
143
|
+
return join(this.userDataPath, "build-history", ...subpaths);
|
|
144
|
+
}
|
|
113
145
|
getNodePath(version = DEFAULT_NODE_VERSION) {
|
|
114
146
|
const isWindows = process.platform === "win32";
|
|
115
147
|
return this.getThirdPartyPath("node", version, isWindows ? "node.exe" : "bin/node");
|
|
@@ -502,287 +534,207 @@ const registerFsHandlers = (_context) => {
|
|
|
502
534
|
});
|
|
503
535
|
};
|
|
504
536
|
//#endregion
|
|
505
|
-
//#region src/
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
* Extracts a .tar.gz archive.
|
|
519
|
-
*/
|
|
520
|
-
async function extractTarGz(archivePath, destinationDir) {
|
|
521
|
-
await mkdir(destinationDir, { recursive: true });
|
|
522
|
-
await tar.x({
|
|
523
|
-
file: archivePath,
|
|
524
|
-
cwd: destinationDir
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* Extracts a .zip archive.
|
|
529
|
-
*/
|
|
530
|
-
async function extractZip(archivePath, destinationDir) {
|
|
531
|
-
await mkdir(destinationDir, { recursive: true });
|
|
532
|
-
return new Promise((resolve, reject) => {
|
|
533
|
-
yauzl.open(archivePath, { lazyEntries: true }, (err, zipfile) => {
|
|
534
|
-
if (err || !zipfile) return reject(err || /* @__PURE__ */ new Error("Could not open zip file"));
|
|
535
|
-
zipfile.on("error", reject);
|
|
536
|
-
zipfile.readEntry();
|
|
537
|
-
zipfile.on("entry", (entry) => {
|
|
538
|
-
const entryPath = join(destinationDir, entry.fileName);
|
|
539
|
-
if (/\/$/.test(entry.fileName)) mkdir(entryPath, { recursive: true }).then(() => zipfile.readEntry()).catch(reject);
|
|
540
|
-
else mkdir(dirname(entryPath), { recursive: true }).then(() => {
|
|
541
|
-
zipfile.openReadStream(entry, (err, readStream) => {
|
|
542
|
-
if (err || !readStream) return reject(err || /* @__PURE__ */ new Error("Could not open read stream"));
|
|
543
|
-
readStream.on("error", reject);
|
|
544
|
-
const writeStream = createWriteStream(entryPath);
|
|
545
|
-
writeStream.on("error", reject);
|
|
546
|
-
writeStream.on("close", () => zipfile.readEntry());
|
|
547
|
-
readStream.pipe(writeStream);
|
|
548
|
-
});
|
|
549
|
-
}).catch(reject);
|
|
537
|
+
//#region src/handlers/config.ts
|
|
538
|
+
const registerConfigHandlers = (context) => {
|
|
539
|
+
const { handle } = useAPI();
|
|
540
|
+
const { logger } = useLogger();
|
|
541
|
+
handle("settings:load", async (_, { send }) => {
|
|
542
|
+
logger().info("settings:load");
|
|
543
|
+
try {
|
|
544
|
+
send({
|
|
545
|
+
type: "end",
|
|
546
|
+
data: {
|
|
547
|
+
type: "success",
|
|
548
|
+
result: await (await setupSettingsConfigFile(context)).getConfig()
|
|
549
|
+
}
|
|
550
550
|
});
|
|
551
|
-
|
|
552
|
-
|
|
551
|
+
} catch (e) {
|
|
552
|
+
logger().error("settings:load error:", e);
|
|
553
|
+
send({
|
|
554
|
+
type: "end",
|
|
555
|
+
data: {
|
|
556
|
+
type: "error",
|
|
557
|
+
ipcError: e instanceof Error ? e.message : "Unable to load settings"
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
553
561
|
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
562
|
+
handle("settings:save", async (_, { send, value }) => {
|
|
563
|
+
const { data } = value;
|
|
564
|
+
try {
|
|
565
|
+
const manager = await setupSettingsConfigFile(context);
|
|
566
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
567
|
+
await manager.setConfig(json);
|
|
568
|
+
send({
|
|
569
|
+
type: "end",
|
|
570
|
+
data: {
|
|
571
|
+
type: "success",
|
|
572
|
+
result: "ok"
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
} catch (e) {
|
|
576
|
+
logger().error("settings:save error:", e);
|
|
577
|
+
send({
|
|
578
|
+
type: "end",
|
|
579
|
+
data: {
|
|
580
|
+
type: "error",
|
|
581
|
+
ipcError: e instanceof Error ? e.message : "Unable to save settings"
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
570
585
|
});
|
|
571
|
-
}
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
stdin: "pipe",
|
|
599
|
-
env: {
|
|
600
|
-
...process.env,
|
|
601
|
-
...execaOptions.env,
|
|
602
|
-
TERM: "xterm-256color",
|
|
603
|
-
FORCE_STDERR_LOGGING: "1"
|
|
604
|
-
},
|
|
605
|
-
cancelSignal: abortSignal
|
|
586
|
+
handle("settings:reset", async (_, { send, value }) => {
|
|
587
|
+
const { key } = value;
|
|
588
|
+
try {
|
|
589
|
+
const manager = await setupSettingsConfigFile(context);
|
|
590
|
+
const currentConfig = await manager.getConfig();
|
|
591
|
+
const defaultValue = appSettingsMigrator.defaultValue[key];
|
|
592
|
+
await manager.setConfig({
|
|
593
|
+
...currentConfig ? currentConfig : {},
|
|
594
|
+
[key]: defaultValue
|
|
595
|
+
});
|
|
596
|
+
send({
|
|
597
|
+
type: "end",
|
|
598
|
+
data: {
|
|
599
|
+
type: "success",
|
|
600
|
+
result: "ok"
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
} catch (e) {
|
|
604
|
+
logger().error("settings:reset error:", e);
|
|
605
|
+
send({
|
|
606
|
+
type: "end",
|
|
607
|
+
data: {
|
|
608
|
+
type: "error",
|
|
609
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset settings"
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
606
613
|
});
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
614
|
+
handle("connections:load", async (_, { send }) => {
|
|
615
|
+
logger().info("connections:load");
|
|
616
|
+
try {
|
|
617
|
+
send({
|
|
618
|
+
type: "end",
|
|
619
|
+
data: {
|
|
620
|
+
type: "success",
|
|
621
|
+
result: await (await setupConnectionsConfigFile(context)).getConfig()
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
} catch (e) {
|
|
625
|
+
logger().error("connections:load error:", e);
|
|
626
|
+
send({
|
|
627
|
+
type: "end",
|
|
628
|
+
data: {
|
|
629
|
+
type: "error",
|
|
630
|
+
ipcError: e instanceof Error ? e.message : "Unable to load connections"
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
610
634
|
});
|
|
611
|
-
|
|
612
|
-
|
|
635
|
+
handle("connections:save", async (_, { send, value }) => {
|
|
636
|
+
const { data } = value;
|
|
637
|
+
try {
|
|
638
|
+
const manager = await setupConnectionsConfigFile(context);
|
|
639
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
640
|
+
await manager.setConfig(json);
|
|
641
|
+
send({
|
|
642
|
+
type: "end",
|
|
643
|
+
data: {
|
|
644
|
+
type: "success",
|
|
645
|
+
result: "ok"
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
} catch (e) {
|
|
649
|
+
logger().error("connections:save error:", e);
|
|
650
|
+
send({
|
|
651
|
+
type: "end",
|
|
652
|
+
data: {
|
|
653
|
+
type: "error",
|
|
654
|
+
ipcError: e instanceof Error ? e.message : "Unable to save connections"
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
}
|
|
613
658
|
});
|
|
614
|
-
|
|
615
|
-
const {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const path = join(dirPath, file.name);
|
|
630
|
-
if (file.isDirectory()) try {
|
|
631
|
-
return await getFolderSize(path);
|
|
632
|
-
} catch {
|
|
633
|
-
return 0;
|
|
634
|
-
}
|
|
635
|
-
try {
|
|
636
|
-
const { size } = await stat(path);
|
|
637
|
-
return size;
|
|
638
|
-
} catch {
|
|
639
|
-
return 0;
|
|
640
|
-
}
|
|
641
|
-
});
|
|
642
|
-
return (await Promise.all(ArrayOfPromises)).reduce((acc, size) => acc + size, 0);
|
|
643
|
-
} catch {
|
|
644
|
-
return 0;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
//#endregion
|
|
648
|
-
//#region src/config.ts
|
|
649
|
-
const getMigrator = (name) => {
|
|
650
|
-
return configRegistry[name] || configRegistry["pipeline"];
|
|
651
|
-
};
|
|
652
|
-
const setupConfigFile = async (name, options) => {
|
|
653
|
-
const ctx = options.context;
|
|
654
|
-
const migrator = options.migrator || getMigrator(name);
|
|
655
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All managed files must have a migration schema.`);
|
|
656
|
-
const filesPath = path.isAbsolute(name) ? name : ctx.getConfigPath(`${name}.json`);
|
|
657
|
-
await ensure(filesPath, JSON.stringify(migrator.defaultValue));
|
|
658
|
-
return {
|
|
659
|
-
setConfig: async (config) => {
|
|
660
|
-
const { logger } = useLogger();
|
|
661
|
-
try {
|
|
662
|
-
await fs$1.writeFile(filesPath, JSON.stringify(config));
|
|
663
|
-
return true;
|
|
664
|
-
} catch (e) {
|
|
665
|
-
logger().error(`Error saving config ${name}:`, e);
|
|
666
|
-
return false;
|
|
667
|
-
}
|
|
668
|
-
},
|
|
669
|
-
getConfig: async () => {
|
|
670
|
-
const { logger } = useLogger();
|
|
671
|
-
let content = void 0;
|
|
672
|
-
let originalJson = void 0;
|
|
673
|
-
let parseFailed = false;
|
|
674
|
-
try {
|
|
675
|
-
content = await fs$1.readFile(filesPath, "utf8");
|
|
676
|
-
if (content !== void 0) originalJson = JSON.parse(content);
|
|
677
|
-
} catch (e) {
|
|
678
|
-
logger().error(`Error reading or parsing config ${name}:`, e);
|
|
679
|
-
parseFailed = true;
|
|
680
|
-
}
|
|
681
|
-
let json = void 0;
|
|
682
|
-
let migrationFailed = false;
|
|
683
|
-
try {
|
|
684
|
-
if (!parseFailed) json = await migrator.migrate(originalJson, {
|
|
685
|
-
debug: false,
|
|
686
|
-
onStep: async (state, version) => {
|
|
687
|
-
const parsedPath = path.parse(filesPath);
|
|
688
|
-
const versionedPath = path.join(parsedPath.dir, `${parsedPath.name}.v${version}.json`);
|
|
689
|
-
try {
|
|
690
|
-
await fs$1.writeFile(versionedPath, JSON.stringify(state));
|
|
691
|
-
logger().info(`Intermediate backup created for ${name} at ${versionedPath}`);
|
|
692
|
-
} catch (e) {
|
|
693
|
-
logger().error(`Failed to create intermediate backup for ${name} at v${version}:`, e);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
else json = migrator.defaultValue;
|
|
698
|
-
} catch (e) {
|
|
699
|
-
logger().error(`Error migrating config ${name}:`, e);
|
|
700
|
-
migrationFailed = true;
|
|
701
|
-
json = migrator.defaultValue;
|
|
702
|
-
}
|
|
703
|
-
let normalized = false;
|
|
704
|
-
if (name.startsWith("pipeline-") || path.isAbsolute(name) || name.endsWith(".json") || name === "pipeline") normalized = normalizePipelineConfig(json);
|
|
705
|
-
if (originalJson?.version !== json?.version || normalized || content === void 0 || parseFailed || migrationFailed) {
|
|
706
|
-
if (parseFailed || migrationFailed) try {
|
|
707
|
-
const parsedPath = path.parse(filesPath);
|
|
708
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
709
|
-
const corruptedPath = path.join(parsedPath.dir, `${parsedPath.name}.corrupted.${timestamp}.json`);
|
|
710
|
-
const backupContent = parseFailed ? content || "" : JSON.stringify(originalJson, null, 2);
|
|
711
|
-
await fs$1.writeFile(corruptedPath, backupContent);
|
|
712
|
-
logger().info(`Corrupted config file preserved at ${corruptedPath}`);
|
|
713
|
-
} catch (e) {
|
|
714
|
-
logger().error(`Failed to backup corrupted config ${name}:`, e);
|
|
659
|
+
handle("connections:reset", async (_, { send, value }) => {
|
|
660
|
+
const { key } = value;
|
|
661
|
+
try {
|
|
662
|
+
const manager = await setupConnectionsConfigFile(context);
|
|
663
|
+
const currentConfig = await manager.getConfig();
|
|
664
|
+
const defaultValue = connectionsMigrator.defaultValue[key];
|
|
665
|
+
await manager.setConfig({
|
|
666
|
+
...currentConfig ? currentConfig : {},
|
|
667
|
+
[key]: defaultValue
|
|
668
|
+
});
|
|
669
|
+
send({
|
|
670
|
+
type: "end",
|
|
671
|
+
data: {
|
|
672
|
+
type: "success",
|
|
673
|
+
result: "ok"
|
|
715
674
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
675
|
+
});
|
|
676
|
+
} catch (e) {
|
|
677
|
+
logger().error("connections:reset error:", e);
|
|
678
|
+
send({
|
|
679
|
+
type: "end",
|
|
680
|
+
data: {
|
|
681
|
+
type: "error",
|
|
682
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset connections"
|
|
720
683
|
}
|
|
721
|
-
}
|
|
722
|
-
return json;
|
|
684
|
+
});
|
|
723
685
|
}
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
//#region src/handlers/config.ts
|
|
728
|
-
const registerConfigHandlers = (context) => {
|
|
729
|
-
const { handle } = useAPI();
|
|
730
|
-
const { logger } = useLogger();
|
|
731
|
-
handle("config:load", async (_, { send, value }) => {
|
|
732
|
-
const { config: name } = value;
|
|
733
|
-
logger().info("config:load", name);
|
|
686
|
+
});
|
|
687
|
+
handle("projects:load", async (_, { send }) => {
|
|
688
|
+
logger().info("projects:load");
|
|
734
689
|
try {
|
|
735
690
|
send({
|
|
736
691
|
type: "end",
|
|
737
692
|
data: {
|
|
738
693
|
type: "success",
|
|
739
|
-
result:
|
|
694
|
+
result: await (await setupProjectsConfigFile(context)).getConfig()
|
|
740
695
|
}
|
|
741
696
|
});
|
|
742
697
|
} catch (e) {
|
|
743
|
-
logger().error(
|
|
698
|
+
logger().error("projects:load error:", e);
|
|
744
699
|
send({
|
|
745
700
|
type: "end",
|
|
746
701
|
data: {
|
|
747
702
|
type: "error",
|
|
748
|
-
ipcError: e instanceof Error ? e.message :
|
|
703
|
+
ipcError: e instanceof Error ? e.message : "Unable to load projects"
|
|
749
704
|
}
|
|
750
705
|
});
|
|
751
706
|
}
|
|
752
707
|
});
|
|
753
|
-
handle("
|
|
754
|
-
const { data
|
|
708
|
+
handle("projects:save", async (_, { send, value }) => {
|
|
709
|
+
const { data } = value;
|
|
755
710
|
try {
|
|
756
|
-
const manager = await
|
|
711
|
+
const manager = await setupProjectsConfigFile(context);
|
|
757
712
|
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
758
713
|
await manager.setConfig(json);
|
|
759
714
|
send({
|
|
760
715
|
type: "end",
|
|
761
716
|
data: {
|
|
762
717
|
type: "success",
|
|
763
|
-
result:
|
|
718
|
+
result: "ok"
|
|
764
719
|
}
|
|
765
720
|
});
|
|
766
721
|
} catch (e) {
|
|
767
|
-
logger().error(
|
|
722
|
+
logger().error("projects:save error:", e);
|
|
768
723
|
send({
|
|
769
724
|
type: "end",
|
|
770
725
|
data: {
|
|
771
726
|
type: "error",
|
|
772
|
-
ipcError: e instanceof Error ? e.message :
|
|
727
|
+
ipcError: e instanceof Error ? e.message : "Unable to save projects"
|
|
773
728
|
}
|
|
774
729
|
});
|
|
775
730
|
}
|
|
776
731
|
});
|
|
777
|
-
handle("
|
|
778
|
-
const {
|
|
779
|
-
logger().info("config:reset", name, key);
|
|
732
|
+
handle("projects:reset", async (_, { send, value }) => {
|
|
733
|
+
const { key } = value;
|
|
780
734
|
try {
|
|
781
|
-
const manager = await
|
|
735
|
+
const manager = await setupProjectsConfigFile(context);
|
|
782
736
|
const currentConfig = await manager.getConfig();
|
|
783
|
-
const
|
|
784
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}`);
|
|
785
|
-
const defaultValue = migrator.defaultValue[key];
|
|
737
|
+
const defaultValue = fileRepoMigrations.defaultValue[key];
|
|
786
738
|
await manager.setConfig({
|
|
787
739
|
...currentConfig ? currentConfig : {},
|
|
788
740
|
[key]: defaultValue
|
|
@@ -791,50 +743,154 @@ const registerConfigHandlers = (context) => {
|
|
|
791
743
|
type: "end",
|
|
792
744
|
data: {
|
|
793
745
|
type: "success",
|
|
794
|
-
result:
|
|
746
|
+
result: "ok"
|
|
795
747
|
}
|
|
796
748
|
});
|
|
797
749
|
} catch (e) {
|
|
798
|
-
logger().error(
|
|
750
|
+
logger().error("projects:reset error:", e);
|
|
799
751
|
send({
|
|
800
752
|
type: "end",
|
|
801
753
|
data: {
|
|
802
754
|
type: "error",
|
|
803
|
-
ipcError: e instanceof Error ? e.message :
|
|
755
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset projects"
|
|
804
756
|
}
|
|
805
757
|
});
|
|
806
758
|
}
|
|
807
759
|
});
|
|
808
|
-
handle("
|
|
809
|
-
const {
|
|
810
|
-
logger().info("
|
|
760
|
+
handle("pipeline:load-by-name", async (_, { send, value }) => {
|
|
761
|
+
const { name } = value;
|
|
762
|
+
logger().info("pipeline:load-by-name", name);
|
|
811
763
|
try {
|
|
812
|
-
const filesPath = path.isAbsolute(name) ? name : context.getConfigPath(`${name}.json`);
|
|
813
|
-
await fs$1.rm(filesPath, { force: true });
|
|
814
|
-
const parsedPath = path.parse(filesPath);
|
|
815
|
-
const dirEntries = await fs$1.readdir(parsedPath.dir).catch(() => []);
|
|
816
|
-
const prefix = `${parsedPath.name}.v`;
|
|
817
|
-
const suffix = `.json`;
|
|
818
|
-
for (const entry of dirEntries) if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
|
|
819
|
-
const backupPath = path.join(parsedPath.dir, entry);
|
|
820
|
-
await fs$1.rm(backupPath, { force: true }).catch((err) => {
|
|
821
|
-
logger().error(`Failed to delete backup ${backupPath}:`, err);
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
764
|
send({
|
|
825
765
|
type: "end",
|
|
826
766
|
data: {
|
|
827
767
|
type: "success",
|
|
828
|
-
result:
|
|
768
|
+
result: await (await setupPipelineConfigFileByName(name, context)).getConfig()
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
} catch (e) {
|
|
772
|
+
logger().error(`pipeline:load-by-name error for ${name}:`, e);
|
|
773
|
+
send({
|
|
774
|
+
type: "end",
|
|
775
|
+
data: {
|
|
776
|
+
type: "error",
|
|
777
|
+
ipcError: e instanceof Error ? e.message : `Unable to load pipeline ${name}`
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
handle("pipeline:load-by-path", async (_, { send, value }) => {
|
|
783
|
+
const { path: absolutePath } = value;
|
|
784
|
+
logger().info("pipeline:load-by-path", absolutePath);
|
|
785
|
+
try {
|
|
786
|
+
send({
|
|
787
|
+
type: "end",
|
|
788
|
+
data: {
|
|
789
|
+
type: "success",
|
|
790
|
+
result: await (await setupPipelineConfigFileByPath(absolutePath, context)).getConfig()
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
} catch (e) {
|
|
794
|
+
logger().error(`pipeline:load-by-path error for ${absolutePath}:`, e);
|
|
795
|
+
send({
|
|
796
|
+
type: "end",
|
|
797
|
+
data: {
|
|
798
|
+
type: "error",
|
|
799
|
+
ipcError: e instanceof Error ? e.message : `Unable to load pipeline ${absolutePath}`
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
handle("pipeline:save-by-name", async (_, { send, value }) => {
|
|
805
|
+
const { data, name } = value;
|
|
806
|
+
try {
|
|
807
|
+
const manager = await setupPipelineConfigFileByName(name, context);
|
|
808
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
809
|
+
await manager.setConfig(json);
|
|
810
|
+
send({
|
|
811
|
+
type: "end",
|
|
812
|
+
data: {
|
|
813
|
+
type: "success",
|
|
814
|
+
result: "ok"
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
} catch (e) {
|
|
818
|
+
logger().error(`pipeline:save-by-name error for ${name}:`, e);
|
|
819
|
+
send({
|
|
820
|
+
type: "end",
|
|
821
|
+
data: {
|
|
822
|
+
type: "error",
|
|
823
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${name}`
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
handle("pipeline:save-by-path", async (_, { send, value }) => {
|
|
829
|
+
const { data, path: absolutePath } = value;
|
|
830
|
+
try {
|
|
831
|
+
const manager = await setupPipelineConfigFileByPath(absolutePath, context);
|
|
832
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
833
|
+
await manager.setConfig(json);
|
|
834
|
+
send({
|
|
835
|
+
type: "end",
|
|
836
|
+
data: {
|
|
837
|
+
type: "success",
|
|
838
|
+
result: "ok"
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
} catch (e) {
|
|
842
|
+
logger().error(`pipeline:save-by-path error for ${absolutePath}:`, e);
|
|
843
|
+
send({
|
|
844
|
+
type: "end",
|
|
845
|
+
data: {
|
|
846
|
+
type: "error",
|
|
847
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${absolutePath}`
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
handle("pipeline:delete-by-name", async (_, { send, value }) => {
|
|
853
|
+
const { name } = value;
|
|
854
|
+
logger().info("pipeline:delete-by-name", name);
|
|
855
|
+
try {
|
|
856
|
+
await deletePipelineConfigFileByName(name, context);
|
|
857
|
+
send({
|
|
858
|
+
type: "end",
|
|
859
|
+
data: {
|
|
860
|
+
type: "success",
|
|
861
|
+
result: "ok"
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
} catch (e) {
|
|
865
|
+
logger().error(`pipeline:delete-by-name error for ${name}:`, e);
|
|
866
|
+
send({
|
|
867
|
+
type: "end",
|
|
868
|
+
data: {
|
|
869
|
+
type: "error",
|
|
870
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${name}`
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
handle("pipeline:delete-by-path", async (_, { send, value }) => {
|
|
876
|
+
const { path: absolutePath } = value;
|
|
877
|
+
logger().info("pipeline:delete-by-path", absolutePath);
|
|
878
|
+
try {
|
|
879
|
+
await deletePipelineConfigFileByPath(absolutePath, context);
|
|
880
|
+
send({
|
|
881
|
+
type: "end",
|
|
882
|
+
data: {
|
|
883
|
+
type: "success",
|
|
884
|
+
result: "ok"
|
|
829
885
|
}
|
|
830
886
|
});
|
|
831
887
|
} catch (e) {
|
|
832
|
-
logger().error(`
|
|
888
|
+
logger().error(`pipeline:delete-by-path error for ${absolutePath}:`, e);
|
|
833
889
|
send({
|
|
834
890
|
type: "end",
|
|
835
891
|
data: {
|
|
836
892
|
type: "error",
|
|
837
|
-
ipcError: e instanceof Error ? e.message : `Unable to delete
|
|
893
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${absolutePath}`
|
|
838
894
|
}
|
|
839
895
|
});
|
|
840
896
|
}
|
|
@@ -848,11 +904,10 @@ var BuildHistoryStorage = class {
|
|
|
848
904
|
this.context = context;
|
|
849
905
|
}
|
|
850
906
|
getStoragePath() {
|
|
851
|
-
return
|
|
907
|
+
return this.context.getConfigPath("pipelines");
|
|
852
908
|
}
|
|
853
909
|
getPipelinePath(pipelineId) {
|
|
854
|
-
|
|
855
|
-
return join(this.getStoragePath(), `pipeline-${sanitizedId}.json`);
|
|
910
|
+
return join(this.getStoragePath(), `${pipelineId}.history.json`);
|
|
856
911
|
}
|
|
857
912
|
async ensureStoragePath() {
|
|
858
913
|
try {
|
|
@@ -879,35 +934,6 @@ var BuildHistoryStorage = class {
|
|
|
879
934
|
throw new Error(`Failed to save pipeline history: ${error}`);
|
|
880
935
|
}
|
|
881
936
|
}
|
|
882
|
-
async applyRetentionPolicy() {
|
|
883
|
-
try {
|
|
884
|
-
this.logger.logger().info("Applying build history retention policy...");
|
|
885
|
-
const policy = (await (await setupConfigFile("settings", { context: this.context })).getConfig())?.buildHistory?.retentionPolicy;
|
|
886
|
-
if (!policy || !policy.enabled) {
|
|
887
|
-
this.logger.logger().info("Retention policy is disabled. Skipping.");
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
const { maxAge, maxEntries } = policy;
|
|
891
|
-
const pipelineFiles = await this.getAllPipelineFiles();
|
|
892
|
-
for (const file of pipelineFiles) {
|
|
893
|
-
const pipelineId = file.replace("pipeline-", "").replace(".json", "");
|
|
894
|
-
let entries = await this.loadPipelineHistory(pipelineId);
|
|
895
|
-
const originalCount = entries.length;
|
|
896
|
-
if (maxAge > 0) {
|
|
897
|
-
const minDate = Date.now() - maxAge * 24 * 60 * 60 * 1e3;
|
|
898
|
-
entries = entries.filter((entry) => entry.createdAt >= minDate);
|
|
899
|
-
}
|
|
900
|
-
if (maxEntries > 0 && entries.length > maxEntries) entries = entries.sort((a, b) => b.createdAt - a.createdAt).slice(0, maxEntries);
|
|
901
|
-
if (entries.length < originalCount) {
|
|
902
|
-
this.logger.logger().info(`[${pipelineId}] Pruned ${originalCount - entries.length} history entries.`);
|
|
903
|
-
await this.savePipelineHistory(pipelineId, entries);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
this.logger.logger().info("Retention policy applied successfully.");
|
|
907
|
-
} catch (error) {
|
|
908
|
-
this.logger.logger().error("Failed to apply retention policy:", error);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
937
|
async save(entry) {
|
|
912
938
|
try {
|
|
913
939
|
const entries = await this.loadPipelineHistory(entry.pipelineId);
|
|
@@ -916,9 +942,6 @@ var BuildHistoryStorage = class {
|
|
|
916
942
|
else entries.push(entry);
|
|
917
943
|
await this.savePipelineHistory(entry.pipelineId, entries);
|
|
918
944
|
this.logger.logger().info(`Saved build history entry: ${entry.id} for pipeline: ${entry.pipelineId}`);
|
|
919
|
-
this.applyRetentionPolicy().catch((err) => {
|
|
920
|
-
this.logger.logger().error("Failed to apply retention policy after save:", err);
|
|
921
|
-
});
|
|
922
945
|
} catch (error) {
|
|
923
946
|
this.logger.logger().error("Failed to save build history entry:", error);
|
|
924
947
|
throw new Error(`Failed to save build history entry: ${error}`);
|
|
@@ -929,7 +952,8 @@ var BuildHistoryStorage = class {
|
|
|
929
952
|
if (pipelineId) return (await this.loadPipelineHistory(pipelineId)).find((e) => e.id === id);
|
|
930
953
|
const files = await this.getAllPipelineFiles();
|
|
931
954
|
for (const file of files) {
|
|
932
|
-
const pId =
|
|
955
|
+
const pId = this.parsePipelineIdFromFilename(file);
|
|
956
|
+
if (!pId) continue;
|
|
933
957
|
const entry = (await this.loadPipelineHistory(pId)).find((e) => e.id === id);
|
|
934
958
|
if (entry) return entry;
|
|
935
959
|
}
|
|
@@ -944,7 +968,8 @@ var BuildHistoryStorage = class {
|
|
|
944
968
|
const files = await this.getAllPipelineFiles();
|
|
945
969
|
const allEntries = [];
|
|
946
970
|
for (const file of files) {
|
|
947
|
-
const pipelineId =
|
|
971
|
+
const pipelineId = this.parsePipelineIdFromFilename(file);
|
|
972
|
+
if (!pipelineId) continue;
|
|
948
973
|
const entries = await this.loadPipelineHistory(pipelineId);
|
|
949
974
|
allEntries.push(...entries);
|
|
950
975
|
}
|
|
@@ -979,7 +1004,8 @@ var BuildHistoryStorage = class {
|
|
|
979
1004
|
} else {
|
|
980
1005
|
const files = await this.getAllPipelineFiles();
|
|
981
1006
|
for (const file of files) {
|
|
982
|
-
const pId =
|
|
1007
|
+
const pId = this.parsePipelineIdFromFilename(file);
|
|
1008
|
+
if (!pId) continue;
|
|
983
1009
|
const entries = await this.loadPipelineHistory(pId);
|
|
984
1010
|
const entryIndex = entries.findIndex((e) => e.id === id);
|
|
985
1011
|
if (entryIndex >= 0) {
|
|
@@ -1013,7 +1039,8 @@ var BuildHistoryStorage = class {
|
|
|
1013
1039
|
} else {
|
|
1014
1040
|
const files = await this.getAllPipelineFiles();
|
|
1015
1041
|
for (const file of files) {
|
|
1016
|
-
const pId =
|
|
1042
|
+
const pId = this.parsePipelineIdFromFilename(file);
|
|
1043
|
+
if (!pId) continue;
|
|
1017
1044
|
const entries = await this.loadPipelineHistory(pId);
|
|
1018
1045
|
const entryIndex = entries.findIndex((e) => e.id === id);
|
|
1019
1046
|
if (entryIndex >= 0) {
|
|
@@ -1034,8 +1061,20 @@ var BuildHistoryStorage = class {
|
|
|
1034
1061
|
try {
|
|
1035
1062
|
await this.ensureStoragePath();
|
|
1036
1063
|
const files = await this.getAllPipelineFiles();
|
|
1037
|
-
|
|
1064
|
+
const cachePathsToDelete = /* @__PURE__ */ new Set();
|
|
1065
|
+
for (const file of files) {
|
|
1066
|
+
const pipelineId = this.parsePipelineIdFromFilename(file);
|
|
1067
|
+
if (pipelineId) {
|
|
1068
|
+
const entries = await this.loadPipelineHistory(pipelineId);
|
|
1069
|
+
for (const entry of entries) if (entry.cachePath) cachePathsToDelete.add(entry.cachePath);
|
|
1070
|
+
}
|
|
1071
|
+
await unlink(join(this.getStoragePath(), file));
|
|
1072
|
+
}
|
|
1038
1073
|
this.logger.logger().info("Cleared all build history");
|
|
1074
|
+
for (const cachePath of cachePathsToDelete) await rm(cachePath, {
|
|
1075
|
+
recursive: true,
|
|
1076
|
+
force: true
|
|
1077
|
+
}).catch(() => {});
|
|
1039
1078
|
} catch (error) {
|
|
1040
1079
|
this.logger.logger().error("Failed to clear build history:", error);
|
|
1041
1080
|
throw new Error(`Failed to clear build history: ${error}`);
|
|
@@ -1043,8 +1082,16 @@ var BuildHistoryStorage = class {
|
|
|
1043
1082
|
}
|
|
1044
1083
|
async clearByPipeline(pipelineId) {
|
|
1045
1084
|
try {
|
|
1046
|
-
|
|
1085
|
+
const pipelinePath = this.getPipelinePath(pipelineId);
|
|
1086
|
+
const entries = await this.loadPipelineHistory(pipelineId);
|
|
1087
|
+
await unlink(pipelinePath);
|
|
1047
1088
|
this.logger.logger().info(`Cleared history for pipeline "${pipelineId}"`);
|
|
1089
|
+
const cachePathsToDelete = /* @__PURE__ */ new Set();
|
|
1090
|
+
for (const entry of entries) if (entry.cachePath) cachePathsToDelete.add(entry.cachePath);
|
|
1091
|
+
for (const cachePath of cachePathsToDelete) await rm(cachePath, {
|
|
1092
|
+
recursive: true,
|
|
1093
|
+
force: true
|
|
1094
|
+
}).catch(() => {});
|
|
1048
1095
|
} catch (error) {
|
|
1049
1096
|
if (error.code === "ENOENT") {
|
|
1050
1097
|
this.logger.logger().warn(`No history file found for pipeline "${pipelineId}". Nothing to clear.`);
|
|
@@ -1069,21 +1116,11 @@ var BuildHistoryStorage = class {
|
|
|
1069
1116
|
size
|
|
1070
1117
|
});
|
|
1071
1118
|
}
|
|
1072
|
-
const policy = (await (await setupConfigFile("settings", { context: this.context })).getConfig())?.buildHistory?.retentionPolicy || {
|
|
1073
|
-
enabled: false,
|
|
1074
|
-
maxEntries: 50,
|
|
1075
|
-
maxAge: 30
|
|
1076
|
-
};
|
|
1077
1119
|
if (allEntries.length === 0) return {
|
|
1078
1120
|
totalEntries: 0,
|
|
1079
1121
|
totalSize: 0,
|
|
1080
1122
|
numberOfPipelines: files.length,
|
|
1081
1123
|
userDataPath: this.context.userDataPath,
|
|
1082
|
-
retentionPolicy: {
|
|
1083
|
-
enabled: policy.enabled,
|
|
1084
|
-
maxEntries: policy.maxEntries,
|
|
1085
|
-
maxAge: policy.maxAge
|
|
1086
|
-
},
|
|
1087
1124
|
disk: {
|
|
1088
1125
|
total: diskSpace.size,
|
|
1089
1126
|
free: diskSpace.free,
|
|
@@ -1108,11 +1145,6 @@ var BuildHistoryStorage = class {
|
|
|
1108
1145
|
newestEntry: sortedEntries[sortedEntries.length - 1]?.createdAt,
|
|
1109
1146
|
numberOfPipelines: files.length,
|
|
1110
1147
|
userDataPath: this.context.userDataPath,
|
|
1111
|
-
retentionPolicy: {
|
|
1112
|
-
enabled: policy.enabled,
|
|
1113
|
-
maxEntries: policy.maxEntries,
|
|
1114
|
-
maxAge: policy.maxAge
|
|
1115
|
-
},
|
|
1116
1148
|
disk: {
|
|
1117
1149
|
total: diskSpace.size,
|
|
1118
1150
|
free: diskSpace.free,
|
|
@@ -1128,11 +1160,15 @@ var BuildHistoryStorage = class {
|
|
|
1128
1160
|
async getAllPipelineFiles() {
|
|
1129
1161
|
try {
|
|
1130
1162
|
await this.ensureStoragePath();
|
|
1131
|
-
return (await readdir(this.getStoragePath())).filter((file) => file.
|
|
1163
|
+
return (await readdir(this.getStoragePath())).filter((file) => file.endsWith(".history.json"));
|
|
1132
1164
|
} catch (error) {
|
|
1133
1165
|
return [];
|
|
1134
1166
|
}
|
|
1135
1167
|
}
|
|
1168
|
+
parsePipelineIdFromFilename(filename) {
|
|
1169
|
+
const match = filename.match(/^(.+)\.history\.json$/);
|
|
1170
|
+
return match ? match[1] : null;
|
|
1171
|
+
}
|
|
1136
1172
|
};
|
|
1137
1173
|
//#endregion
|
|
1138
1174
|
//#region src/handlers/history.ts
|
|
@@ -1414,40 +1450,6 @@ const registerHistoryHandlers = (context) => {
|
|
|
1414
1450
|
});
|
|
1415
1451
|
}
|
|
1416
1452
|
});
|
|
1417
|
-
handle("build-history:configure", async (_, { send, value }) => {
|
|
1418
|
-
try {
|
|
1419
|
-
logger().info("Updating build history configuration:", value.config);
|
|
1420
|
-
const settings = await setupConfigFile("settings", { context });
|
|
1421
|
-
const currentConfig = await settings.getConfig();
|
|
1422
|
-
const newConfig = {
|
|
1423
|
-
...currentConfig,
|
|
1424
|
-
buildHistory: {
|
|
1425
|
-
...currentConfig?.buildHistory,
|
|
1426
|
-
retentionPolicy: {
|
|
1427
|
-
...currentConfig?.buildHistory?.retentionPolicy,
|
|
1428
|
-
...value.config.retentionPolicy
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
};
|
|
1432
|
-
await settings.setConfig(newConfig);
|
|
1433
|
-
send({
|
|
1434
|
-
type: "end",
|
|
1435
|
-
data: {
|
|
1436
|
-
type: "success",
|
|
1437
|
-
result: { result: "ok" }
|
|
1438
|
-
}
|
|
1439
|
-
});
|
|
1440
|
-
} catch (error) {
|
|
1441
|
-
logger().error("Failed to configure build history:", error);
|
|
1442
|
-
send({
|
|
1443
|
-
type: "end",
|
|
1444
|
-
data: {
|
|
1445
|
-
type: "error",
|
|
1446
|
-
ipcError: error instanceof Error ? error.message : "Failed to configure build history"
|
|
1447
|
-
}
|
|
1448
|
-
});
|
|
1449
|
-
}
|
|
1450
|
-
});
|
|
1451
1453
|
};
|
|
1452
1454
|
//#endregion
|
|
1453
1455
|
//#region ../../node_modules/serve-handler/src/glob-slash.js
|
|
@@ -8437,7 +8439,7 @@ var require_error = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
8437
8439
|
})();
|
|
8438
8440
|
}));
|
|
8439
8441
|
//#endregion
|
|
8440
|
-
//#region src/
|
|
8442
|
+
//#region src/server.ts
|
|
8441
8443
|
var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8442
8444
|
const { promisify } = __require("util");
|
|
8443
8445
|
const path$1 = __require("path");
|
|
@@ -8891,44 +8893,6 @@ var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((export
|
|
|
8891
8893
|
stream.pipe(response);
|
|
8892
8894
|
};
|
|
8893
8895
|
})))(), 1);
|
|
8894
|
-
/**
|
|
8895
|
-
* Registers migration handlers for the CLI/Standalone server.
|
|
8896
|
-
* This overrides the default handlers from @pipelab/core-node to include
|
|
8897
|
-
* pipeline and file repo migrations directly in the backend.
|
|
8898
|
-
*/
|
|
8899
|
-
function registerMigrationHandlers(context) {
|
|
8900
|
-
const { handle } = useAPI();
|
|
8901
|
-
const { logger } = useLogger();
|
|
8902
|
-
handle("config:load", async (_, { send, value }) => {
|
|
8903
|
-
const { config: name } = value;
|
|
8904
|
-
logger().info("[CLI Migration] config:load", name);
|
|
8905
|
-
try {
|
|
8906
|
-
const migrator = getMigrator(name);
|
|
8907
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All files loaded via config:load MUST have a migration schema.`);
|
|
8908
|
-
send({
|
|
8909
|
-
type: "end",
|
|
8910
|
-
data: {
|
|
8911
|
-
type: "success",
|
|
8912
|
-
result: { result: await (await setupConfigFile(name, {
|
|
8913
|
-
context,
|
|
8914
|
-
migrator
|
|
8915
|
-
})).getConfig() }
|
|
8916
|
-
}
|
|
8917
|
-
});
|
|
8918
|
-
} catch (e) {
|
|
8919
|
-
logger().error(`[CLI Migration] config:load error for ${name}:`, e);
|
|
8920
|
-
send({
|
|
8921
|
-
type: "end",
|
|
8922
|
-
data: {
|
|
8923
|
-
type: "error",
|
|
8924
|
-
ipcError: e instanceof Error ? e.message : `Unable to load config ${name}`
|
|
8925
|
-
}
|
|
8926
|
-
});
|
|
8927
|
-
}
|
|
8928
|
-
});
|
|
8929
|
-
}
|
|
8930
|
-
//#endregion
|
|
8931
|
-
//#region src/server.ts
|
|
8932
8896
|
const sendStartupProgress = (message) => {
|
|
8933
8897
|
console.log(`[Startup Progress] ${message}`);
|
|
8934
8898
|
webSocketServer.broadcast("startup:progress", {
|
|
@@ -8997,7 +8961,13 @@ async function serveCommand(options, version, _dirname) {
|
|
|
8997
8961
|
response.end(`Error: UI directory not found at ${rawAssetFolder}.\nPlease run 'pnpm build' in apps/ui to generate the distribution.`);
|
|
8998
8962
|
return;
|
|
8999
8963
|
}
|
|
9000
|
-
return (0, import_src.default)(request, response, {
|
|
8964
|
+
return (0, import_src.default)(request, response, {
|
|
8965
|
+
public: rawAssetFolder,
|
|
8966
|
+
rewrites: [{
|
|
8967
|
+
source: "/**",
|
|
8968
|
+
destination: "/index.html"
|
|
8969
|
+
}]
|
|
8970
|
+
});
|
|
9001
8971
|
});
|
|
9002
8972
|
console.log(`Starting Pipelab server on port ${options.port}...`);
|
|
9003
8973
|
if (isDev) {
|
|
@@ -9009,7 +8979,6 @@ async function serveCommand(options, version, _dirname) {
|
|
|
9009
8979
|
version,
|
|
9010
8980
|
context
|
|
9011
8981
|
});
|
|
9012
|
-
registerMigrationHandlers(context);
|
|
9013
8982
|
sendStartupReady();
|
|
9014
8983
|
return server;
|
|
9015
8984
|
}
|
|
@@ -9104,7 +9073,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
9104
9073
|
console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
|
|
9105
9074
|
} else throw new Error(`Offline and no local fallback version available for ${packageName}`);
|
|
9106
9075
|
} else try {
|
|
9107
|
-
const cachePath =
|
|
9076
|
+
const cachePath = ctx.getCachePath(CacheFolder.Pacote);
|
|
9108
9077
|
let packumentPromise = packumentRequests.get(packageName);
|
|
9109
9078
|
if (!packumentPromise) {
|
|
9110
9079
|
packumentPromise = pacote.packument(packageName, { cache: cachePath });
|
|
@@ -9135,7 +9104,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
|
|
|
9135
9104
|
console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
|
|
9136
9105
|
} else throw error;
|
|
9137
9106
|
}
|
|
9138
|
-
const cachePath =
|
|
9107
|
+
const cachePath = ctx.getCachePath(CacheFolder.Pacote);
|
|
9139
9108
|
const packageDir = join(baseDir, resolvedVersion);
|
|
9140
9109
|
const checkStart = Date.now();
|
|
9141
9110
|
const isInstalled = options?.installDeps ? isPackageComplete(packageDir) && isDependenciesInstalledSync(packageDir) : isPackageComplete(packageDir);
|
|
@@ -9212,7 +9181,7 @@ async function runPnpm(cwd, options) {
|
|
|
9212
9181
|
...process.env,
|
|
9213
9182
|
NODE_ENV: "production",
|
|
9214
9183
|
PATH: nodePath ? `${dirname(nodePath)}${delimiter}${process.env.PATH}` : process.env.PATH,
|
|
9215
|
-
PNPM_HOME:
|
|
9184
|
+
PNPM_HOME: ctx.getPnpmPath(),
|
|
9216
9185
|
PNPM_ONLY_ALLOW_TRUSTED_DEPENDENCIES: "false",
|
|
9217
9186
|
...extraEnv
|
|
9218
9187
|
}
|
|
@@ -9741,8 +9710,8 @@ const builtInPlugins = async (options) => {
|
|
|
9741
9710
|
}
|
|
9742
9711
|
}
|
|
9743
9712
|
try {
|
|
9744
|
-
const {
|
|
9745
|
-
const settingsPlugins = (await (await
|
|
9713
|
+
const { setupSettingsConfigFile } = await import("./config-Bi0ORcTK.mjs");
|
|
9714
|
+
const settingsPlugins = (await (await setupSettingsConfigFile(options.context)).getConfig())?.plugins || [];
|
|
9746
9715
|
for (const plugin of settingsPlugins) if (plugin.name) if (plugin.enabled) {
|
|
9747
9716
|
if (!pluginsToLoad.has(plugin.name)) pluginsToLoad.set(plugin.name, "latest");
|
|
9748
9717
|
} else pluginsToLoad.delete(plugin.name);
|
|
@@ -9812,6 +9781,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9812
9781
|
projectName,
|
|
9813
9782
|
projectPath,
|
|
9814
9783
|
pipelineId,
|
|
9784
|
+
cachePath,
|
|
9815
9785
|
startTime,
|
|
9816
9786
|
status: "running",
|
|
9817
9787
|
logs: [],
|
|
@@ -9840,7 +9810,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9840
9810
|
} else logger().error(`[Runner] Failed to load or register plugin "${pluginId}"`);
|
|
9841
9811
|
}
|
|
9842
9812
|
logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
|
|
9843
|
-
await (await
|
|
9813
|
+
await (await setupSettingsConfigFile(ctx)).getConfig();
|
|
9844
9814
|
try {
|
|
9845
9815
|
const result = await processGraph({
|
|
9846
9816
|
graph,
|
|
@@ -9908,7 +9878,6 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9908
9878
|
}, pipelineId);
|
|
9909
9879
|
throw error;
|
|
9910
9880
|
} finally {
|
|
9911
|
-
if (!shouldDisableHistory) buildHistoryStorage.applyRetentionPolicy();
|
|
9912
9881
|
try {
|
|
9913
9882
|
await rm(sandboxPath, {
|
|
9914
9883
|
recursive: true,
|
|
@@ -9917,6 +9886,14 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9917
9886
|
} catch (e) {
|
|
9918
9887
|
console.warn(`Failed to cleanup sandbox at ${sandboxPath}:`, e);
|
|
9919
9888
|
}
|
|
9889
|
+
try {
|
|
9890
|
+
await rm(cachePath, {
|
|
9891
|
+
recursive: true,
|
|
9892
|
+
force: true
|
|
9893
|
+
});
|
|
9894
|
+
} catch (e) {
|
|
9895
|
+
console.warn(`Failed to cleanup cache at ${cachePath}:`, e);
|
|
9896
|
+
}
|
|
9920
9897
|
}
|
|
9921
9898
|
};
|
|
9922
9899
|
//#endregion
|
|
@@ -10292,24 +10269,32 @@ const registerEngineHandlers = (context) => {
|
|
|
10292
10269
|
};
|
|
10293
10270
|
handle("action:execute", async (event, { send, value }) => {
|
|
10294
10271
|
const { nodeId, params, pluginId } = value;
|
|
10295
|
-
|
|
10296
|
-
const cachePath = join(context.userDataPath, "cache", "actions", pluginId, nodeId);
|
|
10272
|
+
const cachePath = context.getCachePath(CacheFolder.Actions, pluginId, nodeId);
|
|
10297
10273
|
const cwd = await context.createTempFolder("action-execute-");
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10274
|
+
try {
|
|
10275
|
+
const mainWindow = void 0;
|
|
10276
|
+
abortControllerGraph = new AbortController();
|
|
10277
|
+
const signalPromise = new Promise((resolve, reject) => {
|
|
10278
|
+
abortControllerGraph.signal.addEventListener("abort", async () => {
|
|
10279
|
+
await send({
|
|
10280
|
+
type: "end",
|
|
10281
|
+
data: {
|
|
10282
|
+
ipcError: "Action aborted",
|
|
10283
|
+
type: "error"
|
|
10284
|
+
}
|
|
10285
|
+
});
|
|
10286
|
+
return reject(/* @__PURE__ */ new Error("Action interrupted"));
|
|
10308
10287
|
});
|
|
10309
|
-
return reject(/* @__PURE__ */ new Error("Action interrupted"));
|
|
10310
10288
|
});
|
|
10311
|
-
|
|
10312
|
-
|
|
10289
|
+
await Promise.race([signalPromise, effectiveActionExecute(nodeId, pluginId, params, mainWindow, send, cwd, cachePath)]);
|
|
10290
|
+
} finally {
|
|
10291
|
+
await rm(cwd, {
|
|
10292
|
+
recursive: true,
|
|
10293
|
+
force: true
|
|
10294
|
+
}).catch((err) => {
|
|
10295
|
+
console.warn(`Failed to cleanup temp folder at ${cwd}:`, err);
|
|
10296
|
+
});
|
|
10297
|
+
}
|
|
10313
10298
|
});
|
|
10314
10299
|
handle("constants:get", async (_, { send }) => {
|
|
10315
10300
|
const userData = context.userDataPath;
|
|
@@ -10333,11 +10318,11 @@ const registerEngineHandlers = (context) => {
|
|
|
10333
10318
|
});
|
|
10334
10319
|
handle("graph:execute", async (event, { send, value }) => {
|
|
10335
10320
|
const { graph, variables, projectName, projectPath, pipelineId } = value;
|
|
10336
|
-
await (await
|
|
10321
|
+
await (await setupSettingsConfigFile(context)).getConfig();
|
|
10337
10322
|
const effectiveProjectName = projectName || "Unnamed Project";
|
|
10338
10323
|
const effectiveProjectPath = projectPath || "";
|
|
10339
10324
|
const effectivePipelineId = pipelineId || "unknown";
|
|
10340
|
-
const effectiveCachePath =
|
|
10325
|
+
const effectiveCachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
|
|
10341
10326
|
const mainWindow = void 0;
|
|
10342
10327
|
abortControllerGraph = new AbortController();
|
|
10343
10328
|
try {
|
|
@@ -10445,7 +10430,7 @@ var JsonFileStorage = class {
|
|
|
10445
10430
|
filePath;
|
|
10446
10431
|
logger = useLogger().logger;
|
|
10447
10432
|
constructor(fileName, context) {
|
|
10448
|
-
this.filePath =
|
|
10433
|
+
this.filePath = context.getConfigPath(fileName);
|
|
10449
10434
|
const dir = path.dirname(this.filePath);
|
|
10450
10435
|
if (!fs.existsSync(dir)) try {
|
|
10451
10436
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -10886,6 +10871,470 @@ const registerPluginsHandlers = (context) => {
|
|
|
10886
10871
|
});
|
|
10887
10872
|
};
|
|
10888
10873
|
//#endregion
|
|
10874
|
+
//#region src/handlers/migration.ts
|
|
10875
|
+
const registerMigrationHandlers = (context) => {
|
|
10876
|
+
const { handle } = useAPI();
|
|
10877
|
+
const { logger } = useLogger();
|
|
10878
|
+
const getFileMetadata = async (filePath) => {
|
|
10879
|
+
const exists = existsSync(filePath);
|
|
10880
|
+
let mtime = Date.now();
|
|
10881
|
+
let version = null;
|
|
10882
|
+
if (exists) {
|
|
10883
|
+
try {
|
|
10884
|
+
const stat = await fs$1.stat(filePath);
|
|
10885
|
+
mtime = stat.mtime ? stat.mtime.getTime() : Date.now();
|
|
10886
|
+
} catch (e) {
|
|
10887
|
+
mtime = Date.now();
|
|
10888
|
+
}
|
|
10889
|
+
try {
|
|
10890
|
+
const content = await fs$1.readFile(filePath, "utf8");
|
|
10891
|
+
const json = JSON.parse(content);
|
|
10892
|
+
version = json && typeof json.version === "string" ? json.version : null;
|
|
10893
|
+
} catch (e) {}
|
|
10894
|
+
} else mtime = Date.now();
|
|
10895
|
+
return {
|
|
10896
|
+
exists,
|
|
10897
|
+
mtime,
|
|
10898
|
+
version
|
|
10899
|
+
};
|
|
10900
|
+
};
|
|
10901
|
+
const isVersionImportable = (sourceVer, targetVer) => {
|
|
10902
|
+
if (!sourceVer) return false;
|
|
10903
|
+
if (!targetVer) return true;
|
|
10904
|
+
try {
|
|
10905
|
+
const coercedSource = semver.coerce(sourceVer);
|
|
10906
|
+
const coercedTarget = semver.coerce(targetVer);
|
|
10907
|
+
if (!coercedSource) return false;
|
|
10908
|
+
if (!coercedTarget) return true;
|
|
10909
|
+
return semver.lte(coercedSource, coercedTarget);
|
|
10910
|
+
} catch {
|
|
10911
|
+
return sourceVer <= targetVer;
|
|
10912
|
+
}
|
|
10913
|
+
};
|
|
10914
|
+
handle("migration:scan-stable", async (_, { send }) => {
|
|
10915
|
+
logger().info("[Migration] Scanning other channel database...");
|
|
10916
|
+
try {
|
|
10917
|
+
const isStable = context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta");
|
|
10918
|
+
const sourceEnv = isStable ? "beta" : "prod";
|
|
10919
|
+
const sourcePath = getDefaultUserDataPath(sourceEnv);
|
|
10920
|
+
if (sourcePath === context.userDataPath) {
|
|
10921
|
+
logger().info("[Migration] Running in same channel. Disabling migration scan.");
|
|
10922
|
+
return send({
|
|
10923
|
+
type: "end",
|
|
10924
|
+
data: {
|
|
10925
|
+
type: "success",
|
|
10926
|
+
result: {
|
|
10927
|
+
sourceChannel: isStable ? "Beta" : "Stable",
|
|
10928
|
+
targetChannel: isStable ? "Stable" : "Beta",
|
|
10929
|
+
settingsExists: false,
|
|
10930
|
+
settingsVersion: null,
|
|
10931
|
+
settingsVersionTarget: null,
|
|
10932
|
+
settingsMtimeSource: Date.now(),
|
|
10933
|
+
settingsMtimeTarget: Date.now(),
|
|
10934
|
+
settingsImportable: false,
|
|
10935
|
+
connectionsExists: false,
|
|
10936
|
+
connectionsCount: 0,
|
|
10937
|
+
connectionsVersion: null,
|
|
10938
|
+
connectionsVersionTarget: null,
|
|
10939
|
+
connectionsMtimeSource: Date.now(),
|
|
10940
|
+
connectionsMtimeTarget: Date.now(),
|
|
10941
|
+
connectionsImportable: false,
|
|
10942
|
+
projectsExists: false,
|
|
10943
|
+
projectsVersion: null,
|
|
10944
|
+
projectsVersionTarget: null,
|
|
10945
|
+
projectsMtimeSource: Date.now(),
|
|
10946
|
+
projectsMtimeTarget: Date.now(),
|
|
10947
|
+
projectsImportable: false,
|
|
10948
|
+
projects: []
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
});
|
|
10952
|
+
}
|
|
10953
|
+
const sourceContext = new PipelabContext({
|
|
10954
|
+
userDataPath: sourcePath,
|
|
10955
|
+
releaseTag: sourceEnv
|
|
10956
|
+
});
|
|
10957
|
+
const sourceSettingsPath = sourceContext.getSettingsPath();
|
|
10958
|
+
const targetSettingsPath = context.getSettingsPath();
|
|
10959
|
+
const sourceConnectionsPath = sourceContext.getConnectionsPath();
|
|
10960
|
+
const targetConnectionsPath = context.getConnectionsPath();
|
|
10961
|
+
const sourceProjectsPath = sourceContext.getProjectsPath();
|
|
10962
|
+
const targetProjectsPath = context.getProjectsPath();
|
|
10963
|
+
const sourceSettingsMeta = await getFileMetadata(sourceSettingsPath);
|
|
10964
|
+
const targetSettingsMeta = await getFileMetadata(targetSettingsPath);
|
|
10965
|
+
const sourceConnectionsMeta = await getFileMetadata(sourceConnectionsPath);
|
|
10966
|
+
const targetConnectionsMeta = await getFileMetadata(targetConnectionsPath);
|
|
10967
|
+
const sourceProjectsMeta = await getFileMetadata(sourceProjectsPath);
|
|
10968
|
+
const targetProjectsMeta = await getFileMetadata(targetProjectsPath);
|
|
10969
|
+
const settingsExists = sourceSettingsMeta.exists;
|
|
10970
|
+
const settingsVersion = sourceSettingsMeta.version;
|
|
10971
|
+
const settingsMtimeSource = sourceSettingsMeta.mtime;
|
|
10972
|
+
const settingsMtimeTarget = targetSettingsMeta.mtime;
|
|
10973
|
+
const settingsImportable = isVersionImportable(settingsVersion, targetSettingsMeta.version);
|
|
10974
|
+
const connectionsExists = sourceConnectionsMeta.exists;
|
|
10975
|
+
const connectionsVersion = sourceConnectionsMeta.version;
|
|
10976
|
+
const connectionsMtimeSource = sourceConnectionsMeta.mtime;
|
|
10977
|
+
const connectionsMtimeTarget = targetConnectionsMeta.mtime;
|
|
10978
|
+
const connectionsImportable = isVersionImportable(connectionsVersion, targetConnectionsMeta.version);
|
|
10979
|
+
const projectsExists = sourceProjectsMeta.exists;
|
|
10980
|
+
const projectsVersion = sourceProjectsMeta.version;
|
|
10981
|
+
const projectsMtimeSource = sourceProjectsMeta.mtime;
|
|
10982
|
+
const projectsMtimeTarget = targetProjectsMeta.mtime;
|
|
10983
|
+
const projectsImportable = isVersionImportable(projectsVersion, targetProjectsMeta.version);
|
|
10984
|
+
let connectionsCount = 0;
|
|
10985
|
+
try {
|
|
10986
|
+
if (sourceConnectionsMeta.exists) {
|
|
10987
|
+
const connContent = await fs$1.readFile(sourceConnectionsPath, "utf8");
|
|
10988
|
+
const connJson = JSON.parse(connContent);
|
|
10989
|
+
if (connJson && Array.isArray(connJson.connections)) connectionsCount = connJson.connections.length;
|
|
10990
|
+
}
|
|
10991
|
+
} catch (err) {
|
|
10992
|
+
logger().error("[Migration] Error parsing source connections.json:", err);
|
|
10993
|
+
}
|
|
10994
|
+
let sourceProjectsList = [];
|
|
10995
|
+
let sourcePipelinesList = [];
|
|
10996
|
+
try {
|
|
10997
|
+
if (sourceProjectsMeta.exists) {
|
|
10998
|
+
const projContent = await fs$1.readFile(sourceProjectsPath, "utf8");
|
|
10999
|
+
const projJson = JSON.parse(projContent);
|
|
11000
|
+
sourceProjectsList = projJson.projects || [];
|
|
11001
|
+
sourcePipelinesList = projJson.pipelines || [];
|
|
11002
|
+
}
|
|
11003
|
+
} catch (err) {
|
|
11004
|
+
logger().error("[Migration] Error parsing source projects.json:", err);
|
|
11005
|
+
}
|
|
11006
|
+
let targetPipelinesList = [];
|
|
11007
|
+
try {
|
|
11008
|
+
if (targetProjectsMeta.exists) {
|
|
11009
|
+
const targetProjContent = await fs$1.readFile(targetProjectsPath, "utf8");
|
|
11010
|
+
targetPipelinesList = JSON.parse(targetProjContent).pipelines || [];
|
|
11011
|
+
}
|
|
11012
|
+
} catch (err) {}
|
|
11013
|
+
const projectsReport = [];
|
|
11014
|
+
for (const proj of sourceProjectsList) {
|
|
11015
|
+
const projPipelines = sourcePipelinesList.filter((p) => p.project === proj.id);
|
|
11016
|
+
const pipelinesReport = [];
|
|
11017
|
+
for (const pipe of projPipelines) {
|
|
11018
|
+
const targetPipe = targetPipelinesList.find((bp) => bp.id === pipe.id);
|
|
11019
|
+
const existsInBeta = !!targetPipe;
|
|
11020
|
+
let pipeName = pipe.id;
|
|
11021
|
+
let pipeDesc = "";
|
|
11022
|
+
if (pipe.type === "internal") {
|
|
11023
|
+
const sourcePipeFile = sourceContext.getConfigPath(`${pipe.configName}.json`);
|
|
11024
|
+
try {
|
|
11025
|
+
if (existsSync(sourcePipeFile)) {
|
|
11026
|
+
const pipeContent = await fs$1.readFile(sourcePipeFile, "utf8");
|
|
11027
|
+
const pipeJson = JSON.parse(pipeContent);
|
|
11028
|
+
pipeName = pipeJson.name || pipeName;
|
|
11029
|
+
pipeDesc = pipeJson.description || pipeDesc;
|
|
11030
|
+
}
|
|
11031
|
+
} catch (err) {
|
|
11032
|
+
logger().error(`[Migration] Error reading source pipeline file ${sourcePipeFile}:`, err);
|
|
11033
|
+
}
|
|
11034
|
+
} else if (pipe.type === "external") {
|
|
11035
|
+
pipeName = pipe.summary?.name || pipeName;
|
|
11036
|
+
pipeDesc = pipe.summary?.description || pipeDesc;
|
|
11037
|
+
}
|
|
11038
|
+
pipelinesReport.push({
|
|
11039
|
+
id: pipe.id,
|
|
11040
|
+
name: pipeName,
|
|
11041
|
+
description: pipeDesc,
|
|
11042
|
+
type: pipe.type,
|
|
11043
|
+
lastModifiedStable: pipe.type !== "pipelab-cloud" ? pipe.lastModified : void 0,
|
|
11044
|
+
lastModifiedBeta: targetPipe && targetPipe.type !== "pipelab-cloud" ? targetPipe.lastModified : void 0,
|
|
11045
|
+
existsInBeta
|
|
11046
|
+
});
|
|
11047
|
+
}
|
|
11048
|
+
projectsReport.push({
|
|
11049
|
+
id: proj.id,
|
|
11050
|
+
name: proj.name,
|
|
11051
|
+
description: proj.description || "",
|
|
11052
|
+
pipelines: pipelinesReport
|
|
11053
|
+
});
|
|
11054
|
+
}
|
|
11055
|
+
send({
|
|
11056
|
+
type: "end",
|
|
11057
|
+
data: {
|
|
11058
|
+
type: "success",
|
|
11059
|
+
result: {
|
|
11060
|
+
sourceChannel: isStable ? "Beta" : "Stable",
|
|
11061
|
+
targetChannel: isStable ? "Stable" : "Beta",
|
|
11062
|
+
settingsExists,
|
|
11063
|
+
settingsVersion,
|
|
11064
|
+
settingsVersionTarget: targetSettingsMeta.version,
|
|
11065
|
+
settingsMtimeSource,
|
|
11066
|
+
settingsMtimeTarget,
|
|
11067
|
+
settingsImportable,
|
|
11068
|
+
connectionsExists,
|
|
11069
|
+
connectionsCount,
|
|
11070
|
+
connectionsVersion,
|
|
11071
|
+
connectionsVersionTarget: targetConnectionsMeta.version,
|
|
11072
|
+
connectionsMtimeSource,
|
|
11073
|
+
connectionsMtimeTarget,
|
|
11074
|
+
connectionsImportable,
|
|
11075
|
+
projectsExists,
|
|
11076
|
+
projectsVersion,
|
|
11077
|
+
projectsVersionTarget: targetProjectsMeta.version,
|
|
11078
|
+
projectsMtimeSource,
|
|
11079
|
+
projectsMtimeTarget,
|
|
11080
|
+
projectsImportable,
|
|
11081
|
+
projects: projectsReport
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
11084
|
+
});
|
|
11085
|
+
} catch (e) {
|
|
11086
|
+
logger().error("[Migration] Error scanning other channel folder:", e);
|
|
11087
|
+
send({
|
|
11088
|
+
type: "end",
|
|
11089
|
+
data: {
|
|
11090
|
+
type: "error",
|
|
11091
|
+
ipcError: e instanceof Error ? e.message : "Failed to scan other channel database"
|
|
11092
|
+
}
|
|
11093
|
+
});
|
|
11094
|
+
}
|
|
11095
|
+
});
|
|
11096
|
+
handle("migration:perform", async (_, { send, value }) => {
|
|
11097
|
+
logger().info("[Migration] Performing migration...");
|
|
11098
|
+
try {
|
|
11099
|
+
const { migrateSettings, migrateConnections, selectedProjects, selectedPipelines } = value;
|
|
11100
|
+
const sourceEnv = context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta") ? "beta" : "prod";
|
|
11101
|
+
const sourcePath = getDefaultUserDataPath(sourceEnv);
|
|
11102
|
+
const sourceContext = new PipelabContext({
|
|
11103
|
+
userDataPath: sourcePath,
|
|
11104
|
+
releaseTag: sourceEnv
|
|
11105
|
+
});
|
|
11106
|
+
sourceContext.getConfigPath();
|
|
11107
|
+
const targetConfigDir = context.getConfigPath();
|
|
11108
|
+
if (sourcePath === context.userDataPath) throw new Error("Cannot migrate data: Source and target directories are identical.");
|
|
11109
|
+
if (existsSync(targetConfigDir)) {
|
|
11110
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
11111
|
+
const backupDir = context.getConfigPath("backups", `backup_${timestamp}`);
|
|
11112
|
+
await fs$1.mkdir(backupDir, { recursive: true });
|
|
11113
|
+
const entries = await fs$1.readdir(targetConfigDir, { withFileTypes: true });
|
|
11114
|
+
for (const entry of entries) {
|
|
11115
|
+
if (entry.name === "backups") continue;
|
|
11116
|
+
const srcPath = join(targetConfigDir, entry.name);
|
|
11117
|
+
const destPath = join(backupDir, entry.name);
|
|
11118
|
+
try {
|
|
11119
|
+
await fs$1.cp(srcPath, destPath, { recursive: true });
|
|
11120
|
+
} catch (copyErr) {
|
|
11121
|
+
logger().error(`[Migration] Failed to backup ${entry.name}:`, copyErr);
|
|
11122
|
+
}
|
|
11123
|
+
}
|
|
11124
|
+
logger().info(`[Migration] Config backup successfully created at ${backupDir}`);
|
|
11125
|
+
}
|
|
11126
|
+
if (migrateSettings) {
|
|
11127
|
+
const sourceSettingsFile = sourceContext.getSettingsPath();
|
|
11128
|
+
const targetSettingsFile = context.getSettingsPath();
|
|
11129
|
+
const sourceMeta = await getFileMetadata(sourceSettingsFile);
|
|
11130
|
+
const targetMeta = await getFileMetadata(targetSettingsFile);
|
|
11131
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate settings: Source version is newer than current version.");
|
|
11132
|
+
if (existsSync(sourceSettingsFile)) {
|
|
11133
|
+
let stableSettings = {
|
|
11134
|
+
version: "7.0.0",
|
|
11135
|
+
locale: "en-US",
|
|
11136
|
+
theme: "light",
|
|
11137
|
+
autosave: true,
|
|
11138
|
+
agents: [],
|
|
11139
|
+
plugins: [],
|
|
11140
|
+
tours: {
|
|
11141
|
+
dashboard: {
|
|
11142
|
+
step: 0,
|
|
11143
|
+
completed: false
|
|
11144
|
+
},
|
|
11145
|
+
editor: {
|
|
11146
|
+
step: 0,
|
|
11147
|
+
completed: false
|
|
11148
|
+
}
|
|
11149
|
+
}
|
|
11150
|
+
};
|
|
11151
|
+
try {
|
|
11152
|
+
const stableContent = await fs$1.readFile(sourceSettingsFile, "utf8");
|
|
11153
|
+
stableSettings = JSON.parse(stableContent);
|
|
11154
|
+
} catch (e) {
|
|
11155
|
+
logger().error("[Migration] Failed to read source settings.json:", e);
|
|
11156
|
+
}
|
|
11157
|
+
let betaSettings = {
|
|
11158
|
+
version: "7.0.0",
|
|
11159
|
+
locale: "en-US",
|
|
11160
|
+
theme: "light",
|
|
11161
|
+
autosave: true,
|
|
11162
|
+
agents: [],
|
|
11163
|
+
plugins: [],
|
|
11164
|
+
tours: {
|
|
11165
|
+
dashboard: {
|
|
11166
|
+
step: 0,
|
|
11167
|
+
completed: false
|
|
11168
|
+
},
|
|
11169
|
+
editor: {
|
|
11170
|
+
step: 0,
|
|
11171
|
+
completed: false
|
|
11172
|
+
}
|
|
11173
|
+
}
|
|
11174
|
+
};
|
|
11175
|
+
if (existsSync(targetSettingsFile)) try {
|
|
11176
|
+
const betaContent = await fs$1.readFile(targetSettingsFile, "utf8");
|
|
11177
|
+
betaSettings = JSON.parse(betaContent);
|
|
11178
|
+
} catch (e) {}
|
|
11179
|
+
betaSettings.theme = stableSettings.theme ?? betaSettings.theme;
|
|
11180
|
+
betaSettings.locale = stableSettings.locale ?? betaSettings.locale;
|
|
11181
|
+
betaSettings.autosave = stableSettings.autosave ?? betaSettings.autosave;
|
|
11182
|
+
if (stableSettings.tours) {
|
|
11183
|
+
betaSettings.tours = betaSettings.tours || {
|
|
11184
|
+
dashboard: {
|
|
11185
|
+
step: 0,
|
|
11186
|
+
completed: false
|
|
11187
|
+
},
|
|
11188
|
+
editor: {
|
|
11189
|
+
step: 0,
|
|
11190
|
+
completed: false
|
|
11191
|
+
}
|
|
11192
|
+
};
|
|
11193
|
+
if (stableSettings.tours.dashboard) betaSettings.tours.dashboard = stableSettings.tours.dashboard;
|
|
11194
|
+
if (stableSettings.tours.editor) betaSettings.tours.editor = stableSettings.tours.editor;
|
|
11195
|
+
}
|
|
11196
|
+
const betaAgents = betaSettings.agents || [];
|
|
11197
|
+
const stableAgents = stableSettings.agents || [];
|
|
11198
|
+
for (const sAgent of stableAgents) {
|
|
11199
|
+
const existingIdx = betaAgents.findIndex((a) => a.id === sAgent.id);
|
|
11200
|
+
if (existingIdx >= 0) betaAgents[existingIdx] = { ...sAgent };
|
|
11201
|
+
else betaAgents.push({ ...sAgent });
|
|
11202
|
+
}
|
|
11203
|
+
betaSettings.agents = betaAgents;
|
|
11204
|
+
const betaPlugins = betaSettings.plugins || [];
|
|
11205
|
+
const stablePlugins = stableSettings.plugins || [];
|
|
11206
|
+
for (const sPlugin of stablePlugins) {
|
|
11207
|
+
const existingIdx = betaPlugins.findIndex((p) => p.name === sPlugin.name);
|
|
11208
|
+
if (existingIdx >= 0) betaPlugins[existingIdx] = { ...sPlugin };
|
|
11209
|
+
else betaPlugins.push({ ...sPlugin });
|
|
11210
|
+
}
|
|
11211
|
+
betaSettings.plugins = betaPlugins;
|
|
11212
|
+
await fs$1.mkdir(dirname(targetSettingsFile), { recursive: true });
|
|
11213
|
+
await fs$1.writeFile(targetSettingsFile, JSON.stringify(betaSettings, null, 2));
|
|
11214
|
+
logger().info("[Migration] Settings merged successfully");
|
|
11215
|
+
}
|
|
11216
|
+
}
|
|
11217
|
+
if (migrateConnections) {
|
|
11218
|
+
const sourceConnectionsFile = sourceContext.getConnectionsPath();
|
|
11219
|
+
const targetConnectionsFile = context.getConnectionsPath();
|
|
11220
|
+
const sourceMeta = await getFileMetadata(sourceConnectionsFile);
|
|
11221
|
+
const targetMeta = await getFileMetadata(targetConnectionsFile);
|
|
11222
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate connections: Source version is newer than current version.");
|
|
11223
|
+
if (existsSync(sourceConnectionsFile)) {
|
|
11224
|
+
let stableConnections = {
|
|
11225
|
+
version: "1.0.0",
|
|
11226
|
+
connections: []
|
|
11227
|
+
};
|
|
11228
|
+
try {
|
|
11229
|
+
const stableContent = await fs$1.readFile(sourceConnectionsFile, "utf8");
|
|
11230
|
+
stableConnections = JSON.parse(stableContent);
|
|
11231
|
+
} catch (e) {
|
|
11232
|
+
logger().error("[Migration] Failed to read source connections.json:", e);
|
|
11233
|
+
}
|
|
11234
|
+
let betaConnections = {
|
|
11235
|
+
version: "1.0.0",
|
|
11236
|
+
connections: []
|
|
11237
|
+
};
|
|
11238
|
+
if (existsSync(targetConnectionsFile)) try {
|
|
11239
|
+
const betaContent = await fs$1.readFile(targetConnectionsFile, "utf8");
|
|
11240
|
+
betaConnections = JSON.parse(betaContent);
|
|
11241
|
+
} catch (e) {}
|
|
11242
|
+
betaConnections.connections = betaConnections.connections || [];
|
|
11243
|
+
stableConnections.connections = stableConnections.connections || [];
|
|
11244
|
+
for (const sConn of stableConnections.connections) {
|
|
11245
|
+
const existingIdx = betaConnections.connections.findIndex((c) => c.id === sConn.id);
|
|
11246
|
+
if (existingIdx >= 0) betaConnections.connections[existingIdx] = { ...sConn };
|
|
11247
|
+
else betaConnections.connections.push({ ...sConn });
|
|
11248
|
+
}
|
|
11249
|
+
await fs$1.mkdir(dirname(targetConnectionsFile), { recursive: true });
|
|
11250
|
+
await fs$1.writeFile(targetConnectionsFile, JSON.stringify(betaConnections, null, 2));
|
|
11251
|
+
logger().info("[Migration] Connections merged successfully");
|
|
11252
|
+
}
|
|
11253
|
+
}
|
|
11254
|
+
if (selectedPipelines.length > 0 || selectedProjects.length > 0) {
|
|
11255
|
+
const sourceProjectsFile = sourceContext.getProjectsPath();
|
|
11256
|
+
const targetProjectsFile = context.getProjectsPath();
|
|
11257
|
+
const sourceMeta = await getFileMetadata(sourceProjectsFile);
|
|
11258
|
+
const targetMeta = await getFileMetadata(targetProjectsFile);
|
|
11259
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate projects: Source version is newer than current version.");
|
|
11260
|
+
let stableFileRepo = {
|
|
11261
|
+
version: "3.0.0",
|
|
11262
|
+
projects: [],
|
|
11263
|
+
pipelines: []
|
|
11264
|
+
};
|
|
11265
|
+
if (existsSync(sourceProjectsFile)) {
|
|
11266
|
+
const stableContent = await fs$1.readFile(sourceProjectsFile, "utf8");
|
|
11267
|
+
stableFileRepo = JSON.parse(stableContent);
|
|
11268
|
+
}
|
|
11269
|
+
let betaFileRepo = {
|
|
11270
|
+
version: "3.0.0",
|
|
11271
|
+
projects: [],
|
|
11272
|
+
pipelines: []
|
|
11273
|
+
};
|
|
11274
|
+
if (existsSync(targetProjectsFile)) try {
|
|
11275
|
+
const betaContent = await fs$1.readFile(targetProjectsFile, "utf8");
|
|
11276
|
+
betaFileRepo = JSON.parse(betaContent);
|
|
11277
|
+
} catch (readErr) {
|
|
11278
|
+
logger().warn("[Migration] Failed to parse existing target projects.json, starting fresh:", readErr);
|
|
11279
|
+
}
|
|
11280
|
+
betaFileRepo.projects = betaFileRepo.projects || [];
|
|
11281
|
+
betaFileRepo.pipelines = betaFileRepo.pipelines || [];
|
|
11282
|
+
for (const projId of selectedProjects) {
|
|
11283
|
+
const stableProj = stableFileRepo.projects.find((p) => p.id === projId);
|
|
11284
|
+
if (stableProj) {
|
|
11285
|
+
const existingProjIdx = betaFileRepo.projects.findIndex((p) => p.id === projId);
|
|
11286
|
+
if (existingProjIdx >= 0) betaFileRepo.projects[existingProjIdx] = { ...stableProj };
|
|
11287
|
+
else betaFileRepo.projects.push({ ...stableProj });
|
|
11288
|
+
}
|
|
11289
|
+
}
|
|
11290
|
+
for (const pipeId of selectedPipelines) {
|
|
11291
|
+
const stablePipe = (stableFileRepo.pipelines || []).find((p) => p.id === pipeId);
|
|
11292
|
+
if (stablePipe) {
|
|
11293
|
+
const parentProjId = stablePipe.project;
|
|
11294
|
+
if (!betaFileRepo.projects.some((p) => p.id === parentProjId)) {
|
|
11295
|
+
const stableProj = stableFileRepo.projects.find((p) => p.id === parentProjId);
|
|
11296
|
+
if (stableProj) betaFileRepo.projects.push({ ...stableProj });
|
|
11297
|
+
}
|
|
11298
|
+
if (stablePipe.type === "internal") {
|
|
11299
|
+
const stablePipeFile = sourceContext.getConfigPath(`${stablePipe.configName}.json`);
|
|
11300
|
+
const betaPipeFile = context.getConfigPath(`${stablePipe.configName}.json`);
|
|
11301
|
+
if (existsSync(stablePipeFile)) {
|
|
11302
|
+
await fs$1.mkdir(dirname(betaPipeFile), { recursive: true });
|
|
11303
|
+
await fs$1.copyFile(stablePipeFile, betaPipeFile);
|
|
11304
|
+
logger().info(`[Migration] Copied pipeline file: ${stablePipe.configName}`);
|
|
11305
|
+
}
|
|
11306
|
+
}
|
|
11307
|
+
const updatedPipe = { ...stablePipe };
|
|
11308
|
+
if (updatedPipe.type !== "pipelab-cloud") updatedPipe.lastModified = (/* @__PURE__ */ new Date()).toISOString();
|
|
11309
|
+
const existingPipeIdx = betaFileRepo.pipelines.findIndex((p) => p.id === pipeId);
|
|
11310
|
+
if (existingPipeIdx >= 0) betaFileRepo.pipelines[existingPipeIdx] = updatedPipe;
|
|
11311
|
+
else betaFileRepo.pipelines.push(updatedPipe);
|
|
11312
|
+
}
|
|
11313
|
+
}
|
|
11314
|
+
await fs$1.mkdir(dirname(targetProjectsFile), { recursive: true });
|
|
11315
|
+
await fs$1.writeFile(targetProjectsFile, JSON.stringify(betaFileRepo, null, 2));
|
|
11316
|
+
logger().info("[Migration] Merged and saved projects.json successfully");
|
|
11317
|
+
}
|
|
11318
|
+
send({
|
|
11319
|
+
type: "end",
|
|
11320
|
+
data: {
|
|
11321
|
+
type: "success",
|
|
11322
|
+
result: { result: "ok" }
|
|
11323
|
+
}
|
|
11324
|
+
});
|
|
11325
|
+
} catch (e) {
|
|
11326
|
+
logger().error("[Migration] Error performing migration:", e);
|
|
11327
|
+
send({
|
|
11328
|
+
type: "end",
|
|
11329
|
+
data: {
|
|
11330
|
+
type: "error",
|
|
11331
|
+
ipcError: e instanceof Error ? e.message : "Failed to migrate selected data"
|
|
11332
|
+
}
|
|
11333
|
+
});
|
|
11334
|
+
}
|
|
11335
|
+
});
|
|
11336
|
+
};
|
|
11337
|
+
//#endregion
|
|
10889
11338
|
//#region src/handlers/index.ts
|
|
10890
11339
|
const registerAllHandlers = async (options) => {
|
|
10891
11340
|
const context = options.context;
|
|
@@ -10898,6 +11347,7 @@ const registerAllHandlers = async (options) => {
|
|
|
10898
11347
|
registerAuthHandlers(context);
|
|
10899
11348
|
registerSystemHandlers(options);
|
|
10900
11349
|
registerPluginsHandlers(context);
|
|
11350
|
+
registerMigrationHandlers(context);
|
|
10901
11351
|
const { registerPlugins } = usePlugins();
|
|
10902
11352
|
builtInPlugins({ context });
|
|
10903
11353
|
};
|
|
@@ -53170,9 +53620,7 @@ async function runPipelineCommand(file, options, version) {
|
|
|
53170
53620
|
version,
|
|
53171
53621
|
context
|
|
53172
53622
|
});
|
|
53173
|
-
|
|
53174
|
-
await (await setupConfigFile("settings", { context })).getConfig();
|
|
53175
|
-
const cachePath = join(context.userDataPath, "cache");
|
|
53623
|
+
const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
|
|
53176
53624
|
await mkdir(cachePath, { recursive: true });
|
|
53177
53625
|
const abortController = new AbortController();
|
|
53178
53626
|
let supabase;
|
|
@@ -53344,6 +53792,6 @@ async function fetchLatestDesktopRelease(options = {}) {
|
|
|
53344
53792
|
return latest;
|
|
53345
53793
|
}
|
|
53346
53794
|
//#endregion
|
|
53347
|
-
export { BuildHistoryStorage, PipelabContext, WebSocketServer, builtInPlugins, downloadFile, ensure, ensureNodeJS, ensurePNPM, executeGraphWithHistory, extractTarGz, extractZip, fetchLatestDesktopRelease, fetchLatestPackageRelease, fetchPackage, fetchPackageReleases, fetchPipelabAsset, fetchPipelabCli, fetchPipelabPlugin, findInstalledPlugins, getDefaultUserDataPath, getFinalPlugins, getFolderSize,
|
|
53795
|
+
export { BuildHistoryStorage, CacheFolder, PipelabContext, WebSocketServer, builtInPlugins, deletePipelineConfigFileByName, deletePipelineConfigFileByPath, downloadFile, ensure, ensureNodeJS, ensurePNPM, executeGraphWithHistory, extractTarGz, extractZip, fetchLatestDesktopRelease, fetchLatestPackageRelease, fetchPackage, fetchPackageReleases, fetchPipelabAsset, fetchPipelabCli, fetchPipelabPlugin, findInstalledPlugins, getDefaultUserDataPath, getFinalPlugins, getFolderSize, handleActionExecute, isDev, isOnline, loadCustomPlugin, loadPipelabPlugin, projectRoot, registerAgentsHandlers, registerAllHandlers, registerConfigHandlers, registerEngineHandlers, registerFsHandlers, registerHistoryHandlers, registerShellHandlers, runPipelineCommand, runPnpm, runWithLiveLogs, sendStartupProgress, sendStartupReady, serveCommand, setupConnectionsConfigFile, setupPipelineConfigFileByName, setupPipelineConfigFileByPath, setupProjectsConfigFile, setupSettingsConfigFile, useAPI, usePluginAPI, webSocketServer, zipFolder };
|
|
53348
53796
|
|
|
53349
53797
|
//# sourceMappingURL=index.mjs.map
|