Skip to content

Commit 0d7ebb7

Browse files
committed
-
1 parent 7e93ceb commit 0d7ebb7

50 files changed

Lines changed: 929 additions & 914 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/src/commands/maintenance.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,7 @@ export async function usageCommand(options: { userData?: string }) {
2626
"Oldest Entry": info.oldestEntry ? new Date(info.oldestEntry).toLocaleString() : "N/A",
2727
"Newest Entry": info.newestEntry ? new Date(info.newestEntry).toLocaleString() : "N/A",
2828
"User Data Path": info.userDataPath,
29-
"Cache Path": join(info.userDataPath, "build-history"),
30-
});
31-
32-
console.log("\nRetention Policy:");
33-
console.table({
34-
Enabled: info.retentionPolicy.enabled ? "Yes" : "No",
35-
"Max Entries":
36-
info.retentionPolicy.maxEntries > 0 ? info.retentionPolicy.maxEntries : "Unlimited",
37-
"Max Age (days)": info.retentionPolicy.maxAge > 0 ? info.retentionPolicy.maxAge : "Unlimited",
29+
"Cache Path": context.getBuildHistoryPath(),
3830
});
3931
}
4032

apps/cli/src/commands/pipelines.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PipelabContext, setupConfigFile } from "@pipelab/core-node";
1+
import { PipelabContext, setupConfigFile, deleteConfigFile } from "@pipelab/core-node";
22
import { FileRepo, SaveLocation } from "@pipelab/shared";
33
import { readFile, unlink, readdir } from "node:fs/promises";
44
import { getDefaultUserDataPath } from "../paths";
@@ -239,21 +239,8 @@ export async function deletePipelineCommand(
239239
// 1. Delete internal file if applicable
240240
if (pipeline.type === "internal") {
241241
try {
242-
const filePath = context.getConfigPath(`${pipeline.configName}.json`);
243-
await unlink(filePath);
242+
await deleteConfigFile(pipeline.configName, context);
244243
console.log(`Deleted pipeline file: ${pipeline.configName}.json`);
245-
246-
// Clean up versioned backups too
247-
const parsedPath = path.parse(filePath);
248-
const dirEntries = await readdir(parsedPath.dir).catch(() => [] as string[]);
249-
const prefix = `${parsedPath.name}.v`;
250-
const suffix = `.json`;
251-
for (const entry of dirEntries) {
252-
if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
253-
const backupPath = path.join(parsedPath.dir, entry);
254-
await unlink(backupPath).catch(() => {});
255-
}
256-
}
257244
} catch (e: any) {
258245
if (e.code !== "ENOENT") {
259246
console.warn(`Warning: Could not delete pipeline file: ${e.message}`);

apps/cli/tests/e2e/tests/history.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ describe("End-to-End: Build History", () => {
3636
});
3737

3838
// The build history should be generated in the user-data folder
39-
// Based on BuildHistoryStorage.getPipelinePath, the filename should be pipeline-<sanitizedId>.json
39+
// Based on BuildHistoryStorage.getPipelinePath, the filename should be config/pipelines/<pipelineId>.history.json
4040
const historyFile = join(
4141
sandbox.paths.userData,
42-
"build-history",
43-
`pipeline-${pipelineId}.json`,
42+
"config",
43+
"pipelines",
44+
`${pipelineId}.history.json`,
4445
);
4546

4647
// Verification: file must exist

apps/ui/src/components/Settings.vue

Lines changed: 4 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,10 @@
172172
<!-- Advanced Tab Content -->
173173
<div v-if="currentSection === 'advanced'" class="settings-panel">
174174
<div class="section-header">
175-
<h3>{{ t("settings.retentionPolicy") }}</h3>
176-
<p class="description">{{ t("settings.retentionPolicyDescription") }}</p>
175+
<h3>{{ t("settings.tabs.advanced") }}</h3>
176+
<p class="description">
177+
{{ t("settings.manage-where-the-app-stores-temporary-and-cache-files") }}
178+
</p>
177179
</div>
178180

179181
<div v-if="storageInfo && storageInfo.disk" class="storage-card mb-4">
@@ -284,81 +286,6 @@
284286
</div>
285287
</div>
286288
</div>
287-
288-
<div class="settings-group">
289-
<div class="setting-item">
290-
<div class="setting-content">
291-
<label for="retention-enabled" class="setting-title">{{
292-
t("settings.retentionEnabled")
293-
}}</label>
294-
<div class="setting-description">
295-
Automatically delete old pipelines builds to save space.
296-
</div>
297-
</div>
298-
<div class="setting-action">
299-
<ToggleSwitch
300-
:disabled="!settingsRef"
301-
input-id="retention-enabled"
302-
:model-value="settingsRef?.buildHistory?.retentionPolicy?.enabled ?? false"
303-
@update:model-value="updateRetentionEnabled"
304-
/>
305-
</div>
306-
</div>
307-
308-
<div
309-
class="setting-item"
310-
:class="{
311-
'opacity-50 pointer-events-none':
312-
!settingsRef?.buildHistory?.retentionPolicy?.enabled,
313-
}"
314-
>
315-
<div class="setting-content">
316-
<label for="max-entries" class="setting-title">{{
317-
t("settings.retentionMaxEntries")
318-
}}</label>
319-
<div class="setting-description">
320-
{{ t("settings.retentionMaxEntriesDescription") }}
321-
</div>
322-
</div>
323-
<div class="setting-action">
324-
<InputNumber
325-
v-model="retentionMaxEntries"
326-
:disabled="!settingsRef || !settingsRef?.buildHistory?.retentionPolicy?.enabled"
327-
input-id="max-entries"
328-
show-buttons
329-
:min="1"
330-
:max="1000"
331-
class="w-[120px]"
332-
/>
333-
</div>
334-
</div>
335-
336-
<div
337-
class="setting-item"
338-
:class="{
339-
'opacity-50 pointer-events-none':
340-
!settingsRef?.buildHistory?.retentionPolicy?.enabled,
341-
}"
342-
>
343-
<div class="setting-content">
344-
<label for="max-age" class="setting-title">{{ t("settings.retentionMaxAge") }}</label>
345-
<div class="setting-description">
346-
{{ t("settings.retentionMaxAgeDescription") }}
347-
</div>
348-
</div>
349-
<div class="setting-action">
350-
<InputNumber
351-
v-model="retentionMaxAge"
352-
:disabled="!settingsRef || !settingsRef?.buildHistory?.retentionPolicy?.enabled"
353-
input-id="max-age"
354-
show-buttons
355-
:min="1"
356-
:max="365"
357-
class="w-[120px]"
358-
/>
359-
</div>
360-
</div>
361-
</div>
362289
</div>
363290

364291
<!-- Versions Tab Content -->
@@ -734,72 +661,6 @@ const updateAutosave = (value: boolean) => {
734661
});
735662
};
736663
737-
const updateRetentionEnabled = (value: boolean) => {
738-
const currentBuildHistory = settingsRef.value.buildHistory || {};
739-
const currentPolicy = currentBuildHistory.retentionPolicy || {
740-
enabled: false,
741-
maxEntries: 50,
742-
maxAge: 30,
743-
};
744-
745-
return appSettings.updateSettings({
746-
...(toRaw(settingsRef.value) as any),
747-
buildHistory: {
748-
...currentBuildHistory,
749-
retentionPolicy: {
750-
...currentPolicy,
751-
enabled: value,
752-
},
753-
},
754-
});
755-
};
756-
757-
const retentionMaxEntries = computed({
758-
get: () => settingsRef.value?.buildHistory?.retentionPolicy?.maxEntries ?? 50,
759-
set: (value: number) => {
760-
const currentBuildHistory = settingsRef.value.buildHistory || {};
761-
const currentPolicy = currentBuildHistory.retentionPolicy || {
762-
enabled: false,
763-
maxEntries: 50,
764-
maxAge: 30,
765-
};
766-
767-
appSettings.updateSettings({
768-
...(toRaw(settingsRef.value) as any),
769-
buildHistory: {
770-
...currentBuildHistory,
771-
retentionPolicy: {
772-
...currentPolicy,
773-
maxEntries: value,
774-
},
775-
},
776-
});
777-
},
778-
});
779-
780-
const retentionMaxAge = computed({
781-
get: () => settingsRef.value?.buildHistory?.retentionPolicy?.maxAge ?? 30,
782-
set: (value: number) => {
783-
const currentBuildHistory = settingsRef.value.buildHistory || {};
784-
const currentPolicy = currentBuildHistory.retentionPolicy || {
785-
enabled: false,
786-
maxEntries: 50,
787-
maxAge: 30,
788-
};
789-
790-
appSettings.updateSettings({
791-
...(toRaw(settingsRef.value) as any),
792-
buildHistory: {
793-
...currentBuildHistory,
794-
retentionPolicy: {
795-
...currentPolicy,
796-
maxAge: value,
797-
},
798-
},
799-
});
800-
},
801-
});
802-
803664
const isBillingPortalUrlLoading = ref(false);
804665
805666
const openBillingPortal = async () => {

apps/ui/src/i18n.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ export const i18n = createI18n<[MessageSchema], Locales>({
77
fallbackLocale: "en-US",
88
messages: {
99
"en-US": en_US,
10-
"fr-FR": fr_FR as unknown as MessageSchema,
11-
"pt-BR": pt_BR as unknown as MessageSchema,
12-
"zh-CN": zh_CN as unknown as MessageSchema,
13-
"es-ES": es_ES as unknown as MessageSchema,
14-
"de-DE": de_DE as unknown as MessageSchema,
10+
"fr-FR": fr_FR,
11+
"pt-BR": pt_BR,
12+
"zh-CN": zh_CN,
13+
"es-ES": es_ES,
14+
"de-DE": de_DE,
1515
},
1616
});

apps/ui/src/pages/editor.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ const run = async () => {
640640
if (!isLoggedIn.value && authStore.hasLoginProvider) {
641641
authStore.displayAuthModal(
642642
t("editor.welcome-back"),
643-
t("editor.please-log-in-to-run-a-scenario"),
643+
t("editor.please-log-in-to-run-a-pipeline"),
644644
);
645645
return;
646646
}

apps/ui/src/pages/index.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,10 @@
380380
:disabled="!canCreatePipeline"
381381
size="small"
382382
@click="onNewFileCreation(newProjectData)"
383-
>{{ $t("home.duplicate-project") }}</Button
383+
>{{ $t("home.duplicate-pipeline") }}</Button
384384
>
385385
<Button v-else :disabled="!canCreatePipeline" size="small" @click="onNewFileCreation()">{{
386-
$t("home.create-project")
386+
$t("home.create-pipeline")
387387
}}</Button>
388388
</div>
389389
</div>
@@ -822,7 +822,7 @@ const onNewFileCreation = async (preset?: Preset) => {
822822
const type: SaveLocation["type"] = isCloudProject.value ? "pipelab-cloud" : "internal";
823823
824824
if (type === "internal") {
825-
pathOrConfigName = `pipeline-${pipelineId}`;
825+
pathOrConfigName = `pipelines/${pipelineId}`;
826826
}
827827
828828
const updatedPreset: Preset = {
@@ -1052,7 +1052,7 @@ const migratePipeline = async (file: EnhancedFile) => {
10521052
rejectClass: "p-button-secondary p-button-outlined",
10531053
acceptClass: "p-button-primary",
10541054
accept: async () => {
1055-
const newConfigName = `pipeline-${nanoid()}`;
1055+
const newConfigName = `pipelines/${nanoid()}`;
10561056
10571057
// Save content to internal config
10581058
await api.execute("config:save", {
@@ -1117,7 +1117,7 @@ const importPipeline = async () => {
11171117
try {
11181118
const fileData = JSON.parse(fileContentResult.result.content) as SavedFile;
11191119
const pipelineId = nanoid();
1120-
const configName = `pipeline-${pipelineId}`;
1120+
const configName = `pipelines/${pipelineId}`;
11211121
11221122
// Save to internal storage
11231123
await api.execute("config:save", {

apps/ui/src/pages/scenarios.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{{ headerSentence }}
55
</div>
66
<div class="content">
7-
{{ $t("scenarios.scenarios") }}
7+
{{ $t("pipelines.title") }}
88
</div>
99
</div>
1010
</template>

apps/ui/src/router/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const routes: RouterOptions["routes"] = [
3838
name: "Scenarios",
3939
component: () => import("../pages/scenarios.vue"),
4040
meta: {
41-
title: t("headers.scenarios"),
41+
title: t("headers.pipelines"),
4242
},
4343
children: [],
4444
},

packages/core-node/src/config.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from "node:path";
33
import { ensure } from "./utils/fs-extras";
44
import fs from "node:fs/promises";
55
import { useLogger } from "@pipelab/shared";
6-
import { configRegistry, Migrator, normalizePipelineConfig } from "@pipelab/shared";
6+
import { configRegistry, Migrator } from "@pipelab/shared";
77

88
export const getMigrator = <T>(name: string) => {
99
return (configRegistry[name] || configRegistry["pipeline"]) as Migrator<T>;
@@ -62,10 +62,7 @@ export const setupConfigFile = async <T>(
6262
debug: false,
6363
onStep: async (state: any, version: string) => {
6464
const parsedPath = path.parse(filesPath);
65-
const versionedPath = path.join(
66-
parsedPath.dir,
67-
`${parsedPath.name}.v${version}.json`,
68-
);
65+
const versionedPath = ctx.getConfigPath(`${parsedPath.name}.v${version}.json`);
6966
try {
7067
await fs.writeFile(versionedPath, JSON.stringify(state));
7168
logger().info(`Intermediate backup created for ${name} at ${versionedPath}`);
@@ -86,33 +83,18 @@ export const setupConfigFile = async <T>(
8683
json = migrator.defaultValue;
8784
}
8885

89-
let normalized = false;
90-
const isPipeline =
91-
name.startsWith("pipeline-") ||
92-
path.isAbsolute(name) ||
93-
name.endsWith(".json") ||
94-
name === "pipeline";
95-
if (isPipeline) {
96-
normalized = normalizePipelineConfig(json);
97-
}
98-
9986
const originalVersion = originalJson?.version;
10087
const newVersion = json?.version;
10188

10289
const shouldSaveBack =
103-
originalVersion !== newVersion ||
104-
normalized ||
105-
content === undefined ||
106-
parseFailed ||
107-
migrationFailed;
90+
originalVersion !== newVersion || content === undefined || parseFailed || migrationFailed;
10891

10992
if (shouldSaveBack) {
11093
if (parseFailed || migrationFailed) {
11194
try {
11295
const parsedPath = path.parse(filesPath);
11396
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
114-
const corruptedPath = path.join(
115-
parsedPath.dir,
97+
const corruptedPath = ctx.getConfigPath(
11698
`${parsedPath.name}.corrupted.${timestamp}.json`,
11799
);
118100
const backupContent = parseFailed
@@ -136,3 +118,10 @@ export const setupConfigFile = async <T>(
136118
},
137119
};
138120
};
121+
122+
export const deleteConfigFile = async (nameOrPath: string, context: PipelabContext) => {
123+
const isAbsolutePath = path.isAbsolute(nameOrPath);
124+
const filesPath = isAbsolutePath ? nameOrPath : context.getConfigPath(`${nameOrPath}.json`);
125+
126+
await fs.rm(filesPath, { force: true });
127+
};

0 commit comments

Comments
 (0)