From 66f3cc1380fc05db28050d16625eed8fbbe08725 Mon Sep 17 00:00:00 2001 From: robert Date: Tue, 7 Jul 2026 12:13:16 -0400 Subject: [PATCH] Adding Jenkinsfile to simplify my deployment. --- Jenkinsfile | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..900e7aa --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,137 @@ +// Jenkinsfile — build + publish both images to the thinkserver:5000 registry. +// +// ttrpg-tracker-firebase : root Dockerfile, nginx serving the static SPA, +// Firebase storage (config baked in at build time). +// ttrpg-tracker-sqlite : docker/Dockerfile, Caddy + Node, server/SQLite +// storage (REACT_APP_STORAGE=server). +// +// Requirements on the Jenkins agent: +// * Docker CLI available, agent user in the docker group (or DooD socket). +// * thinkserver:5000 reachable and trusted by the Docker daemon. If it is a +// plain-HTTP registry, add it to /etc/docker/daemon.json on the AGENT host: +// { "insecure-registries": ["thinkserver:5000"] } (then restart docker) +// +// Jenkins credentials this pipeline expects: +// * ttrpg-firebase-env-local (Secret file) — the .env.local contents with +// REACT_APP_FIREBASE_* values. Baked into the firebase image at build time. +// * thinkserver-registry (Username/pw) — OPTIONAL. Only needed if the +// registry requires auth; the login stage is guarded below. + +pipeline { + agent any + + options { + timestamps() + disableConcurrentBuilds() + timeout(time: 30, unit: 'MINUTES') + } + + parameters { + string(name: 'TRACKER_APP_ID', + defaultValue: 'ttrpg-initiative-tracker-default', + description: 'Firestore/app namespace baked into the SQLite image (REACT_APP_TRACKER_APP_ID).') + booleanParam(name: 'PUSH_LATEST', defaultValue: true, + description: 'Also tag and push :latest in addition to the commit-SHA tag.') + booleanParam(name: 'REGISTRY_AUTH', defaultValue: false, + description: 'Enable docker login using the thinkserver-registry credential.') + } + + environment { + REGISTRY = 'thinkserver:5000' + FIREBASE_IMAGE = "${REGISTRY}/ttrpg-tracker-firebase" + SQLITE_IMAGE = "${REGISTRY}/ttrpg-tracker-sqlite" + DOCKER_BUILDKIT = '1' + } + + stages { + stage('Checkout') { + steps { + checkout scm + script { + env.SHORT_SHA = sh(returnStdout: true, script: 'git rev-parse --short=8 HEAD').trim() + echo "Building tag ${env.SHORT_SHA} (build #${env.BUILD_NUMBER})" + } + } + } + + stage('Registry login') { + when { expression { return params.REGISTRY_AUTH } } + steps { + withCredentials([usernamePassword(credentialsId: 'thinkserver-registry', + usernameVariable: 'REG_USER', + passwordVariable: 'REG_PASS')]) { + sh 'echo "$REG_PASS" | docker login "$REGISTRY" -u "$REG_USER" --password-stdin' + } + } + } + + stage('Build firebase image') { + steps { + // Firebase config is compiled into the static bundle, so it must exist + // at build time. Inject .env.local from the Jenkins secret file, then + // the root Dockerfile's `COPY .env.local .env` picks it up. + withCredentials([file(credentialsId: 'ttrpg-firebase-env-local', variable: 'FIREBASE_ENV')]) { + sh ''' + cp "$FIREBASE_ENV" .env.local + docker build \ + -f Dockerfile \ + -t "$FIREBASE_IMAGE:$SHORT_SHA" \ + . + ''' + } + } + } + + stage('Build sqlite image') { + steps { + // Server/SQLite build. BuildKit is required (syntax directive + cache + // mount in docker/Dockerfile). Context is the repo root. + sh ''' + docker build \ + -f docker/Dockerfile \ + --build-arg REACT_APP_TRACKER_APP_ID="$TRACKER_APP_ID" \ + -t "$SQLITE_IMAGE:$SHORT_SHA" \ + . + ''' + } + } + + stage('Tag latest') { + when { expression { return params.PUSH_LATEST } } + steps { + sh ''' + docker tag "$FIREBASE_IMAGE:$SHORT_SHA" "$FIREBASE_IMAGE:latest" + docker tag "$SQLITE_IMAGE:$SHORT_SHA" "$SQLITE_IMAGE:latest" + ''' + } + } + + stage('Push') { + steps { + sh ''' + docker push "$FIREBASE_IMAGE:$SHORT_SHA" + docker push "$SQLITE_IMAGE:$SHORT_SHA" + ''' + script { + if (params.PUSH_LATEST) { + sh ''' + docker push "$FIREBASE_IMAGE:latest" + docker push "$SQLITE_IMAGE:latest" + ''' + } + } + } + } + } + + post { + always { + // Never leave Firebase secrets in the workspace or log out cleanly. + sh 'rm -f .env.local || true' + sh 'docker logout "$REGISTRY" || true' + } + success { + echo "Pushed ${FIREBASE_IMAGE}:${SHORT_SHA} and ${SQLITE_IMAGE}:${SHORT_SHA}" + } + } +}