feat: export Catalog Studio assets and SQL to catalog repository
This commit is contained in:
1 parent
bfbf244141
commit
9ec9ab31ad
21 files changed
+1811
-514
No files matched your search
@@ -0,0 +1,82 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import { hostname } from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
CATALOG_BRANCH,
|
||||
CATALOG_REMOTE,
|
||||
publishCatalogFiles,
|
||||
recoverCatalogQueue,
|
||||
} from "./catalog-git-core";
|
||||
import { catalogExportEnabled, catalogExportQueue } from "./catalog-git-queue";
|
||||
import { createCatalogSnapshot } from "./catalog-git-snapshot";
|
||||
|
||||
export async function runCatalogExport() {
|
||||
const checkout = process.env.CATALOG_GIT_CHECKOUT?.trim();
|
||||
if (!catalogExportEnabled() || !checkout) return;
|
||||
const queue = catalogExportQueue();
|
||||
await queue.entries();
|
||||
await recoverCatalogQueue(queue.root);
|
||||
const lock = await fs
|
||||
.open(path.join(queue.root, "worker.lock"), "wx")
|
||||
.catch((error: NodeJS.ErrnoException) => {
|
||||
if (error.code === "EEXIST") return null;
|
||||
throw error;
|
||||
});
|
||||
if (!lock) return;
|
||||
let snapshot: string | undefined;
|
||||
const status = async (value: Record<string, unknown>) => {
|
||||
const temp = path.join(queue.root, "status.tmp");
|
||||
let previous: Record<string, unknown> = {};
|
||||
try {
|
||||
previous = JSON.parse(
|
||||
await fs.readFile(path.join(queue.root, "status.json"), "utf8"),
|
||||
);
|
||||
} catch {
|
||||
/* First attempt. */
|
||||
}
|
||||
await fs.writeFile(
|
||||
temp,
|
||||
JSON.stringify({ ...previous, error: undefined, ...value }),
|
||||
);
|
||||
await fs.rename(temp, path.join(queue.root, "status.json"));
|
||||
};
|
||||
try {
|
||||
await lock.writeFile(
|
||||
JSON.stringify({ pid: process.pid, host: hostname() }),
|
||||
);
|
||||
const batch = await queue.batch();
|
||||
if (!batch?.length) return;
|
||||
snapshot = await fs.mkdtemp(path.join(queue.root, "snapshot-"));
|
||||
const files = await createCatalogSnapshot(snapshot);
|
||||
// Any operation started/completed during capture invalidates the snapshot.
|
||||
const current = await queue.batch();
|
||||
if (!current || JSON.stringify(current) !== JSON.stringify(batch)) return;
|
||||
const commit = await publishCatalogFiles({
|
||||
checkout,
|
||||
remote: CATALOG_REMOTE,
|
||||
branch: CATALOG_BRANCH,
|
||||
files,
|
||||
});
|
||||
await queue.complete(batch);
|
||||
await status({ commit, finishedAt: new Date().toISOString() });
|
||||
} catch {
|
||||
// Do not expose credentials or Git stderr through the admin API.
|
||||
await status({
|
||||
error:
|
||||
"Export failed. Check repository access, branch, clean checkout and asset/database availability. Pending changes will be retried.",
|
||||
finishedAt: new Date().toISOString(),
|
||||
});
|
||||
} finally {
|
||||
try {
|
||||
if (
|
||||
snapshot &&
|
||||
path.resolve(snapshot).startsWith(path.resolve(queue.root) + path.sep)
|
||||
) {
|
||||
await fs.rm(snapshot, { recursive: true, force: true });
|
||||
}
|
||||
} finally {
|
||||
await lock.close();
|
||||
await fs.rm(path.join(queue.root, "worker.lock"), { force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user