chore: capture Jenkins Docker baseline

This commit is contained in:
Brent Perteet
2026-08-27 16:08:01 +00:00
commit 9e1cce70e6
13 changed files with 539 additions and 0 deletions

121
README.md Normal file
View File

@@ -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/<name>/Dockerfile`, a matching
`installers/<name>/`, 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.