haiku-atelier-2024/scripts/pull-container-images.ts
gcch d50de6d534 2026-04-10
- corvée: met à jour les deps
- corvée: formate
2026-04-10 23:54:12 +02:00

88 lines
3.3 KiB
TypeScript

import { YAML } from "bun";
import { Array as EffectArray, Console, Data, Effect, 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);