94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
import { AsyncLocalStorage } from "node:async_hooks";
|
|
import type { ComposeSpecification } from "../schema/docker.d";
|
|
import type { KuberHookContext } from "../types";
|
|
import { hookContext, loadConfig, type ResolvedKuberConfig } from "./config";
|
|
import { readCompose, resolveComposeFile } from "./yaml";
|
|
|
|
type AppContext = {
|
|
project: string;
|
|
cwd: string;
|
|
config: ResolvedKuberConfig;
|
|
path(): Promise<string | undefined>;
|
|
hookContext(): Promise<KuberHookContext>;
|
|
compose(): Promise<ComposeSpecification>;
|
|
};
|
|
|
|
const appContext = new AsyncLocalStorage<AppContext>();
|
|
|
|
export function resolveProjectName(
|
|
config: Pick<ResolvedKuberConfig, "project" | "projectConfigured">,
|
|
compose: ComposeSpecification,
|
|
): string {
|
|
if (config.projectConfigured) return config.project;
|
|
return compose.name?.trim() || config.project;
|
|
}
|
|
|
|
export async function provideContext<T>(
|
|
fn: () => T,
|
|
configPath?: string,
|
|
): Promise<Awaited<T>> {
|
|
const cwd = process.cwd();
|
|
const config = await loadConfig(cwd, configPath);
|
|
const path = createCachedGetter("composeFile", () =>
|
|
config.composeFile
|
|
? Promise.resolve(config.composeFile)
|
|
: resolveComposeFile(cwd),
|
|
);
|
|
const parsedCompose = createCachedGetter("parsedCompose", async () => {
|
|
const composeFile = await path();
|
|
if (!composeFile) throw new Error("Compose file cannot be found");
|
|
return readCompose(composeFile);
|
|
});
|
|
|
|
if (!config.projectConfigured) {
|
|
const composeFile = await path();
|
|
if (composeFile) {
|
|
config.project = resolveProjectName(config, await parsedCompose());
|
|
}
|
|
}
|
|
|
|
const getHookContext = createCachedGetter("hookContext", async () => {
|
|
const composeFile = await path();
|
|
if (!composeFile) throw new Error("Compose file cannot be found");
|
|
return hookContext(config, cwd, composeFile);
|
|
});
|
|
const compose = createCachedGetter("compose", async () => {
|
|
const context = await getHookContext();
|
|
const value = await parsedCompose();
|
|
await config.compose?.(value, context);
|
|
return value;
|
|
});
|
|
|
|
return await appContext.run(
|
|
{
|
|
project: config.project,
|
|
cwd,
|
|
config,
|
|
path,
|
|
hookContext: getHookContext,
|
|
compose,
|
|
},
|
|
fn,
|
|
);
|
|
}
|
|
|
|
export function ctx() {
|
|
const store = appContext.getStore();
|
|
if (!store) throw new Error("Not in context!");
|
|
return store;
|
|
}
|
|
|
|
export function createCachedGetter<T>(
|
|
_key: string,
|
|
fn: () => Promise<T>,
|
|
): () => Promise<T> {
|
|
let cached: Promise<T> | undefined;
|
|
return () => {
|
|
cached ??= fn().catch((error) => {
|
|
cached = undefined;
|
|
throw error;
|
|
});
|
|
return cached;
|
|
};
|
|
}
|