88 lines
3.3 KiB
TypeScript
88 lines
3.3 KiB
TypeScript
import { YAML } from "bun";
|
|
import { Console, Data, Effect, Array as EffectArray, pipe, Record, Schema, SchemaIssue } from "effect";
|
|
import { SchemaError } from "effect/Schema";
|
|
|
|
const COMPOSE_PATH = "compose.yaml";
|
|
const DEFAULT_CMD_TIMEOUT = 10_000;
|
|
class Compose extends Schema.Class<Compose>("Compose")({
|
|
name: Schema.String,
|
|
services: Schema.Record(Schema.String, Schema.Unknown),
|
|
}) {}
|
|
|
|
class ScriptError extends Data.TaggedError("ScriptError")<{ cause: unknown }> {}
|
|
|
|
// Const composeSchema = Schema.Record(Schema.Union([Schema.String, Schema.Symbol]), Schema.Unknown);
|
|
// type YamlRecord = ReadonlyRecord<string | symbol, unknown>;
|
|
|
|
/* */
|
|
|
|
/**
|
|
* Retourne les noms des services déclarés dans un ficher _Compose_.
|
|
* @param compose Le fichier _Compose_ sous forme d'objet.
|
|
* @returns Les noms des Services sous forme de tableau.
|
|
*/
|
|
const getServicesFromComposeYaml: (compose: Compose) => ReadonlyArray<string> = (compose) =>
|
|
Record.keys(compose.services);
|
|
|
|
/**
|
|
* Récupère le contenu texte d'un fichier pour un chemin donné.
|
|
*
|
|
* @param filePath Le chemin du fichier dont on souhaite le contenu.
|
|
* @returns Le contenu textuel du fichier sous forme de chaîne de caractères.
|
|
*/
|
|
const getFileContent: (filePath: string) => Effect.Effect<string, ScriptError> = Effect.fn("getFileContent")(
|
|
function* (filePath) {
|
|
const fileRef: Bun.BunFile = Bun.file(filePath);
|
|
|
|
yield* Effect.tryPromise({
|
|
catch: (_): ScriptError => new ScriptError({ cause: "The wanted file does not exist." }),
|
|
try: async (): Promise<boolean> => fileRef.exists(),
|
|
});
|
|
|
|
return yield* Effect.tryPromise({
|
|
catch: (_): ScriptError => new ScriptError({ cause: "Can't retrieve the file's text content." }),
|
|
try: async (): Promise<string> => fileRef.text(),
|
|
});
|
|
},
|
|
);
|
|
|
|
/**
|
|
* Récupère le contenu _YAML_ d'un fichier _Compose_ sous forme de `Record`.
|
|
*
|
|
* @param path Le chemin du fichier _Compose_.
|
|
* @param schema Le `Schema` utilisée pour le parsage des données du fichier.
|
|
* @returns Un `Record` des données du fichier.
|
|
*/
|
|
const getComposeYaml: <ComposeSchema>(
|
|
path: string,
|
|
schema: Schema.Schema<ComposeSchema>,
|
|
) => Effect.Effect<ComposeSchema, ScriptError, unknown> = Effect.fn("getComposeYaml")(function* (path, schema) {
|
|
return yield* pipe(
|
|
getFileContent(path),
|
|
Effect.map((text: string): unknown => YAML.parse(text)),
|
|
Effect.flatMap((yaml: unknown) =>
|
|
Schema.decodeUnknownEffect(schema)(yaml, { errors: "all", onExcessProperty: "ignore" }),
|
|
),
|
|
Effect.mapError((error): ScriptError => {
|
|
if (error instanceof SchemaError) {
|
|
return new ScriptError({ cause: SchemaIssue.makeFormatterStandardSchemaV1()(error.issue) });
|
|
} else {
|
|
return error;
|
|
}
|
|
}),
|
|
);
|
|
});
|
|
|
|
const program: Effect.Effect<ReadonlyArray<string>, ScriptError> = pipe(
|
|
getComposeYaml(COMPOSE_PATH, Compose),
|
|
Effect.map((compose: Compose) => getServicesFromComposeYaml(compose)),
|
|
Effect.map((keys: ReadonlyArray<string>) => EffectArray.filter(keys, (key) => key !== "wordpress")),
|
|
Effect.orElseSucceed(() => [""]),
|
|
Effect.tap((services: ReadonlyArray<string>) => {
|
|
Bun.spawn({ cmd: ["podman", "compose", "pull", ...services], timeout: DEFAULT_CMD_TIMEOUT });
|
|
return Effect.succeed(services);
|
|
}),
|
|
Effect.tapCause(Console.error),
|
|
);
|
|
|
|
Effect.runFork(program);
|