diff options
author | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-10 16:57:03 -0700 |
---|---|---|
committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2025-05-10 16:57:03 -0700 |
commit | fa8f3f9465e87d499f7d6428323f496a884b7818 (patch) | |
tree | ab477dfa52ef30282029c4f136bf605cb24d67a9 /worker | |
download | ci-fa8f3f9465e87d499f7d6428323f496a884b7818.tar.gz ci-fa8f3f9465e87d499f7d6428323f496a884b7818.zip |
initial commit
Diffstat (limited to 'worker')
-rw-r--r-- | worker/Dockerfile | 20 | ||||
-rw-r--r-- | worker/deno.json | 4 | ||||
-rw-r--r-- | worker/jobs/checkout_ci.run | 39 | ||||
-rw-r--r-- | worker/mod.ts | 0 | ||||
-rw-r--r-- | worker/scripts/ansible_playbook | 0 | ||||
-rw-r--r-- | worker/scripts/build_image | 67 | ||||
-rw-r--r-- | worker/scripts/fetch_code | 6 | ||||
-rw-r--r-- | worker/scripts/run_pipeline | 28 |
8 files changed, 164 insertions, 0 deletions
diff --git a/worker/Dockerfile b/worker/Dockerfile new file mode 100644 index 0000000..e3a8f7b --- /dev/null +++ b/worker/Dockerfile @@ -0,0 +1,20 @@ +FROM oci.liz.coffee/img/liz-ci:release as worker + +RUN addgroup docker +RUN adduser -SDh /var/lib/laminar -g 'Laminar' -G users -G docker laminar + +# Secret retrieval +RUN apk add nodejs npm jq +RUN npm install -g @bitwarden/cli + +# Image building / publishing jobs +RUN apk add docker-cli + +# Ansible playbooks +RUN apk add ansible-core openssh + +USER laminar +WORKDIR /var/lib/laminar +EXPOSE 8080 + +CMD [ "/usr/sbin/laminard" ] diff --git a/worker/deno.json b/worker/deno.json new file mode 100644 index 0000000..5636d0a --- /dev/null +++ b/worker/deno.json @@ -0,0 +1,4 @@ +{ + "name": "@liz-ci/worker", + "exports": "./mod.ts" +} diff --git a/worker/jobs/checkout_ci.run b/worker/jobs/checkout_ci.run new file mode 100644 index 0000000..d47697d --- /dev/null +++ b/worker/jobs/checkout_ci.run @@ -0,0 +1,39 @@ +#!/bin/bash +# usage: laminarc run ci remote="ssh://src.liz.coffee:2222/cgit" rev="<sha>" \ +# refname="refs/..." + +set -e + +RUN=`date +%s` +WORKING_DIR=`$PWD/$RUN` + +mkdir -p "$WORKING_DIR" && cd "$WORKING_DIR" + +checkout="$rev" path="tmpsrc" fetch_code.sh + +if [[ ! -e "$WORKING_DIR/tmpsrc/.ci/ci.json" ]]; then + echo "No Continuous Integration configured for $remote." + exit 0 +fi + +PIPELINE_GENERATOR_PATH=$(jq -r '.pipeline' "$WORKING_DIR/tmpsrc/.ci/ci.json") +if [[ "$PIPELINE_GENERATOR_PATH" == *".."* ]]; then + echo "Error: Path contains '..'" + exit 1 +fi + +docker run --rm \ + --network none \ + --cap-drop ALL \ + --security-opt no-new-privileges \ + -v "$WORKING_DIR/tmpsrc/$PIPELINE_GENERATOR:/pipeline" \ + -e refname="$refname" \ + -e rev="$rev" \ + -e remote="$remote" \ + oci.liz.coffee/img/liz-ci:release \ + /pipeline \ + > "$WORKING_DIR/pipeline.json" + +rm -rf tmpsrc + +pipeline="$WORKING_DIR/pipeline.json" run_pipeline diff --git a/worker/mod.ts b/worker/mod.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/worker/mod.ts diff --git a/worker/scripts/ansible_playbook b/worker/scripts/ansible_playbook new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/worker/scripts/ansible_playbook diff --git a/worker/scripts/build_image b/worker/scripts/build_image new file mode 100644 index 0000000..ba1ec8f --- /dev/null +++ b/worker/scripts/build_image @@ -0,0 +1,67 @@ +#!/usr/bin/env -S deno run --allow-env --allow-net + +import type { BuildDockerImageJobProps } from "@liz-ci/model"; +import { + BitwardenSession, + getRequiredEnv, + getStdout, + type LoginItem, +} from "@liz-ci/utils"; + +const args: BuildDockerImageJobProps = { + registry: getRequiredEnv("registry"), + namespace: getRequiredEnv("namespace"), + repository: getRequiredEnv("repository"), + imageTag: getRequiredEnv("imageTag"), + + context: getRequiredEnv("context"), + dockerfile: getRequiredEnv("dockerfile"), + buildTarget: getRequiredEnv("buildTarget"), +}; + +const bitwardenSession = new BitwardenSession(); +const { username: registryUsername, password: registryPassword } = + (await bitwardenSession.getItem<LoginItem>(args.registry))?.login ?? {}; +if (!(registryUsername && registryPassword)) { + throw new Error("where's the login info bruh"); +} + +await getStdout( + [ + "docker", + "login", + "--username", + registryUsername, + "--password", + registryPassword, + args.registry, + ], +); + +const tag = + `${args.registry}/${args.namespace}/${args.repository}:${args.imageTag}`; +await getStdout( + [ + "docker", + "build", + "--target", + args.buildTarget, + "-t", + tag, + "-f", + `${args.dockerfile}`, + `${args.context}`, + ], + { + clearEnv: true, + env: {}, + }, +); + +await getStdout( + [ + "docker", + "push", + tag, + ], +); diff --git a/worker/scripts/fetch_code b/worker/scripts/fetch_code new file mode 100644 index 0000000..d45f6db --- /dev/null +++ b/worker/scripts/fetch_code @@ -0,0 +1,6 @@ +#!/bin/bash + +git clone "$remote" "$path" +cd "$path" +git reset --hard "$checkout" +cd - diff --git a/worker/scripts/run_pipeline b/worker/scripts/run_pipeline new file mode 100644 index 0000000..ad58573 --- /dev/null +++ b/worker/scripts/run_pipeline @@ -0,0 +1,28 @@ +#!/usr/bin/env -S deno --allow-env --allow-net --allow-read + +import { type Job, PipelineImpl } from "@liz-ci/model"; +import { getRequiredEnv, getStdout, validateIdentifier } from "@liz-ci/utils"; + +const stages = await (Deno.readTextFile(getRequiredEnv("pipeline"))) + .then(PipelineImpl.from) + .then((pipeline) => pipeline.getStages()); + +const validateJob = (job: Job) => { + Object.entries(job.arguments).forEach((e) => { + if (!e.every(validateIdentifier)) { + throw new Error(`job of type ${job.type} has invalid entry ${e}`); + } + }); +}; + +for (const stage of stages) { + await Promise.all( + stage.parallelJobs.map((job) => { + validateJob(job); + + return getStdout(job.type, { + env: job.arguments, + }); + }), + ); +} |