The previous fix (47a60b7) escaped the functional \$PHPIZE_DEPS
reference but added an explanatory comment that itself used several
bare/example $ characters (${PHPIZE_DEPS}, $PHPIZE_DEPS, $IDENTIFIER,
${IDENTIFIER}, and a bare $ before a comma). SimpleTemplateEngine
parses the ENTIRE file as template source, comments included, so any
of those threw either MissingPropertyException or a harder
'illegal string body character after dollar sign' parse error
depending on what followed the $.
Rewrote the comment to explain the rule in prose without using the $
glyph as an illustrative example at all, so future edits to the
comment can't reintroduce this. Verified via grep across all five
fallback Dockerfiles that the only remaining $ occurrences are the
intentional ${version} placeholders plus the one properly-escaped
\$PHPIZE_DEPS.
devops-lib
Jenkins Shared Library for this homelab's CI/CD pipeline. Adapted from a much larger, company-wide library — everything not needed for a single-node homelab (GKE/EKS, JFrog, S3, Sonar, org-specific BU/team validation, and ~65 other files covering languages/deploy-targets this setup never uses) has been removed rather than carried along unused; see git history if any of that is ever worth reviving.
Using it in a service repo
The entire Jenkinsfile is 2 lines:
@Library('devops-lib') _
homelabPipeline(repo_name: 'my-service')
repo_name is the only required key. Everything else has a sensible
default — override any of them by passing extra keys to homelabPipeline,
or by committing a config.yaml to the service repo's own root (merged
in after checkout; repo-committed values win over the Jenkinsfile call).
| Key | Default | Notes |
|---|---|---|
service_name |
repo_name |
Second path segment under devops-helm-charts/values/ |
argo_app_name |
repo_name |
Must match the ArgoCD Application's metadata.name |
harbor_project |
homelab |
Must be an existing, public Harbor project |
helm_repo_url |
devops-helm-charts on this Gitea |
— |
image_tag_yq_path |
.deployment.image.tag |
Override this if the app's chart isn't 1.0.0 — e.g. sts-2.0.0 uses .podtemplate.image.tag instead. Getting this wrong doesn't fail loudly: yq -i creates the path if missing rather than erroring, silently leaving the real field un-bumped. |
dockerBuildVersion |
none | Only read when the repo has no Dockerfile of its own — picks a fallback template (see below). No default; either ship a Dockerfile or set this. |
Pipeline stages
checkOut → loadConfig → runHooks(pre_build) → buildDocker → runHooks(post_build) → updateHelmTag → syncArgoApp → notify, all inside
a podTemplate (resources/org/homelab/dind-pod.yaml) via
node(POD_LABEL) { ... }.
loadConfig— if the repo has aconfig.yamlat its root, its keys are merged into the pipeline config (repo values win).runHooks— readsconfig.yaml'shooks.pre_build/hooks.post_buildlists, each{name, script, interpreter, requirements, blocking, timeout_seconds}. Blocking by default;blocking: falsedemotes a failure to advisory (log + continue). Script paths must be repo-relative (no.., no absolute paths).buildDocker— uses the repo's ownDockerfileif present; otherwise renders one fromresources/com/homelab/<lang>-Dockerfilebased ondockerBuildVersion(e.g.go-1.22,node-20,python-3.12,java-21,php-8.3). All fallback templates pull base images from Harbor'sbase-imagesproject (mirrored via the separatedevops-base-imagesrepo), not Docker Hub directly.updateHelmTag— clonesdevops-helm-charts, bumps the image tag viayqatimage_tag_yq_path, commits, pushes tomain.syncArgoApp— calls the ArgoCD REST API to syncargo_app_name.
Adding a new language's fallback template
- Add the base image to
devops-base-images/images.txt, re-mirror it into Harbor. - Add
resources/com/homelab/<lang>-Dockerfile, parametrized by${version}(rendered viaconstructTemplate.groovy'sSimpleTemplateEnginewrapper). - Add a case for it in
buildDocker.groovy'stemplatesmap.
Build-tools image
resources/org/homelab/build-tools.Dockerfile bakes git/yq/bash/
python3+pip/venv/curl into the docker-cli container's image, so
nothing gets installed on demand on every single build. Built and pushed
manually (not through any Jenkins job) — see that file's own header
comment.