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: 0so it only orchestrates. Configured entirely via 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 viaMCUXpressoInstallerCLI(see agents/mcuxpresso/Dockerfile and 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:
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:
/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
-
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
.shinstallers ->installers/mplabx/(see the README there)
- MCUXpresso Installer (
-
Generate the SSH keypair and admin password:
./scripts/setup-secrets.shThis writes
secrets/agent_ssh_key(+.pub) andsecrets/jenkins_admin_password.txt. Note the printed password - you'll need it to log in. Nothing insecrets/is committed to git. -
Build and start everything:
docker compose build docker compose up -d -
Visit http://localhost:8080 and log in as
adminwith the password from step 2. Under Manage Jenkins > Nodes, confirmmcuxpresso-agentandmplabx-agentcome 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 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--helplocally against the exact version you downloaded before trusting the build. - Install paths: the
PATHset 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 ninjaresolves 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 --helplists 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 (knownHostsstrategy) 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.