# 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.