Files
devops-lib-gcp/README.md
T
Mukul SharmaandClaude Opus 5 6d743cbe48 Make the registry hostname configurable and drop the CA mount
buildDocker.groovy hardcoded harbor.35.238.248.203.nip.io as the push
target, so the registry could not move without editing this shared library
and every consumer moving in the same commit. It now reads
config.harbor_registry, whose default lives in homelabPipeline.groovy
beside harbor_project and every other key.

The stage errors rather than defaulting when the value is missing. Carrying
a second copy of the literal would leave two defaults free to disagree, and
an unset value would otherwise build an image named "null/<project>/<repo>"
— which docker accepts as a hostname and then fails to resolve, pointing
nowhere near the cause.

The dind pod no longer mounts the registry CA. That mount existed because
the registry was a nip.io name, which no public CA will issue for, so
cert-manager signed Harbor from a private CA; the node pool was told to
trust it for pulls, but a push comes from dockerd inside the build pod,
which has its own trust store. harbor.infra.deployshed.com carries a Let's
Encrypt certificate that both already trust, so the mount, its volume and
the whole arrangement go away rather than being repointed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N
2026-09-17 09:31:32 +05:30

6.8 KiB

devops-lib (GKE)

Jenkins Shared Library for the CI/CD pipeline on the GKE cluster. The GCP counterpart of the homelab repo of the same name, and a copy rather than a shared repo because several values here are cluster-specific in ways that would break the other cluster if crossed over.

Register it in Jenkins under the name devops-lib, exactly as on the homelab. Consuming repos then need no change at all — the same two-line Jenkinsfile works on either cluster, and which library it resolves to is a property of the Jenkins it runs on.

What differs from the homelab copy, all of it a consequence of GKE being a real cloud rather than one VM:

  • The registry hostname is harbor.infra.deployshed.com, in the push target (harbor_registry, see the table below) and in all five fallback Dockerfiles.
  • Harbor speaks TLS, with a public certificate. The homelab's dind passes --insecure-registry; here nothing is needed at all, because Let's Encrypt issues for the real domain and dockerd already trusts it.
  • helm_repo_url uses cluster DNS, since the clone happens inside a build pod. The homelab points it at an ingress hostname.
  • build-tools is not in this repo. It lives in devops-base-images-gcp alongside the mirrored base images, and is referenced here only by tag.

Everything else — the stage flow, the fallback templates, the hooks contract — is unchanged. The library was already adapted from a much larger, company-wide one; see git history for what was removed.

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_registry harbor.infra.deployshed.com The registry hostname images are pushed to and pulled from. Must be spelled identically here, in the dockerconfigjson auths key, and in Harbor's externalURL — docker matches both stored credentials and TLS trust by exact hostname, so a mismatch fails as unauthorized rather than as a name problem
harbor_project apps-registry Must already exist in Harbor, and be public unless you also wire an imagePullSecret — the app values assume anonymous pull. A push to a missing project fails as unauthorized: project <name> not found
helm_repo_url devops-helm-charts-gcp, over cluster DNS http://gitea-http.gitea.svc.cluster.local:3000/gitadmin/… — pod-to-pod, so it never leaves the cluster and comes back through the ingress
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 a config.yaml at its root, its keys are merged into the pipeline config (repo values win).
  • runHooks — reads config.yaml's hooks.pre_build/hooks.post_build lists, each {name, script, interpreter, requirements, blocking, timeout_seconds}. Blocking by default; blocking: false demotes a failure to advisory (log + continue). Script paths must be repo-relative (no .., no absolute paths).
  • buildDocker — uses the repo's own Dockerfile if present; otherwise renders one from resources/com/homelab/<lang>-Dockerfile based on dockerBuildVersion (e.g. go-1.22, node-20, python-3.12, java-21, php-8.3). All fallback templates pull base images from Harbor's base-images project (mirrored via the separate devops-base-images-gcp repo), not Docker Hub directly. Only versions actually mirrored there resolve — an unmirrored tag fails the build rather than silently falling back to Docker Hub.
  • updateHelmTag — clones devops-helm-charts, bumps the image tag via yq at image_tag_yq_path, commits, pushes to main.
  • syncArgoApp — calls the ArgoCD REST API to sync argo_app_name.

Adding a new language's fallback template

  1. Add the base image to devops-base-images/images.txt, re-mirror it into Harbor.
  2. Add resources/com/homelab/<lang>-Dockerfile, parametrized by ${version} (rendered via constructTemplate.groovy's SimpleTemplateEngine wrapper).
  3. Add a case for it in buildDocker.groovy's templates map.

Build-tools image

The docker-cli container runs harbor.infra.deployshed.com/base-images/build-tools:1, which bakes in git, yq, bash, python3 with pip and venv, and curl, so nothing is installed on demand on every build.

It is not built here. The Dockerfile lives in devops-base-images-gcp, next to the mirrored base images, because it is the same kind of artefact: built by hand, occasionally, and pushed to Harbor. A Jenkins job could not build it anyway — it is the image Jenkins builds in.

dind-pod.yaml pins the tag, so rebuilding the image rolls nothing out until that pin is bumped. Bump the tag rather than overwriting one.

Registry trust

Nothing to configure. Harbor is reached at harbor.infra.deployshed.com, which carries a publicly trusted Let's Encrypt certificate, so both containerd on the node (pulls) and dockerd in the build pod (pushes) accept it out of the box.

This used to be a real piece of setup, and the history is worth keeping because reintroducing a nip.io registry name would bring it all back. A nip.io address cannot have a public certificate — it is not on the public suffix list, and every *.nip.io certificate on the internet shares one rate limit — so cert-manager signed Harbor from a private CA instead. The node pool was told to trust that CA at creation, which covered pulls; the push came from dockerd inside the build pod, a separate client with its own trust store, so dind-pod.yaml had to mount the CA at /etc/docker/certs.d/<registry host>/ca.crt as well. The failure when that mount was missing was thoroughly confusing: pushes failed TLS verification while pulls of the very same image succeeded.