Files
devops-lib-gcp/docs/tribal-knowledge.md
T
2026-08-26 02:02:24 +05:30

9.9 KiB
Raw Blame History

Tribal Knowledge

Non-obvious conventions, design decisions, and operational patterns in devops-lib that aren't captured in comments or standard docs. Authored manually from codebase review.


1. Five whitelists = five git clones per build

constructParam.groovy:getWhitelistedRepos does a fresh git clone of Meesho/whitelists for every whitelist check — there is no caching between calls. A build that hits all five gates (skip-sonar, app-config-disabled, multizone-enabled, allowedNonDevelopPrDeployment, ValidateCacConfig) clones the repo five times. This is deliberate: each clone captures the latest whitelist state so a DevOps change takes effect on the very next build without any library release. The cost is ~5× clone latency under GitHub rate-limiting.

Takeaway: Never refactor getWhitelistedRepos to cache the clone across calls without confirming the freshness guarantee is no longer required.


2. ringmaster-bot is the only signal that distinguishes Ringmaster from Turbo-Turtle

deployRingmaster.groovy:run switches the callback target based solely on getCause(UserIdCause).getUserId() == "ringmaster-bot". There is no explicit flag or env var. If Ringmaster ever renames its bot user, callbacks silently fall through to the Turbo-Turtle endpoint, which will reject them.

Takeaway: The string "ringmaster-bot" is a load-bearing constant. Don't change it without coordinating with the Ringmaster team.


3. buildObjHelper falls through to defaultBuild silently

buildObjHelper.groovy:run matches the toolchain field against a series of conditions (maven, gradle, go, node, python, php, docker). If none match, it instantiates defaultBuild without logging a warning. A mis-spelled toolchain value (e.g. golang instead of go) produces a silent no-op build that succeeds with no artifact.

Takeaway: If a build produces no Docker image but reports success, check toolchain spelling in deployment.yaml first.


4. JVM memory flags are auto-calculated — don't set them manually

deployArgoCD.groovy:update_helm_repo computes xms and xmx from the pod's memory_limit using the formula xms = xmx = memory_limit * 0.5. If a service hard-codes -Xmx in JAVA_OPTS, the auto-calculated value in the Helm values will collide, with the last one winning depending on JVM arg order.

Takeaway: Leave xms/xmx unset in service configs; let the pipeline compute them. If you must override, set jvm_memory_override: true in deployment.yaml to suppress auto-calc.


5. Canary is mandatory for sp0 and up0 — no override

deployArgoCD.groovy:run checks priority_v2 and blocks a non-canary prd deploy if the priority is sp0 or up0. There is no whitelist or flag to bypass this. The check happens before any Helm update, so the build fails fast.

Takeaway: Any service with priority_v2: sp0 or up0 must have canary configured in deployment.yaml. Attempting a direct prd deploy will always fail at the ArgoCD stage.


6. Turbo-Turtle callback uses a temp file to avoid shell escaping

deployRingmaster.groovy:run passes the Turbo-Turtle JSON payload inline via curl -d '$newCICD_JSON'.

Takeaway: If you add a new field to the Turbo-Turtle payload, add it to the temp-file write — never inline it in the curl command.


7. env.CHANGE_ID is the canonical PR-build detector

Every builder and stage that needs to distinguish a PR build from a branch build checks env.CHANGE_ID (set by the GitHub Branch Source plugin). constructParam.groovy:run remaps cicd_environment from prd to int for main/master-targeting PRs and from stg to ftr for develop-targeting PRs based on this. Don't check env.BRANCH_NAME =~ /PR-/; that pattern breaks on non-GitHub SCMs and on re-triggered builds.

Takeaway: Use env.CHANGE_ID to detect PR context, not branch name patterns.


8. Non-prd node pools are BU-scoped, not service-scoped

nodePoolSelection.groovy:run assigns stg/ftr/dev pods to {BU}-shared pools. All services in the same BU share one node pool in non-prd environments. A memory leak or noisy-neighbour in one supply service degrades all other supply services on staging.

Takeaway: Non-prd performance issues may be caused by a neighbour in the same BU pool, not the service under test.


9. The constructTemplate._construct() method is @NonCPS

constructTemplate.groovy:_construct() is annotated @NonCPS because it uses Java regex and string interpolation that is not serialisable by the Jenkins CPS engine. Any caller that invokes it inside a parallel block or closure must ensure the closure itself is either also @NonCPS or does not cross a serialisation boundary.

Takeaway: Don't move _construct into a CPS context (e.g. by inlining its logic into a stage body). Keep the @NonCPS annotation and call it from a CPS-safe wrapper.


11. Node build changes always require updating both buildNode.groovy and node-Dockerfile

src/com/meesho/stages/buildNode.groovy and resources/com/meesho/node-Dockerfile are paired files — the Dockerfile's else branch (the default install command, currently npm ci) must stay in sync with the detection/selection logic in buildNode.groovy. When npm_install_arg is not explicitly set in config.yaml, buildNode.groovy detects the package manager at runtime and passes the install command to the Dockerfile via the npm_install_arg template variable. The Dockerfile's default branch handles the fallback when no explicit or detected command is provided.

Takeaway: Any change to Node install logic (npm ci, npm install, pnpm detection) in buildNode.groovy must be paired with the same intent reflected in node-Dockerfile. Changing only one file leaves the two out of sync and silently breaks either the runtime path or the fallback.


12. The build-tools pod image version encodes the Go and sonar-scanner toolchain

resources/org/meesho/prd-pod.yaml and stg-pod.yaml reference a build-tools image from the internal GAR registry (e.g. asia-southeast1-docker.pkg.dev/meesho-devops-admin-0622/admin/devops/build-tools:lunar-v2.0.21). This image bundles the Go compiler, sonar-scanner-cli, and other language toolchain binaries. When a new toolchain binary is needed (e.g. a newer Go version or sonar-scanner), the fix is to bump the image tag in both pod YAMLs — not to download the tool at build time from go.dev or sonarsource.com. External downloads are a reliability risk behind a corporate network and are the wrong pattern for this repo.

Takeaway: If a build stage needs a new CLI tool or toolchain binary, bump the build-tools image tag in prd-pod.yaml + stg-pod.yaml. Never curl / wget a tool from the internet inside a pipeline stage.


13. Go sonar skip logic lives in constructParam.groovy::skipSonarCheckForGo()

Quality gate and sonar skip decisions for Go builds live in constructParam.groovy, not inline in buildGo.groovy. The pattern mirrors the existing skipSonarCheckForbidden() method: check getWhitelistedRepos("skip-sonar-whitelist") and env.BRANCH_NAME.contains("hotfix"), return true if either matches. This keeps all skip-sonar policy in one place. When adding Go sonar support, add skipSonarCheckForGo(Map config) to constructParam.groovy and call it from buildGo.groovy::buildDckr().

Takeaway: Sonar skip logic belongs in constructParam.groovy. Don't embed whitelist checks or hotfix branch checks inline in language build stages.


14. DinD sidecar DOCKER_HOST values must match the existing infrastructure endpoints

When creating a Docker-in-Docker sidecar pod, set DOCKER_HOST in the pod YAML to the pre-provisioned TCP service endpoints — do not use tcp://localhost:2375 (socket sharing). The correct values are:

Environment DOCKER_HOST
prd dind-prd-svc
stg / ftr dind-dev-new-svc.jenkins-new.svc.cluster.local

These are exactly the same values constructParam.groovy sets via accountDetails[env.cicd_environment]['dockerHost']. Because the pod YAML and constructParam agree on the value, no change to constructParam.groovy is needed when adding a sidecar pod. Using localhost creates a conflict that requires an env.SIDECAR_ENABLED guard — unnecessary complexity that the TCP endpoint approach eliminates.

Also: the DinD container image must come from the internal GAR registry: asia-southeast1-docker.pkg.dev/meesho-devops-admin-0622/admin/devops/docker:28-dind. Never use docker:N-dind from Docker Hub — it will be blocked by network policy and is not security-scanned.

Takeaway: DinD sidecar DOCKER_HOST = dind-prd-svc (prd) or dind-dev-new-svc.jenkins-new.svc.cluster.local (stg). DinD image = internal GAR docker:28-dind. No constructParam changes needed.


15. scm is unavailable inside Jenkins shared library code

The scm variable (branch, remote URL, credentials) is injected by the GitHub Branch Source plugin into consumer Jenkinsfiles only. It is not available inside vars/ or src/ of the shared library. Referencing scm.branches or scm.userRemoteConfigs in library code causes a MissingPropertyException at runtime.

Takeaway: Never access scm in vars/*.groovy or src/com/meesho/**/*.groovy. Use env.BRANCH_NAME, env.GIT_URL, or env.CHANGE_* variables instead — these are set by the plugin before library code runs.


10. ArgoCD app-of-apps must be refreshed before per-service sync

deployArgoCD.groovy:refresh_app_of_apps triggers a sync of the ArgoCD app-of-apps application before syncing the individual service app. Skipping this step means a newly created service (first deploy) won't have its ArgoCD Application object created yet, and the subsequent refresh_and_sync will target a non-existent app.

Takeaway: The four-step deploy order (update_argo_repo → refresh_app_of_apps → update_helm_repo → refresh_and_sync) is load-bearing. Steps 2 and 4 are not interchangeable.