#!/usr/bin/env -S deno run --allow-env --allow-net --allow-run --allow-read --allow-write import { Either, getRequiredEnvVars, getStdout, type IEither, LogTraceable, LogMetricTraceable, Metric, prependWith, TraceUtil, } from "@emprespresso/pengueno"; import type { AnsiblePlaybookJob } from "@emprespresso/ci_model"; import { Bitwarden, type SecureNote } from "@emprespresso/ci_worker"; const eitherJob = getRequiredEnvVars([ "path", "playbooks", ]) .mapRight((baseArgs) => ( { type: "ansible_playbook.ts", arguments: baseArgs, } )); const eitherVault = Bitwarden.getConfigFromEnvironment() .mapRight((config) => new Bitwarden(config)); const playbookMetric = Metric.fromName("ansiblePlaybook.playbook"); const _logJob = LogTraceable.of(eitherJob).bimap(TraceUtil.withTrace("ansible_playbook")); await LogMetricTraceable.ofLogTraceable(_logJob).bimap(TraceUtil.withMetricTrace(playbookMetric)) .peek((tEitherJob) => tEitherJob.trace.trace("starting ansible playbook job! (⑅˘꒳˘)") ) .map((tEitherJob) => tEitherJob.get().flatMapAsync((job) => eitherVault.flatMapAsync(async (vault) => { const eitherKey = await vault.unlock(tEitherJob); return eitherKey.mapRight((key) => ({ job, key, vault })); }) ) ) .map(async (tEitherJobVault) => { tEitherJobVault.trace.trace( "getting ansible secwets uwu~", ); const eitherJobVault = await tEitherJobVault.get(); const eitherSshKey = await eitherJobVault .flatMapAsync(({ key, vault }) => vault.fetchSecret(tEitherJobVault, key, "ssh_key") ); const eitherSshKeyFile = await eitherSshKey.mapRight(({ notes }) => notes) .flatMapAsync(saveToTempFile); const eitherAnsibleSecrets = await eitherJobVault .flatMapAsync(({ key, vault }) => vault.fetchSecret(tEitherJobVault, key, "ansible_playbooks") ); const eitherAnsibleSecretsFile = await eitherAnsibleSecrets.mapRight(( { notes }, ) => notes).flatMapAsync(saveToTempFile); return eitherJobVault.flatMapAsync(async ({ job, vault, key }) => { const eitherLocked = await vault.lock(tEitherJobVault, key); return eitherLocked.flatMap((_locked) => eitherSshKeyFile.flatMap((sshKeyFile) => eitherAnsibleSecretsFile.mapRight((secretsFile) => ({ job, sshKeyFile, secretsFile, })) ) ); }); }) .map(async (tEitherJobAndSecrets) => { const eitherJobAndSecrets = await tEitherJobAndSecrets.get(); return eitherJobAndSecrets.flatMapAsync( ({ job, sshKeyFile, secretsFile }) => { const volumes = [ `${job.arguments.path}:/ansible`, `${sshKeyFile}:/root/id_rsa`, `${secretsFile}:/ansible/secrets.yml`, ]; const playbookCmd = `ansible-playbook -e @secrets.yml ${job.arguments.playbooks}`; const deployCmd = [ "docker", "run", ...prependWith(volumes, "-v"), "willhallonline/ansible:latest", ...playbookCmd.split(" "), ]; tEitherJobAndSecrets.trace.trace( `running ansible magic~ (◕ᴗ◕✿) ${deployCmd}`, ); return tEitherJobAndSecrets.move(deployCmd).map(getStdout).get(); }, ); }) .get(); const saveToTempFile = (text: string): Promise> => Either.fromFailableAsync( () => Deno.makeTempDir({ dir: Deno.cwd() }) .then((dir) => Deno.makeTempFile({ dir })) .then(async (f) => { await Deno.writeTextFile(f, text); return f; }), );