@pipelab/core-node 1.0.1-beta.25 → 1.0.1-beta.27
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/index.d.mts +10 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +96 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/handlers/build-history.ts +28 -7
- package/src/handlers/history.ts +30 -0
- package/src/runner.ts +2 -1
- package/src/utils/fs-extras.ts +30 -1
- package/src/utils.ts +18 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipelab/core-node",
|
|
3
|
-
"version": "1.0.1-beta.
|
|
3
|
+
"version": "1.0.1-beta.27",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Pipelab automation engine for Node.js",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"adm-zip": "0.5.16",
|
|
30
30
|
"archiver": "7.0.1",
|
|
31
|
+
"check-disk-space": "3.4.0",
|
|
31
32
|
"es-toolkit": "1.25.2",
|
|
32
33
|
"execa": "9.5.2",
|
|
33
34
|
"nanoid": "5.0.7",
|
|
@@ -38,8 +39,8 @@
|
|
|
38
39
|
"type-fest": "4.39.0",
|
|
39
40
|
"ws": "8.18.3",
|
|
40
41
|
"yauzl": "2.10.0",
|
|
41
|
-
"@pipelab/constants": "1.0.1-beta.
|
|
42
|
-
"@pipelab/shared": "2.0.1-beta.
|
|
42
|
+
"@pipelab/constants": "1.0.1-beta.23",
|
|
43
|
+
"@pipelab/shared": "2.0.1-beta.24"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@types/adm-zip": "0.5.7",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"@types/yauzl": "2.10.3",
|
|
53
54
|
"tsdown": "0.21.2",
|
|
54
55
|
"typescript": "^5.0.0",
|
|
55
|
-
"@pipelab/tsconfig": "1.0.1-beta.
|
|
56
|
+
"@pipelab/tsconfig": "1.0.1-beta.23"
|
|
56
57
|
},
|
|
57
58
|
"scripts": {
|
|
58
59
|
"build": "tsdown",
|
|
@@ -2,9 +2,10 @@ import { PipelabContext } from "../context";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { writeFile, readFile, unlink, mkdir, stat, readdir } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { BuildHistoryEntry, IBuildHistoryStorage, AppConfig } from "@pipelab/shared";
|
|
6
|
-
import { useLogger } from "@pipelab/shared";
|
|
5
|
+
import { useLogger, BuildHistoryEntry, IBuildHistoryStorage, AppConfig } from "@pipelab/shared";
|
|
7
6
|
import { setupConfigFile } from "../config";
|
|
7
|
+
import checkDiskSpace from "check-disk-space";
|
|
8
|
+
import { getFolderSize } from "../utils/fs-extras";
|
|
8
9
|
|
|
9
10
|
// Simplified storage - one file per pipeline containing array of build entries
|
|
10
11
|
|
|
@@ -128,6 +129,11 @@ export class BuildHistoryStorage implements IBuildHistoryStorage {
|
|
|
128
129
|
this.logger
|
|
129
130
|
.logger()
|
|
130
131
|
.info(`Saved build history entry: ${entry.id} for pipeline: ${entry.pipelineId}`);
|
|
132
|
+
|
|
133
|
+
// Apply retention policy after each save
|
|
134
|
+
this.applyRetentionPolicy().catch((err) => {
|
|
135
|
+
this.logger.logger().error("Failed to apply retention policy after save:", err);
|
|
136
|
+
});
|
|
131
137
|
} catch (error) {
|
|
132
138
|
this.logger.logger().error("Failed to save build history entry:", error);
|
|
133
139
|
throw new Error(`Failed to save build history entry: ${error}`);
|
|
@@ -280,24 +286,31 @@ export class BuildHistoryStorage implements IBuildHistoryStorage {
|
|
|
280
286
|
oldestEntry?: number;
|
|
281
287
|
newestEntry?: number;
|
|
282
288
|
numberOfPipelines: number;
|
|
283
|
-
cachePath: string;
|
|
284
289
|
userDataPath: string;
|
|
285
290
|
retentionPolicy: {
|
|
286
291
|
enabled: boolean;
|
|
287
292
|
maxEntries: number;
|
|
288
293
|
maxAge: number;
|
|
289
294
|
};
|
|
295
|
+
disk: {
|
|
296
|
+
total: number;
|
|
297
|
+
free: number;
|
|
298
|
+
pipelab: number;
|
|
299
|
+
};
|
|
290
300
|
}> {
|
|
291
301
|
try {
|
|
292
302
|
const allEntries = await this.getAll();
|
|
293
303
|
const files = await this.getAllPipelineFiles();
|
|
294
304
|
|
|
305
|
+
const diskSpace = await checkDiskSpace(this.context.userDataPath);
|
|
306
|
+
const pipelabSize = await getFolderSize(this.context.userDataPath);
|
|
307
|
+
|
|
295
308
|
const settings = await setupConfigFile<AppConfig>("settings", { context: this.context });
|
|
296
309
|
const config = await settings.getConfig();
|
|
297
310
|
const policy = config?.buildHistory?.retentionPolicy || {
|
|
298
311
|
enabled: false,
|
|
299
|
-
maxEntries:
|
|
300
|
-
maxAge:
|
|
312
|
+
maxEntries: 50,
|
|
313
|
+
maxAge: 30,
|
|
301
314
|
};
|
|
302
315
|
|
|
303
316
|
if (allEntries.length === 0) {
|
|
@@ -305,13 +318,17 @@ export class BuildHistoryStorage implements IBuildHistoryStorage {
|
|
|
305
318
|
totalEntries: 0,
|
|
306
319
|
totalSize: 0,
|
|
307
320
|
numberOfPipelines: files.length,
|
|
308
|
-
cachePath: config?.cacheFolder || tmpdir(),
|
|
309
321
|
userDataPath: this.context.userDataPath,
|
|
310
322
|
retentionPolicy: {
|
|
311
323
|
enabled: policy.enabled,
|
|
312
324
|
maxEntries: policy.maxEntries,
|
|
313
325
|
maxAge: policy.maxAge,
|
|
314
326
|
},
|
|
327
|
+
disk: {
|
|
328
|
+
total: diskSpace.size,
|
|
329
|
+
free: diskSpace.free,
|
|
330
|
+
pipelab: pipelabSize,
|
|
331
|
+
},
|
|
315
332
|
};
|
|
316
333
|
}
|
|
317
334
|
|
|
@@ -334,13 +351,17 @@ export class BuildHistoryStorage implements IBuildHistoryStorage {
|
|
|
334
351
|
oldestEntry: sortedEntries[0]?.createdAt,
|
|
335
352
|
newestEntry: sortedEntries[sortedEntries.length - 1]?.createdAt,
|
|
336
353
|
numberOfPipelines: files.length,
|
|
337
|
-
cachePath: config?.cacheFolder || tmpdir(),
|
|
338
354
|
userDataPath: this.context.userDataPath,
|
|
339
355
|
retentionPolicy: {
|
|
340
356
|
enabled: policy.enabled,
|
|
341
357
|
maxEntries: policy.maxEntries,
|
|
342
358
|
maxAge: policy.maxAge,
|
|
343
359
|
},
|
|
360
|
+
disk: {
|
|
361
|
+
total: diskSpace.size,
|
|
362
|
+
free: diskSpace.free,
|
|
363
|
+
pipelab: pipelabSize,
|
|
364
|
+
},
|
|
344
365
|
};
|
|
345
366
|
} catch (error) {
|
|
346
367
|
this.logger.logger().error("Failed to get storage info:", error);
|
package/src/handlers/history.ts
CHANGED
|
@@ -226,6 +226,36 @@ export const registerHistoryHandlers = (context: PipelabContext) => {
|
|
|
226
226
|
}
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
handle("build-history:clear-by-pipeline", async (event, { send, value }) => {
|
|
230
|
+
try {
|
|
231
|
+
await checkBuildHistoryAuthorization(event);
|
|
232
|
+
|
|
233
|
+
await buildHistoryStorage.clearByPipeline(value.pipelineId);
|
|
234
|
+
send({
|
|
235
|
+
type: "end",
|
|
236
|
+
data: { type: "success", result: { result: "ok" } },
|
|
237
|
+
});
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logger().error(`Failed to clear build history for pipeline ${value.pipelineId}:`, error);
|
|
240
|
+
|
|
241
|
+
if (error instanceof SubscriptionRequiredError) {
|
|
242
|
+
send({
|
|
243
|
+
type: "end",
|
|
244
|
+
data: { type: "error", ipcError: error.userMessage, code: error.code },
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
send({
|
|
250
|
+
type: "end",
|
|
251
|
+
data: {
|
|
252
|
+
type: "error",
|
|
253
|
+
ipcError: error instanceof Error ? error.message : "Failed to clear build history for pipeline",
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
229
259
|
handle("build-history:get-storage-info", async (event, { send }) => {
|
|
230
260
|
try {
|
|
231
261
|
await checkBuildHistoryAuthorization(event);
|
package/src/runner.ts
CHANGED
|
@@ -69,7 +69,8 @@ export async function runPipelineCommand(file: string, options: RunOptions, vers
|
|
|
69
69
|
|
|
70
70
|
const settings = await setupConfigFile<AppConfig>("settings", { context });
|
|
71
71
|
const config = await settings.getConfig();
|
|
72
|
-
const cachePath =
|
|
72
|
+
const cachePath = join(context.userDataPath, "cache");
|
|
73
|
+
await mkdir(cachePath, { recursive: true });
|
|
73
74
|
|
|
74
75
|
const abortController = new AbortController();
|
|
75
76
|
|
package/src/utils/fs-extras.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, createWriteStream, createReadStream } from "node:fs";
|
|
2
2
|
import { execa, Options, Subprocess } from "execa";
|
|
3
|
-
import { mkdir as mkdirP, access, writeFile, realpath, mkdtemp, chmod } from "node:fs/promises";
|
|
3
|
+
import { mkdir as mkdirP, access, writeFile, realpath, mkdtemp, chmod, stat, readdir } from "node:fs/promises";
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import tar from "tar";
|
|
@@ -180,3 +180,32 @@ export const runWithLiveLogs = async (
|
|
|
180
180
|
throw new Error(`Command failed with exit code ${code}: ${error.message}`);
|
|
181
181
|
}
|
|
182
182
|
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Calculates the total size of a directory recursively.
|
|
186
|
+
*/
|
|
187
|
+
export async function getFolderSize(dirPath: string): Promise<number> {
|
|
188
|
+
try {
|
|
189
|
+
const files = await readdir(dirPath, { withFileTypes: true });
|
|
190
|
+
const ArrayOfPromises = files.map(async (file) => {
|
|
191
|
+
const path = join(dirPath, file.name);
|
|
192
|
+
if (file.isDirectory()) {
|
|
193
|
+
try {
|
|
194
|
+
return await getFolderSize(path);
|
|
195
|
+
} catch {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const { size } = await stat(path);
|
|
201
|
+
return size;
|
|
202
|
+
} catch {
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
const results = await Promise.all(ArrayOfPromises);
|
|
207
|
+
return results.reduce((acc, size) => acc + size, 0);
|
|
208
|
+
} catch {
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -125,6 +125,9 @@ export const executeGraphWithHistory = async ({
|
|
|
125
125
|
const logs: any[] = [];
|
|
126
126
|
const sandboxPath = await generateTempFolder(cachePath);
|
|
127
127
|
const { logger } = useLogger();
|
|
128
|
+
let completedSteps = 0;
|
|
129
|
+
let failedSteps = 0;
|
|
130
|
+
let cancelledSteps = 0;
|
|
128
131
|
|
|
129
132
|
// Ensure all plugins used in the graph are downloaded and registered
|
|
130
133
|
const { registerPlugins, plugins: registeredPlugins } = usePlugins();
|
|
@@ -156,7 +159,7 @@ export const executeGraphWithHistory = async ({
|
|
|
156
159
|
logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
|
|
157
160
|
const settingsFile = await setupConfigFile<AppConfig>("settings", { context: ctx });
|
|
158
161
|
const config = await settingsFile.getConfig();
|
|
159
|
-
const shouldCleanup =
|
|
162
|
+
const shouldCleanup = true;
|
|
160
163
|
|
|
161
164
|
try {
|
|
162
165
|
const result = await processGraph({
|
|
@@ -175,7 +178,8 @@ export const executeGraphWithHistory = async ({
|
|
|
175
178
|
async (data) => {
|
|
176
179
|
if (data.type === "log") {
|
|
177
180
|
const logEntry = {
|
|
178
|
-
|
|
181
|
+
id: nanoid(),
|
|
182
|
+
level: "info",
|
|
179
183
|
message: data.data.message,
|
|
180
184
|
timestamp: data.data.time,
|
|
181
185
|
nodeUid: node?.uid,
|
|
@@ -195,7 +199,14 @@ export const executeGraphWithHistory = async ({
|
|
|
195
199
|
onNodeEnter: (node) => {
|
|
196
200
|
onNodeEnter?.(node);
|
|
197
201
|
},
|
|
198
|
-
onNodeExit: (node) => {
|
|
202
|
+
onNodeExit: async (node) => {
|
|
203
|
+
completedSteps++;
|
|
204
|
+
if (!shouldDisableHistory) {
|
|
205
|
+
// Update progress in the background
|
|
206
|
+
buildHistoryStorage.update(buildId, { completedSteps }, pipelineId).catch((err) => {
|
|
207
|
+
logger().error(`Failed to update progress for build ${buildId}:`, err);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
199
210
|
onNodeExit?.(node);
|
|
200
211
|
},
|
|
201
212
|
abortSignal,
|
|
@@ -211,6 +222,7 @@ export const executeGraphWithHistory = async ({
|
|
|
211
222
|
duration: endTime - startTime,
|
|
212
223
|
output: result.steps,
|
|
213
224
|
logs,
|
|
225
|
+
completedSteps,
|
|
214
226
|
},
|
|
215
227
|
pipelineId,
|
|
216
228
|
);
|
|
@@ -234,6 +246,9 @@ export const executeGraphWithHistory = async ({
|
|
|
234
246
|
timestamp: endTime,
|
|
235
247
|
},
|
|
236
248
|
logs,
|
|
249
|
+
completedSteps,
|
|
250
|
+
failedSteps: isCanceled ? 0 : 1,
|
|
251
|
+
cancelledSteps: isCanceled ? 1 : 0,
|
|
237
252
|
},
|
|
238
253
|
pipelineId,
|
|
239
254
|
);
|