diff options
-rw-r--r-- | .ci/ci.ts | 1 | ||||
-rw-r--r-- | Dockerfile | 2 | ||||
-rw-r--r-- | hooks/Dockerfile | 2 | ||||
-rw-r--r-- | hooks/mod.ts | 5 | ||||
-rw-r--r-- | model/job.ts | 1 | ||||
-rw-r--r-- | utils/secret.ts | 7 | ||||
-rw-r--r-- | worker/scripts/ansible_playbook | 67 |
7 files changed, 79 insertions, 6 deletions
@@ -67,6 +67,7 @@ const getPipeline = () => { const thenDeploy: AnsiblePlaybookJob = { type: "ansible_playbook", arguments: { + path: "infra", playbooks: "playbooks/ci.yml", }, }; @@ -8,7 +8,7 @@ RUN cmake -B /opt/laminar/build -S /opt/laminar/src -G Ninja -DCMAKE_BUILD_TYPE= && cmake --build /opt/laminar/build \ && cmake --install /opt/laminar/build --strip -FROM denoland/deno:alpine as liz-ci +FROM denoland/deno:alpine AS liz-ci RUN apk add --no-cache capnproto sqlite-libs zlib curl COPY --from=laminar /usr/sbin/laminard /usr/sbin/laminard diff --git a/hooks/Dockerfile b/hooks/Dockerfile index a287e2f..61fc0f5 100644 --- a/hooks/Dockerfile +++ b/hooks/Dockerfile @@ -1,3 +1,3 @@ -FROM oci.liz.coffee/img/liz-ci:release as hooks +FROM oci.liz.coffee/img/liz-ci:release AS hooks CMD [ "deno", "run", "--allow-env", "--allow-net", "/app/hooks/mod.ts" ] diff --git a/hooks/mod.ts b/hooks/mod.ts index f432b72..a8f649c 100644 --- a/hooks/mod.ts +++ b/hooks/mod.ts @@ -1,9 +1,10 @@ #!/usr/bin/env -S deno run --allow-env --allow-net -import { getStdout, validateIdentifier } from "@liz-ci/utils"; +import { getStdout, validateIdentifier, getRequiredEnv } from "@liz-ci/utils"; -const addr = { port: 9000, hostname: "0.0.0.0" }; +getRequiredEnv("LAMINAR_HOST"); +const addr = { port: 9000, hostname: "0.0.0.0" }; Deno.serve(addr, async (req) => { const { pathname } = new URL(req.url); if (pathname === "/health") { diff --git a/model/job.ts b/model/job.ts index 96e0959..53d548f 100644 --- a/model/job.ts +++ b/model/job.ts @@ -32,6 +32,7 @@ export interface BuildDockerImageJob extends Job { } export interface AnsiblePlaybookJobProps extends JobArgT { + readonly path: string; readonly playbooks: string; } diff --git a/utils/secret.ts b/utils/secret.ts index 9847aa6..8860998 100644 --- a/utils/secret.ts +++ b/utils/secret.ts @@ -15,7 +15,7 @@ export class BitwardenSession { public async getItem<T extends LoginItem | SecureNote>( secretName: string, - ): Promise<T | undefined> { + ): Promise<T> { return await this.sessionInitializer.then((session) => getStdout(`bw list items`, { env: { @@ -24,7 +24,10 @@ export class BitwardenSession { }) ).then((items) => JSON.parse(items)).then((items) => items.find(({ name }: { name: string }) => name === secretName) - ); + ).then((item) => { + if (!item) throw new Error("Could not find bitwarden item " + secretName); + return item; + }); } async close(): Promise<void> { diff --git a/worker/scripts/ansible_playbook b/worker/scripts/ansible_playbook index e69de29..bfeeb8b 100644 --- a/worker/scripts/ansible_playbook +++ b/worker/scripts/ansible_playbook @@ -0,0 +1,67 @@ +#!/usr/bin/env -S deno run --allow-env --allow-net --allow-run --allow-read --allow-write + +import { + BitwardenSession, + getRequiredEnv, + getStdout, + type SecureNote, +} from "@liz-ci/utils"; +import type { AnsiblePlaybookJobProps } from "@liz-ci/model"; + +const args: AnsiblePlaybookJobProps = { + path: getRequiredEnv("path"), + playbooks: getRequiredEnv("playbooks"), +}; + +const tempKeyFile = await Deno.makeTempFile(); +const cwd = Deno.cwd(); +const bitwardenSession = new BitwardenSession(); + +try { + Deno.chdir(args.path); + + const { notes: ansibleSecrets } = await bitwardenSession.getItem<SecureNote>( + "ansible_secrets", + ); + await Deno.writeTextFile("secrets.yml", ansibleSecrets); + + const { notes: privateKey } = await bitwardenSession.getItem<SecureNote>( + "ssh_key", + ); + + // Create a temporary file for the SSH key + await Deno.writeTextFile(tempKeyFile, privateKey); + await getStdout(["chmod", "600", tempKeyFile]); + + // Start ssh-agent and add the key + const sshAgent = await getStdout(["ssh-agent", "-s"]); + const [SSH_AGENT_PID, SSH_AUTH_SOCK] = [ + /SSH_AGENT_PID=(\d+)/, + /SSH_AUTH_SOCK=([^;]+)/, + ] + .map((regex) => sshAgent.match(regex)?.[1]) + .map((val) => { + if (!val) throw new Error("Failed to start ssh-agent"); + return val; + }); + + const sshEnv = { + SSH_AGENT_PID, + SSH_AUTH_SOCK, + }; + await getStdout(["ssh-add", tempKeyFile], { + env: sshEnv, + }); + await getStdout([ + "ansible-playbook", + "-e", + "@secrets.yml", + ...args.playbooks.split(" "), + ], { env: sshEnv }); +} finally { + await Promise.allSettled([ + Deno.chdir.bind(null, cwd), + Deno.remove(tempKeyFile), + getStdout(["ssh-agent", "-k"]), + ]); +} |