commit 9e1cce70e6297287d88012c7299027129ea10c53 Author: Brent Perteet Date: Thu Aug 27 16:08:01 2026 +0000 chore: capture Jenkins Docker baseline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbe3551 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +secrets/* +!secrets/.gitkeep + +installers/**/* +!installers/**/ +!installers/**/README.md + +# Locally installed, versioned MCUXpresso SDK bundles +sdks/ + +*.log + +# Local AI/editor settings +.claude/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..453525f --- /dev/null +++ b/README.md @@ -0,0 +1,121 @@ +# Jenkins for firmware builds (MCUXpresso + MPLAB X) + +A general-purpose Jenkins controller plus two toolchain-specific build +agents, connected over SSH inside a private Docker network: + +- `jenkins-controller` - lean controller, no toolchains, `numExecutors: 0` + so it only orchestrates. Configured entirely via + [controller/casc/jenkins.yaml](controller/casc/jenkins.yaml) + (JCasC) - security realm, the SSH credential, and both agent node + definitions are declared there. +- `mcuxpresso-agent` - Ubuntu + NXP's Arm GNU Toolchain/CMake/Ninja, fetched + headlessly via `MCUXpressoInstallerCLI` (see + [agents/mcuxpresso/Dockerfile](agents/mcuxpresso/Dockerfile) and + [installers/mcuxpresso/README.md](installers/mcuxpresso/README.md) for how + that actually works - it's not a plain apt package). Jenkins label: + `mcuxpresso arm-gcc`. +- `mplabx-agent` - Ubuntu + MPLAB X IDE + XC8/XC16/XC32 compilers. + Jenkins label: `mplabx`. + +Point pipelines at a toolchain with `agent { label 'mcuxpresso' }` or +`agent { label 'mplabx' }`. + +## MCUXpresso SDKs + +SDKs are stored outside the agent container and mounted read-only from the +Docker host: + +```text +host: /home/brent/jenkins-docker/sdks +container: /opt/mcux-sdks +``` + +Keep each SDK in a versioned directory so pipelines can select one explicitly, +for example: + +```text +/home/brent/jenkins-docker/sdks/EVK-MIMXRT1020/25.06.00-8a1cd039 +``` + +Set `SdkRootDirPath` in a pipeline when it needs a path other than its project +default. The parent directory is mounted read-only, so adding or replacing an +SDK is an explicit host-side operation rather than a build side effect. +The `sdks/` directory is ignored by Git so initializing and pushing this +infrastructure directory does not upload the vendor SDK contents. + +## Why separate images + +Each vendor toolchain is large, versioned independently, and installed from +a manually-downloaded, license-gated installer. Keeping them out of the +controller means the controller stays small/disposable, and you can +rebuild/upgrade one toolchain without touching the other or restarting +Jenkins itself. + +## One-time setup + +1. **Get the vendor installers** (both require a free account/login to + *download*, so this step can't be automated in the Dockerfile - though + the actual component installs that follow don't need any further login): + - MCUXpresso Installer (`*.deb.bin`) -> `installers/mcuxpresso/` (see + the README there) + - MPLAB X + XC8/XC16/XC32 `.sh` installers -> `installers/mplabx/` (see + the README there) + +2. **Generate the SSH keypair and admin password**: + + ```bash + ./scripts/setup-secrets.sh + ``` + + This writes `secrets/agent_ssh_key` (+ `.pub`) and + `secrets/jenkins_admin_password.txt`. Note the printed password - you'll + need it to log in. Nothing in `secrets/` is committed to git. + +3. **Build and start everything**: + + ```bash + docker compose build + docker compose up -d + ``` + +4. Visit http://localhost:8080 and log in as `admin` with the password from + step 2. Under **Manage Jenkins > Nodes**, confirm `mcuxpresso-agent` and + `mplabx-agent` come online (they connect automatically via the SSH + launcher defined in JCasC - no manual node setup needed). + +## Things to verify before relying on this in production + +- **Silent-install flags**: the MPLAB X/XC installer commands in + [agents/mplabx/Dockerfile](agents/mplabx/Dockerfile) use + `--mode unattended --unattendedmodeui none`, the standard flags for + InstallBuilder-based installers, but Microchip hasn't guaranteed this + across every release. Run the installer with `--help` locally against the + exact version you downloaded before trusting the build. +- **Install paths**: the `PATH` set in that same Dockerfile assumes + `/opt/microchip/...`; Microchip has changed default install directories + across MPLAB X major versions, so confirm it matches what your installer + actually chose (check the build logs, or add `-D INSTALL_DIR=...` to pin + it explicitly). +- **MCUXpresso component versions**: the Dockerfile bakes in whatever + `mcux-cli install -c armToolchain cmake ninja` resolves to as "current" at + build time (verified working: Arm GNU Toolchain 14.2.1, CMake 3.30.0, + Ninja 1.13.2). `mcux-cli install --help` lists older versions too (e.g. + Arm GNU Toolchain 13.2.1) but a quick test showed `-c + armToolchain:13.2.1`-style pinning is rejected as an invalid choice - I + didn't find the right syntax for requesting a non-default version, so + for now you get whatever's newest. Worth checking NXP's own docs if you + need a pinned version for reproducibility. +- **Licensing**: XC8/XC16/XC32 free-tier compilers work out of the box; + Standard/Pro tiers need a license file or floating license server, which + isn't configured here. +- **Host key verification**: the controller's SSH launcher uses + `nonVerifyingKeyVerificationStrategy`, fine for an isolated local Docker + network but worth tightening (`knownHosts` strategy) if you deploy this + beyond your machine. + +## Adding more toolchains later + +Add a new directory under `agents//Dockerfile`, a matching +`installers//`, a service in `docker-compose.yml`, and a `permanent` +node block in `controller/casc/jenkins.yaml` with a distinct label - same +pattern as the two agents here. diff --git a/agents/mcuxpresso/Dockerfile b/agents/mcuxpresso/Dockerfile new file mode 100644 index 0000000..2e0259b --- /dev/null +++ b/agents/mcuxpresso/Dockerfile @@ -0,0 +1,83 @@ +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + openssh-server openjdk-17-jre-headless git make build-essential python3 \ + curl unzip ca-certificates xz-utils \ + # Electron runtime deps: MCUXpressoInstallerCLI ships as an Electron + # app and needs these even for headless/CLI use. + libglib2.0-0 libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \ + libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 \ + libpango-1.0-0 libcairo2 libasound2 libxshmfence1 libx11-6 libxext6 \ + libxrender1 libgtk-3-0 xvfb \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -m -s /bin/bash jenkins \ + && mkdir -p /home/jenkins/.ssh /var/run/sshd \ + && chmod 700 /home/jenkins/.ssh + +COPY secrets/agent_ssh_key.pub /home/jenkins/.ssh/authorized_keys +RUN chown -R jenkins:jenkins /home/jenkins/.ssh \ + && chmod 600 /home/jenkins/.ssh/authorized_keys + +# --- MCUXpresso Installer --------------------------------------------------- +# NXP no longer ships a plain IDE .deb - the download is "MCUXpresso +# Installer", a Makeself-wrapped Electron app (*.deb.bin) that itself +# contains a documented headless CLI (MCUXpressoInstallerCLI) for fetching +# individual packages/components non-interactively, no NXP account/login +# needed at download time. Get it from https://www.nxp.com/mcuxpresso and +# place it in installers/mcuxpresso/ before building. See +# installers/mcuxpresso/README.md. +COPY installers/mcuxpresso/ /tmp/installers/ +RUN set -e; \ + INSTALLER=$(ls /tmp/installers/*.deb.bin 2>/dev/null | head -n1); \ + if [ -z "$INSTALLER" ]; then \ + echo "ERROR: no MCUXpresso installer (*.deb.bin) found in installers/mcuxpresso/. See installers/mcuxpresso/README.md" >&2; \ + exit 1; \ + fi; \ + chmod +x "$INSTALLER"; \ + # --noexec: unpack the Makeself payload (install.sh + the real .deb) + # without running install.sh, which assumes an interactive desktop user + # and does things (desktop icons, chrome-sandbox setuid) we don't need + # in a headless CI agent. + "$INSTALLER" --target /tmp/mcux_pkg --noprogress --noexec --keep; \ + DEB=$(ls /tmp/mcux_pkg/*.deb | head -n1); \ + dpkg -x "$DEB" /opt/mcuxpresso-installer; \ + rm -rf /tmp/installers /tmp/mcux_pkg; \ + # The app writes its own logs/cache next to its binary (e.g. + # /logs/cli.log). Left root-owned, a non-root caller can't + # create that path and the app retries the failed write in an infinite + # loop instead of erroring out - so it must be writable by the jenkins + # user before anything ever invokes it. + chown -R jenkins:jenkins /opt/mcuxpresso-installer + +# The installer is Electron; running as root needs a setuid chrome-sandbox +# binary we don't bother setting up, and our CI user is unprivileged anyway, +# so just disable the sandbox instead. +ENV ELECTRON_DISABLE_SANDBOX=1 + +# MCUXpressoInstallerCLI still initializes Electron/Chromium under the hood +# and needs *a* display even for pure CLI output, so always drive it through +# a throwaway Xvfb instance. +RUN printf '#!/bin/sh\nexec xvfb-run -a /opt/mcuxpresso-installer/MCUXpressoInstaller/MCUXpressoInstallerCLI "$@"\n' \ + > /usr/local/bin/mcux-cli \ + && chmod +x /usr/local/bin/mcux-cli + +# Bake in a default toolchain so pipelines aren't hitting the network on +# every build. `docker run --rm mcux-cli install --help` lists every +# available -p/-c package/component and version if you want to change this. +USER jenkins +RUN mcux-cli install -c armToolchain cmake ninja; \ + ARMGCC_DIR=$(ls -d /home/jenkins/.mcuxpressotools/arm-gnu-toolchain-*-x86_64-arm-none-eabi | head -n1); \ + CMAKE_DIR=$(ls -d /home/jenkins/.mcuxpressotools/cmake-*-linux-x86_64 | head -n1); \ + NINJA_DIR=$(ls -d /home/jenkins/.mcuxpressotools/ninja-* | head -n1); \ + ln -s "$ARMGCC_DIR" /home/jenkins/.mcuxpressotools/arm-toolchain; \ + ln -s "$CMAKE_DIR" /home/jenkins/.mcuxpressotools/cmake; \ + ln -s "$NINJA_DIR" /home/jenkins/.mcuxpressotools/ninja; \ + test -x /home/jenkins/.mcuxpressotools/arm-toolchain/bin/arm-none-eabi-gcc +USER root + +ENV PATH="/home/jenkins/.mcuxpressotools/arm-toolchain/bin:/home/jenkins/.mcuxpressotools/cmake/bin:/home/jenkins/.mcuxpressotools/ninja:${PATH}" + +EXPOSE 22 +CMD ["/usr/sbin/sshd", "-D"] diff --git a/agents/mplabx/Dockerfile b/agents/mplabx/Dockerfile new file mode 100644 index 0000000..4758751 --- /dev/null +++ b/agents/mplabx/Dockerfile @@ -0,0 +1,72 @@ +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + openssh-server openjdk-17-jre-headless git make build-essential \ + curl unzip ca-certificates \ + libxext6 libxrender1 libxtst6 libxi6 libusb-1.0-0 \ + && rm -rf /var/lib/apt/lists/* +# libX* above: MPLAB X's NetBeans-based platform pulls these in even for +# headless/CLI use (mplab_ipe, make-based project builds). Drop them if you +# confirm your specific build path never touches the IDE runtime. +# libusb-1.0-0: required by the installer's own 64-bit library check (used +# for MPLAB IPE's device programming/debug tool support). + +RUN useradd -m -s /bin/bash jenkins \ + && mkdir -p /home/jenkins/.ssh /var/run/sshd \ + && chmod 700 /home/jenkins/.ssh + +COPY secrets/agent_ssh_key.pub /home/jenkins/.ssh/authorized_keys +RUN chown -R jenkins:jenkins /home/jenkins/.ssh \ + && chmod 600 /home/jenkins/.ssh/authorized_keys + +# --- MPLAB X IDE + XC8/XC16/XC32 compilers --------------------------------- +# Download these from https://www.microchip.com (account required) and place +# them in installers/mplabx/ before building this image. See +# installers/mplabx/README.md for expected filenames. +COPY installers/mplabx/ /tmp/installers/ +RUN set -e; \ + IDE_INSTALLER=$(ls /tmp/installers/MPLABX*.sh 2>/dev/null | head -n1); \ + if [ -z "$IDE_INSTALLER" ]; then \ + echo "ERROR: no MPLAB X installer (MPLABX-*-linux-installer.sh) found in installers/mplabx/. See installers/mplabx/README.md" >&2; \ + exit 1; \ + fi; \ + chmod +x "$IDE_INSTALLER"; \ + # These installers are makeself wrappers around an InstallBuilder elf + # installer: flags before "--" go to the makeself wrapper itself + # (--target, --nox11, ...); flags after "--" are passed through to the + # embedded installer (--mode, --unattendedmodeui, --installdir, ...). + # The wrapper's root check greps $USER (unset under plain `sh -c`, as + # Docker RUN uses) rather than trusting uid 0, so export it explicitly. + export USER=root; \ + "$IDE_INSTALLER" --nox11 -- --mode unattended --unattendedmodeui none --installdir /opt/microchip/mplabx; \ + # XC compiler installers ship in two different forms depending on + # version: a makeself-wrapped "*.sh" (same two-tier flag convention as + # the IDE installer above, taking --installdir) or a bare InstallBuilder + # elf "*.run" (flags passed directly, taking --prefix instead). Detect + # which by sniffing the first two bytes ("#!" vs the ELF magic). + for XC in /tmp/installers/xc8*.sh /tmp/installers/xc8*.run \ + /tmp/installers/xc16*.sh /tmp/installers/xc16*.run \ + /tmp/installers/xc32*.sh /tmp/installers/xc32*.run; do \ + [ -f "$XC" ] || continue; \ + chmod +x "$XC"; \ + XC_NAME=$(basename "$XC" | cut -d- -f1); \ + if [ "$(head -c2 "$XC")" = "#!" ]; then \ + "$XC" --nox11 -- --mode unattended --unattendedmodeui none --installdir "/opt/microchip/$XC_NAME"; \ + else \ + "$XC" --mode unattended --unattendedmodeui none --prefix "/opt/microchip/$XC_NAME"; \ + fi; \ + done; \ + rm -rf /tmp/installers + +ENV PATH="/opt/microchip/mplabx/mplab_platform/bin:/opt/microchip/xc8/bin:/opt/microchip/xc16/bin:/opt/microchip/xc32/bin:${PATH}" +# The IDE installer also symlinks its main executables (mplab_ide, mplab_ipe, +# mdb, prjMakefilesGenerator, projectPackager) into /usr/bin directly, so +# they work even without the mplab_platform/bin entry above. +# --installdir/--prefix above pin each install to an unversioned path, but +# the bin/ subdirectory layout inside it can still vary by installer version +# - if `docker compose run mplabx-agent which xc32-gcc` (or mdb, etc.) comes +# up empty, inspect /opt/microchip/*/ in the built image and adjust PATH. + +EXPOSE 22 +CMD ["/usr/sbin/sshd", "-D"] diff --git a/controller/Dockerfile b/controller/Dockerfile new file mode 100644 index 0000000..4e9dbe8 --- /dev/null +++ b/controller/Dockerfile @@ -0,0 +1,7 @@ +FROM jenkins/jenkins:lts-jdk17 + +COPY controller/plugins.txt /usr/share/jenkins/ref/plugins.txt +RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt + +ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false" +ENV CASC_JENKINS_CONFIG="/var/jenkins_home/casc_configs/jenkins.yaml" diff --git a/controller/casc/jenkins.yaml b/controller/casc/jenkins.yaml new file mode 100644 index 0000000..0aebf5e --- /dev/null +++ b/controller/casc/jenkins.yaml @@ -0,0 +1,73 @@ +jenkins: + systemMessage: "Firmware build Jenkins controller - configured via JCasC" + # Controller stays a pure orchestrator; real builds run on the labeled + # toolchain agents below. Bump this if you want the controller itself + # to run lightweight jobs (e.g. git checkout/fan-out stages). + numExecutors: 0 + securityRealm: + local: + allowsSignup: false + users: + - id: "${JENKINS_ADMIN_ID}" + password: "${JENKINS_ADMIN_PASSWORD}" + authorizationStrategy: + loggedInUsersCanDoAnything: + allowAnonymousRead: false + nodes: + - permanent: + name: "mcuxpresso-agent" + remoteFS: "/home/jenkins" + labelString: "mcuxpresso arm-gcc" + numExecutors: 2 + mode: EXCLUSIVE + launcher: + ssh: + host: "mcuxpresso-agent" + port: 22 + credentialsId: "agent-ssh-key" + launchTimeoutSeconds: 60 + maxNumRetries: 5 + retryWaitTime: 15 + sshHostKeyVerificationStrategy: + nonVerifyingKeyVerificationStrategy: {} + - permanent: + name: "mplabx-agent" + remoteFS: "/home/jenkins" + labelString: "mplabx" + numExecutors: 2 + mode: EXCLUSIVE + launcher: + ssh: + host: "mplabx-agent" + port: 22 + credentialsId: "agent-ssh-key" + launchTimeoutSeconds: 60 + maxNumRetries: 5 + retryWaitTime: 15 + sshHostKeyVerificationStrategy: + nonVerifyingKeyVerificationStrategy: {} + +credentials: + system: + domainCredentials: + - credentials: + - basicSSHUserPrivateKey: + scope: GLOBAL + id: "agent-ssh-key" + username: "jenkins" + privateKeySource: + directEntry: + privateKey: "${AGENT_SSH_PRIVATE_KEY}" + + - basicSSHUserPrivateKey: + scope: GLOBAL + id: "gitea-ultra2-app" + description: "Read-only deploy key for mate/ultra2-app" + username: "git" + privateKeySource: + directEntry: + privateKey: "${GITEA_ULTRA2_APP_PRIVATE_KEY}" + +unclassified: + location: + url: "https://jenkins.sparksoftdesign.com/" diff --git a/controller/plugins.txt b/controller/plugins.txt new file mode 100644 index 0000000..514d51e --- /dev/null +++ b/controller/plugins.txt @@ -0,0 +1,5 @@ +configuration-as-code +ssh-slaves +git +workflow-aggregator +credentials-binding diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..eb88ccf --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,56 @@ +services: + jenkins-controller: + build: + context: . + dockerfile: controller/Dockerfile + ports: + - "8090:8080" + environment: + JENKINS_ADMIN_ID: "admin" + secrets: + - source: agent_ssh_private_key + target: AGENT_SSH_PRIVATE_KEY + - source: gitea_ultra2_app_private_key + target: GITEA_ULTRA2_APP_PRIVATE_KEY + - source: jenkins_admin_password + target: JENKINS_ADMIN_PASSWORD + volumes: + - jenkins_home:/var/jenkins_home + - ./controller/casc:/var/jenkins_home/casc_configs:ro + networks: + - jenkins-net + + mcuxpresso-agent: + build: + context: . + dockerfile: agents/mcuxpresso/Dockerfile + hostname: mcuxpresso-agent + ports: + - "2222:22" + volumes: + - ./sdks:/opt/mcux-sdks:ro + - ./secrets/gitea_known_hosts:/etc/ssh/ssh_known_hosts:ro + networks: + - jenkins-net + + mplabx-agent: + build: + context: . + dockerfile: agents/mplabx/Dockerfile + hostname: mplabx-agent + networks: + - jenkins-net + +networks: + jenkins-net: + +volumes: + jenkins_home: + +secrets: + agent_ssh_private_key: + file: ./secrets/agent_ssh_key + gitea_ultra2_app_private_key: + file: ./secrets/gitea_ultra2_app_key + jenkins_admin_password: + file: ./secrets/jenkins_admin_password.txt diff --git a/installers/mcuxpresso/README.md b/installers/mcuxpresso/README.md new file mode 100644 index 0000000..43beaf7 --- /dev/null +++ b/installers/mcuxpresso/README.md @@ -0,0 +1,27 @@ +# MCUXpresso installer + +Download "MCUXpresso Installer" (not a plain IDE package - NXP replaced that +with an installer app) from https://www.nxp.com/mcuxpresso (NXP account +required) and place the Linux download in this directory before running +`docker compose build mcuxpresso-agent`. + +The file NXP ships is named something like `MCUXpressoInstaller.deb.bin` - a +Makeself self-extracting archive wrapping an Electron app, not a `.deb` you +can `apt install` directly. The Dockerfile unpacks it, pulls out the real +`.deb` inside, and uses the app's bundled `MCUXpressoInstallerCLI` binary to +fetch the toolchain non-interactively (no NXP login needed for the actual +component downloads - see the Dockerfile for how). It picks up the first +`*.deb.bin` it finds here, so you only need one file present at a time. + +By default the image bakes in the Arm GNU Toolchain, CMake, and Ninja (see +`agents/mcuxpresso/Dockerfile`). To install additional packages/components +(e.g. an MCUXpresso SDK bundle, LinkServer, SEGGER J-Link support), run: + +```bash +docker run --rm jenkins-docker-mcuxpresso-agent mcux-cli install --help +``` + +to see every available `-p`/`-c` choice, then add them to the `mcux-cli +install` line in the Dockerfile. + +This directory is gitignored except for this README. diff --git a/installers/mplabx/README.md b/installers/mplabx/README.md new file mode 100644 index 0000000..8248801 --- /dev/null +++ b/installers/mplabx/README.md @@ -0,0 +1,16 @@ +# MPLAB X installer + +Download the following Linux installers from +https://www.microchip.com/mplab/mplab-x-ide (Microchip account required) and +place them in this directory before running +`docker compose build mplabx-agent`: + +- `MPLABX--linux-installer.sh` (required) +- `xc8--full-install-linux-x64-installer.{sh,run}` (optional, if you need the XC8 compiler) +- `xc16--full-install-linux-installer.{sh,run}` (optional, if you need the XC16 compiler) +- `xc32--full-install-linux-installer.{sh,run}` (optional, if you need the XC32 compiler) + +The Dockerfile globs for these filename prefixes, so exact version numbers +don't matter. XC compiler installers ship as either a `.sh` (makeself- +wrapped) or a bare `.run` (ELF) installer depending on version - both are +handled. This directory is gitignored except for this README. diff --git a/nginx/jenkins.sparksoftdesign.com.conf b/nginx/jenkins.sparksoftdesign.com.conf new file mode 100644 index 0000000..5ad3d67 --- /dev/null +++ b/nginx/jenkins.sparksoftdesign.com.conf @@ -0,0 +1,42 @@ +server { + server_name jenkins.sparksoftdesign.com; + + client_max_body_size 500M; + + location / { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_request_buffering off; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $scheme; + + # Jenkins inbound WebSocket agents use this same endpoint. + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + } + + listen [::]:443 ssl ipv6only=on; # managed by Certbot + listen 443 ssl; # managed by Certbot + ssl_certificate /etc/letsencrypt/live/jenkins.sparksoftdesign.com/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/jenkins.sparksoftdesign.com/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot +} + +server { + if ($host = jenkins.sparksoftdesign.com) { + return 301 https://$host$request_uri; + } # managed by Certbot + + listen 80; + listen [::]:80; + server_name jenkins.sparksoftdesign.com; + return 404; # managed by Certbot +} diff --git a/scripts/setup-secrets.sh b/scripts/setup-secrets.sh new file mode 100755 index 0000000..2cb555e --- /dev/null +++ b/scripts/setup-secrets.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Generates the SSH keypair (controller -> agents) and the Jenkins admin +# password consumed by docker-compose.yml as Docker secrets. Safe to re-run; +# it skips anything that already exists. +set -euo pipefail +cd "$(dirname "$0")/.." + +mkdir -p secrets + +if [ ! -f secrets/agent_ssh_key ]; then + ssh-keygen -t ed25519 -f secrets/agent_ssh_key -N "" -C "jenkins-agent" + echo "Generated secrets/agent_ssh_key(.pub)" +else + echo "secrets/agent_ssh_key already exists, skipping" +fi + +if [ ! -f secrets/jenkins_admin_password.txt ]; then + openssl rand -base64 18 > secrets/jenkins_admin_password.txt + echo "Generated Jenkins admin password (saved to secrets/jenkins_admin_password.txt):" + cat secrets/jenkins_admin_password.txt +else + echo "secrets/jenkins_admin_password.txt already exists, skipping" +fi diff --git a/secrets/.gitkeep b/secrets/.gitkeep new file mode 100644 index 0000000..e69de29