@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipelab/core-node",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.19",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Pipelab automation engine for Node.js",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"type-fest": "4.39.0",
|
|
41
41
|
"ws": "8.18.3",
|
|
42
42
|
"yauzl": "2.10.0",
|
|
43
|
-
"@pipelab/constants": "1.0.0-beta.
|
|
44
|
-
"@pipelab/shared": "1.0.0-beta.
|
|
43
|
+
"@pipelab/constants": "1.0.0-beta.17",
|
|
44
|
+
"@pipelab/shared": "1.0.0-beta.15"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/adm-zip": "0.5.7",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"tsdown": "0.21.2",
|
|
57
57
|
"typescript": "^5.0.0",
|
|
58
58
|
"vitest": "3.1.4",
|
|
59
|
-
"@pipelab/tsconfig": "1.0.0-beta.
|
|
59
|
+
"@pipelab/tsconfig": "1.0.0-beta.15"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsdown",
|
package/src/config.test.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, vi } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
setupConfigFile,
|
|
4
|
+
setupProjectsConfigFile,
|
|
5
|
+
deletePipelineConfigFileByName,
|
|
6
|
+
deletePipelineConfigFileByPath,
|
|
7
|
+
} from "./config";
|
|
3
8
|
import { FileRepo } from "@pipelab/shared";
|
|
4
9
|
import { PipelabContext } from "./context";
|
|
5
10
|
import fs from "node:fs/promises";
|
|
@@ -38,9 +43,9 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
38
43
|
// 1. Setup V1 configuration file in the context's config path
|
|
39
44
|
const configDir = context.getConfigPath();
|
|
40
45
|
await fs.mkdir(configDir, { recursive: true });
|
|
41
|
-
|
|
42
|
-
const projectsFilePath =
|
|
43
|
-
|
|
46
|
+
|
|
47
|
+
const projectsFilePath = context.getProjectsPath();
|
|
48
|
+
|
|
44
49
|
const v1Config = {
|
|
45
50
|
version: "1.0.0",
|
|
46
51
|
data: {
|
|
@@ -49,14 +54,14 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
49
54
|
type: "internal",
|
|
50
55
|
configName: "pipeline-1",
|
|
51
56
|
lastModified: "2026-06-04",
|
|
52
|
-
}
|
|
53
|
-
}
|
|
57
|
+
},
|
|
58
|
+
},
|
|
54
59
|
};
|
|
55
|
-
|
|
60
|
+
|
|
56
61
|
await fs.writeFile(projectsFilePath, JSON.stringify(v1Config));
|
|
57
62
|
|
|
58
63
|
// 2. Initialize setupConfigFile
|
|
59
|
-
const configInstance = await
|
|
64
|
+
const configInstance = await setupProjectsConfigFile(context);
|
|
60
65
|
|
|
61
66
|
// Verify file exists
|
|
62
67
|
expect(existsSync(projectsFilePath)).toBe(true);
|
|
@@ -64,11 +69,11 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
64
69
|
// 3. Retrieve config (this triggers the migration process and onStep callback)
|
|
65
70
|
const migratedConfig = await configInstance.getConfig();
|
|
66
71
|
|
|
67
|
-
// 4. Assert the main config is updated on disk to version
|
|
68
|
-
expect(migratedConfig.version).toBe("
|
|
69
|
-
|
|
72
|
+
// 4. Assert the main config is updated on disk to version 3.0.0
|
|
73
|
+
expect(migratedConfig.version).toBe("3.0.0");
|
|
74
|
+
|
|
70
75
|
const updatedContent = JSON.parse(await fs.readFile(projectsFilePath, "utf8"));
|
|
71
|
-
expect(updatedContent.version).toBe("
|
|
76
|
+
expect(updatedContent.version).toBe("3.0.0");
|
|
72
77
|
|
|
73
78
|
// 5. Assert that the intermediate backup file was created on disk
|
|
74
79
|
const backupFilePath = path.join(configDir, "projects.v2.0.0.json");
|
|
@@ -82,17 +87,17 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
82
87
|
|
|
83
88
|
test("should create default config file if missing on setup", async () => {
|
|
84
89
|
const configDir = context.getConfigPath();
|
|
85
|
-
const projectsFilePath =
|
|
90
|
+
const projectsFilePath = context.getProjectsPath();
|
|
86
91
|
|
|
87
92
|
expect(existsSync(projectsFilePath)).toBe(false);
|
|
88
93
|
|
|
89
94
|
// Initialize setupConfigFile
|
|
90
|
-
await
|
|
95
|
+
await setupProjectsConfigFile(context);
|
|
91
96
|
|
|
92
97
|
// Should create file with default value
|
|
93
98
|
expect(existsSync(projectsFilePath)).toBe(true);
|
|
94
99
|
const content = JSON.parse(await fs.readFile(projectsFilePath, "utf8"));
|
|
95
|
-
expect(content.version).toBe("
|
|
100
|
+
expect(content.version).toBe("3.0.0");
|
|
96
101
|
expect(content.projects).toHaveLength(1);
|
|
97
102
|
expect(content.pipelines).toEqual([]);
|
|
98
103
|
});
|
|
@@ -100,20 +105,22 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
100
105
|
test("should fallback to default config and preserve corrupted file if parsing fails", async () => {
|
|
101
106
|
const configDir = context.getConfigPath();
|
|
102
107
|
await fs.mkdir(configDir, { recursive: true });
|
|
103
|
-
const projectsFilePath =
|
|
108
|
+
const projectsFilePath = context.getProjectsPath();
|
|
104
109
|
|
|
105
110
|
await fs.writeFile(projectsFilePath, "{ corrupted json... }");
|
|
106
111
|
|
|
107
|
-
const configInstance = await
|
|
112
|
+
const configInstance = await setupProjectsConfigFile(context);
|
|
108
113
|
const config = await configInstance.getConfig();
|
|
109
114
|
|
|
110
|
-
expect(config.version).toBe("
|
|
115
|
+
expect(config.version).toBe("3.0.0");
|
|
111
116
|
expect(config.projects).toHaveLength(1);
|
|
112
117
|
expect(config.pipelines).toEqual([]);
|
|
113
118
|
|
|
114
119
|
// Verify a timestamped corrupted backup file exists
|
|
115
120
|
const files = await fs.readdir(configDir);
|
|
116
|
-
const corruptedFile = files.find(
|
|
121
|
+
const corruptedFile = files.find(
|
|
122
|
+
(f) => f.startsWith("projects.corrupted.") && f.endsWith(".json"),
|
|
123
|
+
);
|
|
117
124
|
expect(corruptedFile).toBeDefined();
|
|
118
125
|
const corruptedContent = await fs.readFile(path.join(configDir, corruptedFile!), "utf8");
|
|
119
126
|
expect(corruptedContent).toBe("{ corrupted json... }");
|
|
@@ -122,7 +129,7 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
122
129
|
test("should fallback to default config and preserve file if migration throws", async () => {
|
|
123
130
|
const configDir = context.getConfigPath();
|
|
124
131
|
await fs.mkdir(configDir, { recursive: true });
|
|
125
|
-
const projectsFilePath =
|
|
132
|
+
const projectsFilePath = context.getProjectsPath();
|
|
126
133
|
|
|
127
134
|
const customMigrator = {
|
|
128
135
|
defaultValue: { version: "2.0.0", projects: [], pipelines: [] } as any,
|
|
@@ -133,7 +140,7 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
133
140
|
|
|
134
141
|
await fs.writeFile(projectsFilePath, JSON.stringify({ version: "1.0.0", invalidData: true }));
|
|
135
142
|
|
|
136
|
-
const configInstance = await setupConfigFile<any>(
|
|
143
|
+
const configInstance = await setupConfigFile<any>(context.getProjectsPath(), {
|
|
137
144
|
context,
|
|
138
145
|
migrator: customMigrator,
|
|
139
146
|
});
|
|
@@ -143,14 +150,18 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
143
150
|
|
|
144
151
|
// Verify a timestamped corrupted backup file exists
|
|
145
152
|
const files = await fs.readdir(configDir);
|
|
146
|
-
const corruptedFile = files.find(
|
|
153
|
+
const corruptedFile = files.find(
|
|
154
|
+
(f) => f.startsWith("projects.corrupted.") && f.endsWith(".json"),
|
|
155
|
+
);
|
|
147
156
|
expect(corruptedFile).toBeDefined();
|
|
148
|
-
const corruptedContent = JSON.parse(
|
|
157
|
+
const corruptedContent = JSON.parse(
|
|
158
|
+
await fs.readFile(path.join(configDir, corruptedFile!), "utf8"),
|
|
159
|
+
);
|
|
149
160
|
expect(corruptedContent.invalidData).toBe(true);
|
|
150
161
|
});
|
|
151
162
|
|
|
152
163
|
test("should save config to disk via setConfig", async () => {
|
|
153
|
-
const configInstance = await
|
|
164
|
+
const configInstance = await setupProjectsConfigFile(context);
|
|
154
165
|
const initialConfig = await configInstance.getConfig();
|
|
155
166
|
|
|
156
167
|
const newConfig: FileRepo = {
|
|
@@ -169,8 +180,53 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
169
180
|
const success = await configInstance.setConfig(newConfig);
|
|
170
181
|
expect(success).toBe(true);
|
|
171
182
|
|
|
172
|
-
const savedContent = JSON.parse(
|
|
183
|
+
const savedContent = JSON.parse(
|
|
184
|
+
await fs.readFile(context.getProjectsPath(), "utf8"),
|
|
185
|
+
);
|
|
173
186
|
expect(savedContent.pipelines).toHaveLength(1);
|
|
174
187
|
expect(savedContent.pipelines[0].id).toBe("pipeline-new");
|
|
175
188
|
});
|
|
176
189
|
});
|
|
190
|
+
|
|
191
|
+
describe("deletePipelineConfigFile", () => {
|
|
192
|
+
let tempDir: string;
|
|
193
|
+
let context: PipelabContext;
|
|
194
|
+
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
vol.reset();
|
|
197
|
+
tempDir = "/tmp/pipelab-test-config";
|
|
198
|
+
context = new PipelabContext({ userDataPath: tempDir });
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("should delete pipeline config file using relative name", async () => {
|
|
202
|
+
const configDir = context.getConfigPath();
|
|
203
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
204
|
+
|
|
205
|
+
const pipelineFilePath = path.join(configDir, "my-pipeline.json");
|
|
206
|
+
await fs.writeFile(pipelineFilePath, JSON.stringify({ name: "My Pipeline" }));
|
|
207
|
+
expect(existsSync(pipelineFilePath)).toBe(true);
|
|
208
|
+
|
|
209
|
+
await deletePipelineConfigFileByName("my-pipeline", context);
|
|
210
|
+
expect(existsSync(pipelineFilePath)).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("should delete pipeline config file using absolute path", async () => {
|
|
214
|
+
const configDir = context.getConfigPath();
|
|
215
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
216
|
+
|
|
217
|
+
const pipelineFilePath = path.join(configDir, "another-pipeline.json");
|
|
218
|
+
await fs.writeFile(pipelineFilePath, JSON.stringify({ name: "Another Pipeline" }));
|
|
219
|
+
expect(existsSync(pipelineFilePath)).toBe(true);
|
|
220
|
+
|
|
221
|
+
await deletePipelineConfigFileByPath(pipelineFilePath, context);
|
|
222
|
+
expect(existsSync(pipelineFilePath)).toBe(false);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("should not throw error when attempting to delete non-existent pipeline file", async () => {
|
|
226
|
+
const nonExistentPath = context.getConfigPath("does-not-exist.json");
|
|
227
|
+
expect(existsSync(nonExistentPath)).toBe(false);
|
|
228
|
+
|
|
229
|
+
await expect(deletePipelineConfigFileByName("does-not-exist", context)).resolves.not.toThrow();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
package/src/config.ts
CHANGED
|
@@ -2,28 +2,26 @@ import { PipelabContext } from "./context";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { ensure } from "./utils/fs-extras";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import {
|
|
6
|
+
useLogger,
|
|
7
|
+
Migrator,
|
|
8
|
+
AppConfig,
|
|
9
|
+
ConnectionsConfig,
|
|
10
|
+
FileRepo,
|
|
11
|
+
SavedFile,
|
|
12
|
+
appSettingsMigrator,
|
|
13
|
+
connectionsMigrator,
|
|
14
|
+
fileRepoMigrations,
|
|
15
|
+
savedFileMigrator,
|
|
16
|
+
} from "@pipelab/shared";
|
|
11
17
|
|
|
12
18
|
export const setupConfigFile = async <T>(
|
|
13
|
-
|
|
14
|
-
options: { context: PipelabContext; migrator
|
|
19
|
+
filesPath: string,
|
|
20
|
+
options: { context: PipelabContext; migrator: Migrator<T> },
|
|
15
21
|
) => {
|
|
16
22
|
const ctx = options.context;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
if (!migrator) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
`No migrator found for configuration: ${name}. All managed files must have a migration schema.`,
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const isAbsolutePath = path.isAbsolute(name);
|
|
26
|
-
const filesPath = isAbsolutePath ? name : ctx.getConfigPath(`${name}.json`);
|
|
23
|
+
const parsedPath = path.parse(filesPath);
|
|
24
|
+
const migrator = options.migrator;
|
|
27
25
|
|
|
28
26
|
await ensure(filesPath, JSON.stringify(migrator.defaultValue));
|
|
29
27
|
|
|
@@ -34,7 +32,7 @@ export const setupConfigFile = async <T>(
|
|
|
34
32
|
await fs.writeFile(filesPath, JSON.stringify(config));
|
|
35
33
|
return true;
|
|
36
34
|
} catch (e) {
|
|
37
|
-
logger().error(`Error saving config ${name}:`, e);
|
|
35
|
+
logger().error(`Error saving config ${parsedPath.name}:`, e);
|
|
38
36
|
return false;
|
|
39
37
|
}
|
|
40
38
|
},
|
|
@@ -50,7 +48,7 @@ export const setupConfigFile = async <T>(
|
|
|
50
48
|
originalJson = JSON.parse(content);
|
|
51
49
|
}
|
|
52
50
|
} catch (e) {
|
|
53
|
-
logger().error(`Error reading or parsing config ${name}:`, e);
|
|
51
|
+
logger().error(`Error reading or parsing config ${parsedPath.name}:`, e);
|
|
54
52
|
parseFailed = true;
|
|
55
53
|
}
|
|
56
54
|
|
|
@@ -61,13 +59,15 @@ export const setupConfigFile = async <T>(
|
|
|
61
59
|
json = await migrator.migrate(originalJson, {
|
|
62
60
|
debug: false,
|
|
63
61
|
onStep: async (state: any, version: string) => {
|
|
64
|
-
const parsedPath = path.parse(filesPath);
|
|
65
62
|
const versionedPath = path.join(parsedPath.dir, `${parsedPath.name}.v${version}.json`);
|
|
66
63
|
try {
|
|
67
64
|
await fs.writeFile(versionedPath, JSON.stringify(state));
|
|
68
|
-
logger().info(`Intermediate backup created for ${name} at ${versionedPath}`);
|
|
65
|
+
logger().info(`Intermediate backup created for ${parsedPath.name} at ${versionedPath}`);
|
|
69
66
|
} catch (e) {
|
|
70
|
-
logger().error(
|
|
67
|
+
logger().error(
|
|
68
|
+
`Failed to create intermediate backup for ${parsedPath.name} at v${version}:`,
|
|
69
|
+
e,
|
|
70
|
+
);
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
});
|
|
@@ -75,52 +75,39 @@ export const setupConfigFile = async <T>(
|
|
|
75
75
|
json = migrator.defaultValue;
|
|
76
76
|
}
|
|
77
77
|
} catch (e) {
|
|
78
|
-
logger().error(`Error migrating config ${name}:`, e);
|
|
78
|
+
logger().error(`Error migrating config ${parsedPath.name}:`, e);
|
|
79
79
|
migrationFailed = true;
|
|
80
80
|
json = migrator.defaultValue;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
let normalized = false;
|
|
84
|
-
const isPipeline =
|
|
85
|
-
name.startsWith("pipeline-") ||
|
|
86
|
-
path.isAbsolute(name) ||
|
|
87
|
-
name.endsWith(".json") ||
|
|
88
|
-
name === "pipeline";
|
|
89
|
-
if (isPipeline) {
|
|
90
|
-
normalized = normalizePipelineConfig(json);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
83
|
const originalVersion = originalJson?.version;
|
|
94
84
|
const newVersion = json?.version;
|
|
95
85
|
|
|
96
86
|
const shouldSaveBack =
|
|
97
|
-
originalVersion !== newVersion ||
|
|
98
|
-
normalized ||
|
|
99
|
-
content === undefined ||
|
|
100
|
-
parseFailed ||
|
|
101
|
-
migrationFailed;
|
|
87
|
+
originalVersion !== newVersion || content === undefined || parseFailed || migrationFailed;
|
|
102
88
|
|
|
103
89
|
if (shouldSaveBack) {
|
|
104
90
|
if (parseFailed || migrationFailed) {
|
|
105
91
|
try {
|
|
106
|
-
const parsedPath = path.parse(filesPath);
|
|
107
92
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
108
93
|
const corruptedPath = path.join(
|
|
109
94
|
parsedPath.dir,
|
|
110
95
|
`${parsedPath.name}.corrupted.${timestamp}.json`,
|
|
111
96
|
);
|
|
112
|
-
const backupContent = parseFailed
|
|
97
|
+
const backupContent = parseFailed
|
|
98
|
+
? content || ""
|
|
99
|
+
: JSON.stringify(originalJson, null, 2);
|
|
113
100
|
await fs.writeFile(corruptedPath, backupContent);
|
|
114
101
|
logger().info(`Corrupted config file preserved at ${corruptedPath}`);
|
|
115
102
|
} catch (e) {
|
|
116
|
-
logger().error(`Failed to backup corrupted config ${name}:`, e);
|
|
103
|
+
logger().error(`Failed to backup corrupted config ${parsedPath.name}:`, e);
|
|
117
104
|
}
|
|
118
105
|
}
|
|
119
106
|
|
|
120
107
|
try {
|
|
121
108
|
await fs.writeFile(filesPath, JSON.stringify(json));
|
|
122
109
|
} catch (e) {
|
|
123
|
-
logger().error(`Error saving migrated config ${name}:`, e);
|
|
110
|
+
logger().error(`Error saving migrated config ${parsedPath.name}:`, e);
|
|
124
111
|
}
|
|
125
112
|
}
|
|
126
113
|
|
|
@@ -128,3 +115,52 @@ export const setupConfigFile = async <T>(
|
|
|
128
115
|
},
|
|
129
116
|
};
|
|
130
117
|
};
|
|
118
|
+
|
|
119
|
+
export const setupSettingsConfigFile = (context: PipelabContext) => {
|
|
120
|
+
return setupConfigFile<AppConfig>(context.getSettingsPath(), {
|
|
121
|
+
context,
|
|
122
|
+
migrator: appSettingsMigrator,
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const setupConnectionsConfigFile = (context: PipelabContext) => {
|
|
127
|
+
return setupConfigFile<ConnectionsConfig>(context.getConnectionsPath(), {
|
|
128
|
+
context,
|
|
129
|
+
migrator: connectionsMigrator,
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const setupProjectsConfigFile = (context: PipelabContext) => {
|
|
134
|
+
return setupConfigFile<FileRepo>(context.getProjectsPath(), {
|
|
135
|
+
context,
|
|
136
|
+
migrator: fileRepoMigrations,
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const setupPipelineConfigFileByName = (name: string, context: PipelabContext) => {
|
|
141
|
+
const filesPath = context.getConfigPath(`${name}.json`);
|
|
142
|
+
return setupConfigFile<SavedFile>(filesPath, {
|
|
143
|
+
context,
|
|
144
|
+
migrator: savedFileMigrator,
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const setupPipelineConfigFileByPath = (absolutePath: string, context: PipelabContext) => {
|
|
149
|
+
return setupConfigFile<SavedFile>(absolutePath, {
|
|
150
|
+
context,
|
|
151
|
+
migrator: savedFileMigrator,
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const deleteConfigFile = async (filesPath: string) => {
|
|
156
|
+
await fs.rm(filesPath, { force: true });
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const deletePipelineConfigFileByName = async (name: string, context: PipelabContext) => {
|
|
160
|
+
const filesPath = context.getConfigPath(`${name}.json`);
|
|
161
|
+
await deleteConfigFile(filesPath);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const deletePipelineConfigFileByPath = async (absolutePath: string, context: PipelabContext) => {
|
|
165
|
+
await deleteConfigFile(absolutePath);
|
|
166
|
+
};
|
package/src/context.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join, dirname, resolve } from "node:path";
|
|
2
2
|
import { homedir, platform } from "node:os";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { mkdir, realpath, mkdtemp } from "node:fs/promises";
|
|
6
6
|
import { SandboxFolder, DEFAULT_NODE_VERSION, DEFAULT_PNPM_VERSION } from "@pipelab/constants";
|
|
7
7
|
|
|
@@ -51,6 +51,14 @@ function findProjectRoot(startDir: string): string | null {
|
|
|
51
51
|
|
|
52
52
|
export const projectRoot = findProjectRoot(_dirname);
|
|
53
53
|
|
|
54
|
+
export const CacheFolder = {
|
|
55
|
+
Actions: "actions",
|
|
56
|
+
Pipelines: "pipelines",
|
|
57
|
+
Pacote: "pacote",
|
|
58
|
+
} as const;
|
|
59
|
+
|
|
60
|
+
export type CacheFolderType = (typeof CacheFolder)[keyof typeof CacheFolder];
|
|
61
|
+
|
|
54
62
|
export interface PipelabContextOptions {
|
|
55
63
|
userDataPath: string;
|
|
56
64
|
releaseTag?: string;
|
|
@@ -77,8 +85,44 @@ export class PipelabContext {
|
|
|
77
85
|
return join(this.userDataPath, "config", ...subpaths);
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
getSettingsPath() {
|
|
89
|
+
return this.getConfigPath("settings.json");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getConnectionsPath() {
|
|
93
|
+
return this.getConfigPath("connections.json");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getProjectsPath() {
|
|
97
|
+
return this.getConfigPath("projects.json");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private _cachedSettings: any = null;
|
|
101
|
+
private _cachedSettingsTime: number = 0;
|
|
102
|
+
|
|
103
|
+
private getSettings() {
|
|
104
|
+
const settingsPath = this.getSettingsPath();
|
|
105
|
+
if (!existsSync(settingsPath)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
if (this._cachedSettings && now - this._cachedSettingsTime < 2000) {
|
|
110
|
+
return this._cachedSettings;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const content = readFileSync(settingsPath, "utf8");
|
|
114
|
+
this._cachedSettings = JSON.parse(content);
|
|
115
|
+
this._cachedSettingsTime = now;
|
|
116
|
+
return this._cachedSettings;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
return this._cachedSettings;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
80
122
|
getTempPath(...subpaths: string[]) {
|
|
81
|
-
|
|
123
|
+
const settings = this.getSettings();
|
|
124
|
+
const base = settings?.tempFolder || join(this.userDataPath, "temp");
|
|
125
|
+
return join(base, ...subpaths);
|
|
82
126
|
}
|
|
83
127
|
|
|
84
128
|
async createTempFolder(prefix = "pipelab-") {
|
|
@@ -88,14 +132,25 @@ export class PipelabContext {
|
|
|
88
132
|
return await mkdtemp(join(realBaseDir, prefix));
|
|
89
133
|
}
|
|
90
134
|
|
|
91
|
-
getCachePath(
|
|
92
|
-
|
|
135
|
+
getCachePath(): string;
|
|
136
|
+
getCachePath(folder: CacheFolderType, ...subpaths: string[]): string;
|
|
137
|
+
getCachePath(folder?: CacheFolderType, ...subpaths: string[]) {
|
|
138
|
+
const settings = this.getSettings();
|
|
139
|
+
const base = settings?.cacheFolder || join(this.userDataPath, "cache");
|
|
140
|
+
if (!folder) {
|
|
141
|
+
return base;
|
|
142
|
+
}
|
|
143
|
+
return join(base, folder, ...subpaths);
|
|
93
144
|
}
|
|
94
145
|
|
|
95
146
|
getPnpmPath(...subpaths: string[]) {
|
|
96
147
|
return join(this.userDataPath, "pnpm", ...subpaths);
|
|
97
148
|
}
|
|
98
149
|
|
|
150
|
+
getBuildHistoryPath(...subpaths: string[]) {
|
|
151
|
+
return join(this.userDataPath, "build-history", ...subpaths);
|
|
152
|
+
}
|
|
153
|
+
|
|
99
154
|
getNodePath(version = DEFAULT_NODE_VERSION) {
|
|
100
155
|
const isWindows = process.platform === "win32";
|
|
101
156
|
return this.getThirdPartyPath("node", version, isWindows ? "node.exe" : "bin/node");
|