added files

This commit is contained in:
Your Name
2026-08-26 02:02:24 +05:30
parent 58ee8a276a
commit 3419cfba0c
200 changed files with 22132 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
<!--
Auto-generated by /meesho-init.
Regenerate by deleting this file and re-running the skill.
Do not duplicate content from docs/architecture.md, docs/tribal-knowledge.md,
docs/acronyms.md, or review-learnings.md — link to them instead.
-->
# devops-lib — Claude guide
Jenkins shared library that backs every Meesho service's CI/CD pipeline.
Loaded by Jenkins as a Global Library; consumed by service repos through a
`@Library('devops-lib')` import in their `Jenkinsfile`. There is **no local
build, test, or run command** — the library only executes inside Jenkins.
## What this repo is
- Code is **Groovy** (Jenkins CPS-style, with `@NonCPS` islands).
- `vars/*.groovy` are Jenkins shared-library **globals** — the entry points
service repos call (e.g. `buildPipeline { ... }`, `eksCICD { ... }`).
- `src/com/meesho/stages/**.groovy` — stage implementations dispatched from
the globals (`buildMaven`, `buildGo`, `deployArgoCD`, `notify`, …).
- `src/com/meesho/utilities/**.groovy` — shared helpers (`constructParam`,
`gitActions`, `nodePoolSelection`, `constructTemplate`).
- `resources/com/meesho/**` — per-language `Dockerfile`, `*-deployment.yaml`,
`*-values.yaml` templates rendered into the service repo at deploy time.
- `resources/org/meesho/**-pod.yaml` — Jenkins build-agent pod templates
selected by `env.INFRA_ENV` (`dev` / `stg` / `prd`).
## Architecture
Long-form architecture and data flow lives in
[`docs/architecture.md`](docs/architecture.md). Read it before changing any
file under `src/com/meesho/stages/` or `vars/`.
Pre-existing context worth reading:
- [`docs/acronyms.md`](docs/acronyms.md) — repo-specific acronyms (BU,
BUILDKIT, CAC, GCPP, INFRA, …).
- [`docs/tribal-knowledge.md`](docs/tribal-knowledge.md) — load-bearing
non-obvious conventions (whitelist git clones, JVM memory auto-calc, canary
enforcement, `ringmaster-bot` user-id switch, etc.).
- [`review-learnings.md`](review-learnings.md) — PR-review-derived rules,
some graduated into the NEVER DO section below.
- [`BUGS_AND_IMPROVEMENTS_REPORT.md`](BUGS_AND_IMPROVEMENTS_REPORT.md) —
catalogue of known bugs / debt; do not silently "fix" these without
reading the linked PR history.
## Quick reference
| Task | Command |
|---|---|
| Sanity-check Groovy syntax | `groovy -e 'load "vars/buildPipeline.groovy"'` (not wired in CI; use only as a local lint) |
| Validate Helm value templates | runs inside Jenkins via `validate_configs.py` (no standalone CLI) |
| Run the library | Cannot run locally — push a branch, point a Jenkins job at `@Library('devops-lib@<branch>')`, trigger from a service repo |
**No `Makefile`, no `build.gradle`, no `pom.xml`, no `Dockerfile`, no
`Jenkinsfile`, no `package.json`, no `.github/workflows/` at the repo root.**
A Jenkins job test-loads this library; there is no project-local equivalent.
**No test suite.** `BUGS_AND_IMPROVEMENTS_REPORT.md` flags this as a known
high-priority gap. Do not fabricate `./gradlew test` or similar — they do not
exist.
## Stack & tools
- **Groovy** — Jenkins CPS engine. Beware: any `@NonCPS` method must not be
called across a serialisation boundary (e.g., inside a `parallel` closure)
without the closure itself being `@NonCPS`.
- **Jenkins shared-library layout** — `vars/<name>.groovy` exposes `<name>`
as a global step; `src/com/meesho/**.groovy` is classpath-loaded.
- **Helm / ArgoCD** — service deploys go through
`deployArgoCD.groovy``update_helm_repo``refresh_app_of_apps`
`refresh_and_sync`. Step order is load-bearing
([tribal-knowledge §10](docs/tribal-knowledge.md)).
- **Turbo-Turtle / Ringmaster** — `deployRingmaster.groovy` routes callbacks
based purely on `getCause(UserIdCause).getUserId() == "ringmaster-bot"`.
Renaming that user id silently breaks the routing.
- **Validation** — `resources/com/meesho/validate_configs.py` (1207 lines) is
a monolithic Python validator invoked from Groovy via `sh`.
- **Whitelists** — `getWhitelistedRepos()` does a fresh `git clone` of
`Meesho/whitelists` **on every call** (no caching). Five whitelist gates
each clone independently — this is intentional, do not refactor.
## Critical conventions
1. **`vars/` is the public API.** Adding a new entry point means adding a new
file under `vars/`. Renaming or removing a `vars/` global is a breaking
change for every service Jenkinsfile.
2. **`build_tool` switch lives in `src/com/meesho/stages/buildObjHelper.groovy`.**
Supported values today are `maven`, `gradle`, `docker`, `python-*`,
`node-*`, `go*`, `php`. There is no `sbt`, no `rust`. Unknown values fall
through to `defaultBuild` silently.
3. **`@NonCPS` rule.** `constructTemplate._construct()` is annotated
`@NonCPS` because it uses `groovy.text.SimpleTemplateEngine` (Java,
non-serialisable). Keep it `@NonCPS` and call it from a CPS-safe wrapper.
4. **JVM memory auto-calc.** `update_helm_repo` derives `xms == xmx ==
memory_limit * 0.5`. Do not hard-code `-Xmx` in `JAVA_OPTS`.
5. **Canary is mandatory for `priority_v2: sp0` / `up0` in prd.** No
whitelist, no bypass. The build fails fast before the Helm update.
6. **PR vs branch detection.** Use `env.CHANGE_ID` (set by the GitHub Branch
Source plugin), never `env.BRANCH_NAME =~ /PR-/`.
7. **Hard-coded `'Meesho'` org.** Many helpers in `gitActions.groovy` /
`constructParam.groovy` embed the GitHub org name as a literal — keep this
stable. Do not parametrise without a coordinated rollout.
8. **Pod selection.** `env.INFRA_ENV` selects the Jenkins agent pod template
via `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")`. Don't add
inline `podTemplate` blocks — they bypass the central agent inventory.
## NEVER DO
These rules come from PR-review history (`review-learnings.md`) and
post-incident notes (`docs/tribal-knowledge.md`). Each links to its source.
- **Never refactor `getWhitelistedRepos` to cache clones across calls**
without confirming the freshness guarantee is no longer required
([tribal-knowledge §1](docs/tribal-knowledge.md)).
- **Never rename `"ringmaster-bot"`** in `deployRingmaster.groovy` without
coordinating with the Ringmaster team — it's a load-bearing string
([tribal-knowledge §2](docs/tribal-knowledge.md)).
- **Never set `xms` / `xmx` manually in service Helm values** — the pipeline
computes them from `memory_limit`; manual values collide
([tribal-knowledge §4](docs/tribal-knowledge.md)).
- **Never print or `cat` an SSH private key to stdout / logs**
(review-learnings `P0_NO_PRIVATE_KEY_LEAK`, PR #634). Always
`withCredentials { ... }` and write to a 0600 file.
- **Never hard-code bare IPs as curl/HTTP targets in pipeline code.**
(review-learnings `P0_HARDCODED_IP_IN_PIPELINE`, PR #681; known existing
exception: `securityScan.groovy:11` flagged for remediation.) Use
DNS-resolvable hostnames.
- **Never bypass canary on `sp0`/`up0` prd deploys**
([tribal-knowledge §5](docs/tribal-knowledge.md)).
- **Never inline the Turbo-Turtle JSON payload in `curl` differently from
the current `-d '$newCICD_JSON'` pattern** without re-verifying bash
escaping ([tribal-knowledge §6](docs/tribal-knowledge.md)).
- **Never use `--no-verify` on `git commit`** — pre-commit hooks include
TruffleHog secret scanning and CAC validation. Bypassing is a P0
policy violation.
- **Never commit to `main` or `develop` directly** — those branches are
protected; all changes go via PR.
## Where to ask
- `#ci-cd-status` Slack channel — pipeline failures, library questions.
- `@maintainer` Slack handle is required in every pipeline `param` map
(consumed by `src/com/meesho/stages/notify.groovy:9`).
## Pointers
- [docs/architecture.md](docs/architecture.md) — full module-by-module map.
- [docs/acronyms.md](docs/acronyms.md) — repo-specific abbreviations.
- [docs/tribal-knowledge.md](docs/tribal-knowledge.md) — non-obvious patterns.
- [review-learnings.md](review-learnings.md) — PR-review-derived rules,
graduation candidates for this file's NEVER DO section.
- [review.md](review.md) — review process / rubric.
- [BUGS_AND_IMPROVEMENTS_REPORT.md](BUGS_AND_IMPROVEMENTS_REPORT.md) — known
bug catalogue; check before "fixing" anything that looks suspect.
+188
View File
@@ -0,0 +1,188 @@
# DevOps Library - Bugs and Improvements Report - Generated By Cursor AI
## Executive Summary
This report documents critical bugs, security vulnerabilities, and improvement opportunities found in the Meesho DevOps Jenkins shared library. The codebase shows signs of technical debt with multiple critical issues that need immediate attention.
## Critical Bugs (High Priority)
### 1. **Hard-coded Branch Name Bug** 🔴
- **File**: `src/com/meesho/stages/buildGradle.groovy`
- **Line**: 252
- **Issue**: `branch_name = 'repo'` - Hard-coded instead of using actual branch name
- **Impact**: JFrog deployment will always think it's not on master/main branch
- **Fix**: Change to `branch_name = "${env.BRANCH_NAME}"`
### 2. **Method Name Typo** 🔴
- **File**: `src/com/meesho/stages/checkOut.groovy`
- **Line**: 22
- **Issue**: Method name `chekoutSubmodule` should be `checkoutSubmodule`
- **Impact**: Will cause runtime errors if this method is called
- **Fix**: Rename method to correct spelling
### 3. **XML Query on Non-XML Files** 🔴
- **File**: `src/com/meesho/stages/buildGradle.groovy`
- **Lines**: Multiple locations
- **Issue**: Using `xq` (XML query) command on `build.gradle` files
- **Impact**: Will fail as Gradle files are not XML format
- **Fix**: Parse Gradle files appropriately or use Gradle APIs
### 4. **Exception Type Typo** 🔴
- **File**: `src/com/meesho/stages/helmGenerator.groovy`
- **Line**: 102
- **Issue**: `catch (Exceptione)` - Typo in Exception class name
- **Impact**: Syntax error, code won't compile
- **Fix**: Change to `catch (Exception e)`
### 5. **Incomplete JDK Version Handling** 🟡
- **File**: `vars/onlyPushtoJfrog.groovy`
- **Lines**: 48-53
- **Issue**: Only handles JDK 8 and 17, but allows JDK 11 and 21 in parameters
- **Impact**: JDK 11 and 21 users will default to JDK 8
- **Fix**: Add cases for all supported JDK versions
### 6. **Duplicate Map Key** 🟡
- **File**: `src/com/meesho/stages/buildPython.groovy`
- **Lines**: 76-79
- **Issue**: `buildRegistry` key defined twice in same map
- **Impact**: First value will be overwritten
- **Fix**: Remove duplicate key
## Security Vulnerabilities (Critical)
### 1. **Hard-coded IP Address** 🔴
- **File**: `src/com/meesho/stages/securityScan.groovy`
- **Line**: 12
- **Issue**: Hard-coded IP `172.31.5.29:63232`
- **Impact**: Security risk, inflexible configuration
- **Fix**: Move to configuration/environment variable
### 2. **Password Exposure in Process List** 🔴
- **Files**: Multiple locations
- **Issue**: ECR and ArgoCD login commands expose passwords
- **Examples**:
```groovy
sh "aws ecr get-login-password | docker login --password-stdin"
sh "argocd login --password ${ARGO_PASSWORD}"
```
- **Impact**: Passwords visible in process list and logs
- **Fix**: Use secure credential handling methods
### 3. **SSH Key Security Issues** 🔴
- **File**: `src/com/meesho/utilities/addSSHKey.groovy`
- **Issues**:
- SSH key written to file before permissions are set (race condition)
- No cleanup of SSH key file after use
- Key stored in plaintext
- **Fix**: Set permissions atomically, ensure cleanup, use agent forwarding
### 4. **Unsafe File Deletion** 🟡
- **Multiple files**
- **Issue**: `rm -rf *` commands without safeguards
- **Impact**: Could delete unintended files
- **Fix**: Use specific file paths, add safety checks
## Code Quality Issues
### 1. **Security Scan Disabled** 🔴
- **File**: `vars/buildPipeline.groovy`
- **Line**: 23
- **Issue**: Security scan is commented out
- **Impact**: No security validation in CI/CD pipeline
- **Fix**: Re-enable or remove with proper documentation
### 2. **Incomplete Implementation** 🟡
- **File**: `vars/cloudFunctionCICD.groovy`
- **Issue**: Just prints "Hello World"
- **Impact**: Feature not functional
- **Fix**: Complete implementation or remove
### 3. **Generic Exception Handling** 🟡
- **Throughout codebase**
- **Issue**: Catching generic `Exception` everywhere
- **Impact**: Hides specific errors, makes debugging difficult
- **Fix**: Catch specific exceptions
### 4. **Inconsistent Null Checking** 🟡
- **Throughout codebase**
- **Issue**: Mix of `== null`, `!= null`, and no safe navigation
- **Fix**: Use Groovy's safe navigation operator (`?.`)
## Architectural Improvements
### 1. **No Test Coverage** 🔴
- **Issue**: No unit or integration tests
- **Impact**: High risk of regressions
- **Fix**: Implement comprehensive test suite
### 2. **Monolithic Python Script** 🟡
- **File**: `resources/com/meesho/validate_configs.py`
- **Issue**: 1207 lines in single file
- **Fix**: Refactor into modules
### 3. **Hard-coded Values** 🟡
- **Throughout codebase**
- **Issues**:
- GitHub org "Meesho" hard-coded
- Node names like "slave02"
- Various URLs and endpoints
- **Fix**: Move to configuration
### 4. **No Retry Mechanism** 🟡
- **Issue**: Retry mechanisms are inconsistently applied - present in ArgoCD sync (--http-retry-max 3) and Docker push (retryDockerPush) but absent from most other critical operations.
- **Impact**: Transient failures cause pipeline failures
- **Fix**: Implement retry with exponential backoff
### 5. **Inconsistent Logging** 🟡
- **Issue**: Mix of `log.info()`, `echo`, and print statements
- **Fix**: Standardize logging approach
## Technical Debt
### 1. **Commented Code** 🟡
- **Throughout codebase**
- **Issue**: Large blocks of commented code
- **Fix**: Remove or document why it's kept
### 2. **TODO Comments** 🟡
- **Multiple files**
- **Issue**: TODO comments without action plans
- **Fix**: Create tickets or implement
### 3. **Inconsistent Error Handling** 🟡
- **Issue**: Some methods set `env.msg`, others don't
- **Fix**: Standardize error handling pattern
## Recommendations
### Immediate Actions (P0)
1. Fix the hard-coded branch name bug in buildGradle.groovy
2. Fix method name typo in checkOut.groovy
3. Fix exception typo in helmGenerator.groovy
4. Address security vulnerabilities (passwords, SSH keys)
5. Re-enable or properly remove security scanning
### Short-term (P1)
1. Fix XML query usage on Gradle files
2. Complete JDK version handling
3. Remove duplicate map keys
4. Implement proper credential handling
5. Add safety checks to file deletion commands
### Medium-term (P2)
1. Implement comprehensive test coverage
2. Refactor large Python script
3. Create configuration management system
4. Standardize error handling and logging
5. Implement retry mechanisms
### Long-term (P3)
1. Remove all hard-coded values
2. Clean up technical debt (commented code, TODOs)
3. Implement proper monitoring and alerting
4. Create comprehensive documentation
5. Consider migrating to more modern CI/CD patterns
## Conclusion
The codebase requires significant attention to address critical bugs and security vulnerabilities. While functional, it shows signs of organic growth without proper architecture governance. Implementing the recommended fixes will greatly improve reliability, security, and maintainability of the DevOps library.
+156
View File
@@ -0,0 +1,156 @@
<!--
Auto-generated by /meesho-init.
Regenerate by deleting this file and re-running the skill.
Do not duplicate content from docs/architecture.md, docs/tribal-knowledge.md,
docs/acronyms.md, or review-learnings.md — link to them instead.
-->
# devops-lib — Claude guide
Jenkins shared library that backs every Meesho service's CI/CD pipeline.
Loaded by Jenkins as a Global Library; consumed by service repos through a
`@Library('devops-lib')` import in their `Jenkinsfile`. There is **no local
build, test, or run command** — the library only executes inside Jenkins.
## What this repo is
- Code is **Groovy** (Jenkins CPS-style, with `@NonCPS` islands).
- `vars/*.groovy` are Jenkins shared-library **globals** — the entry points
service repos call (e.g. `buildPipeline { ... }`, `eksCICD { ... }`).
- `src/com/meesho/stages/**.groovy` — stage implementations dispatched from
the globals (`buildMaven`, `buildGo`, `deployArgoCD`, `notify`, …).
- `src/com/meesho/utilities/**.groovy` — shared helpers (`constructParam`,
`gitActions`, `nodePoolSelection`, `constructTemplate`).
- `resources/com/meesho/**` — per-language `Dockerfile`, `*-deployment.yaml`,
`*-values.yaml` templates rendered into the service repo at deploy time.
- `resources/org/meesho/**-pod.yaml` — Jenkins build-agent pod templates
selected by `env.INFRA_ENV` (`dev` / `stg` / `prd`).
## Architecture
Long-form architecture and data flow lives in
[`docs/architecture.md`](docs/architecture.md). Read it before changing any
file under `src/com/meesho/stages/` or `vars/`.
Pre-existing context worth reading:
- [`docs/acronyms.md`](docs/acronyms.md) — repo-specific acronyms (BU,
BUILDKIT, CAC, GCPP, INFRA, …).
- [`docs/tribal-knowledge.md`](docs/tribal-knowledge.md) — load-bearing
non-obvious conventions (whitelist git clones, JVM memory auto-calc, canary
enforcement, `ringmaster-bot` user-id switch, etc.).
- [`review-learnings.md`](review-learnings.md) — PR-review-derived rules,
some graduated into the NEVER DO section below.
- [`BUGS_AND_IMPROVEMENTS_REPORT.md`](BUGS_AND_IMPROVEMENTS_REPORT.md) —
catalogue of known bugs / debt; do not silently "fix" these without
reading the linked PR history.
## Quick reference
| Task | Command |
|---|---|
| Sanity-check Groovy syntax | `groovy -e 'load "vars/buildPipeline.groovy"'` (not wired in CI; use only as a local lint) |
| Validate Helm value templates | runs inside Jenkins via `validate_configs.py` (no standalone CLI) |
| Run the library | Cannot run locally — push a branch, point a Jenkins job at `@Library('devops-lib@<branch>')`, trigger from a service repo |
**No `Makefile`, no `build.gradle`, no `pom.xml`, no `Dockerfile`, no
`Jenkinsfile`, no `package.json`, no `.github/workflows/` at the repo root.**
A Jenkins job test-loads this library; there is no project-local equivalent.
**No test suite.** `BUGS_AND_IMPROVEMENTS_REPORT.md` flags this as a known
high-priority gap. Do not fabricate `./gradlew test` or similar — they do not
exist.
## Stack & tools
- **Groovy** — Jenkins CPS engine. Beware: any `@NonCPS` method must not be
called across a serialisation boundary (e.g., inside a `parallel` closure)
without the closure itself being `@NonCPS`.
- **Jenkins shared-library layout** — `vars/<name>.groovy` exposes `<name>`
as a global step; `src/com/meesho/**.groovy` is classpath-loaded.
- **Helm / ArgoCD** — service deploys go through
`deployArgoCD.groovy``update_helm_repo``refresh_app_of_apps`
`refresh_and_sync`. Step order is load-bearing
([tribal-knowledge §10](docs/tribal-knowledge.md)).
- **Turbo-Turtle / Ringmaster** — `deployRingmaster.groovy` routes callbacks
based purely on `getCause(UserIdCause).getUserId() == "ringmaster-bot"`.
Renaming that user id silently breaks the routing.
- **Validation** — `resources/com/meesho/validate_configs.py` (1207 lines) is
a monolithic Python validator invoked from Groovy via `sh`.
- **Whitelists** — `getWhitelistedRepos()` does a fresh `git clone` of
`Meesho/whitelists` **on every call** (no caching). Five whitelist gates
each clone independently — this is intentional, do not refactor.
## Critical conventions
1. **`vars/` is the public API.** Adding a new entry point means adding a new
file under `vars/`. Renaming or removing a `vars/` global is a breaking
change for every service Jenkinsfile.
2. **`build_tool` switch lives in `src/com/meesho/stages/buildObjHelper.groovy`.**
Supported values today are `maven`, `gradle`, `docker`, `python-*`,
`node-*`, `go*`, `php`. There is no `sbt`, no `rust`. Unknown values fall
through to `defaultBuild` silently.
3. **`@NonCPS` rule.** `constructTemplate._construct()` is annotated
`@NonCPS` because it uses `groovy.text.SimpleTemplateEngine` (Java,
non-serialisable). Keep it `@NonCPS` and call it from a CPS-safe wrapper.
4. **JVM memory auto-calc.** `update_helm_repo` derives `xms == xmx ==
memory_limit * 0.5`. Do not hard-code `-Xmx` in `JAVA_OPTS`.
5. **Canary is mandatory for `priority_v2: sp0` / `up0` in prd.** No
whitelist, no bypass. The build fails fast before the Helm update.
6. **PR vs branch detection.** Use `env.CHANGE_ID` (set by the GitHub Branch
Source plugin), never `env.BRANCH_NAME =~ /PR-/`.
7. **Hard-coded `'Meesho'` org.** Many helpers in `gitActions.groovy` /
`constructParam.groovy` embed the GitHub org name as a literal — keep this
stable. Do not parametrise without a coordinated rollout.
8. **Pod selection.** `env.INFRA_ENV` selects the Jenkins agent pod template
via `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")`. Don't add
inline `podTemplate` blocks — they bypass the central agent inventory.
## NEVER DO
These rules come from PR-review history (`review-learnings.md`) and
post-incident notes (`docs/tribal-knowledge.md`). Each links to its source.
- **Never refactor `getWhitelistedRepos` to cache clones across calls**
without confirming the freshness guarantee is no longer required
([tribal-knowledge §1](docs/tribal-knowledge.md)).
- **Never rename `"ringmaster-bot"`** in `deployRingmaster.groovy` without
coordinating with the Ringmaster team — it's a load-bearing string
([tribal-knowledge §2](docs/tribal-knowledge.md)).
- **Never set `xms` / `xmx` manually in service Helm values** — the pipeline
computes them from `memory_limit`; manual values collide
([tribal-knowledge §4](docs/tribal-knowledge.md)).
- **Never print or `cat` an SSH private key to stdout / logs**
(review-learnings `P0_NO_PRIVATE_KEY_LEAK`, PR #634). Always
`withCredentials { ... }` and write to a 0600 file.
- **Never hard-code bare IPs as curl/HTTP targets in pipeline code.**
(review-learnings `P0_HARDCODED_IP_IN_PIPELINE`, PR #681; known existing
exception: `securityScan.groovy:11` flagged for remediation.) Use
DNS-resolvable hostnames.
- **Never bypass canary on `sp0`/`up0` prd deploys**
([tribal-knowledge §5](docs/tribal-knowledge.md)).
- **Never inline the Turbo-Turtle JSON payload in `curl` differently from
the current `-d '$newCICD_JSON'` pattern** without re-verifying bash
escaping ([tribal-knowledge §6](docs/tribal-knowledge.md)).
- **Never use `--no-verify` on `git commit`** — pre-commit hooks include
TruffleHog secret scanning and CAC validation. Bypassing is a P0
policy violation.
- **Never commit to `main` or `develop` directly** — those branches are
protected; all changes go via PR.
## Where to ask
- `#ci-cd-status` Slack channel — pipeline failures, library questions.
- `@maintainer` Slack handle is required in every pipeline `param` map
(consumed by `src/com/meesho/stages/notify.groovy:9`).
## Pointers
- [docs/architecture.md](docs/architecture.md) — full module-by-module map.
- [docs/acronyms.md](docs/acronyms.md) — repo-specific abbreviations.
- [docs/tribal-knowledge.md](docs/tribal-knowledge.md) — non-obvious patterns.
- [review-learnings.md](review-learnings.md) — PR-review-derived rules,
graduation candidates for this file's NEVER DO section.
- [review.md](review.md) — review process / rubric.
- [BUGS_AND_IMPROVEMENTS_REPORT.md](BUGS_AND_IMPROVEMENTS_REPORT.md) — known
bug catalogue; check before "fixing" anything that looks suspect.
+65
View File
@@ -1,2 +1,67 @@
# devops-lib
---
## Parameters
Most of the functionality depends on the parameters provided by the users in form of groovy map of key and value pairs. The supported parameters are as below:
### Required parameters
**repo_name**: The key repo_name is required for checking out the code in a subdirectory. The value is the repository name that you want to checkout
**build_tool**: This parameter is required to identify which build_tool to use in the pipeline. The supported values are *maven*, *gradle*, *docker*, *python*, *node*, *go*, *php* (and their prefixed variants such as *maven-3.3-jdk-17*, *python-3*, *node-16*, *go1.21*)
**maintainer** : This parameter is required to send the notification in the slack channel *#ci-cd-status*. Please provide your slack username here
### Optional parameter
`devops-lib` is Meesho's Jenkins Shared Library that provides a unified CI/CD pipeline for all microservices across the organisation. Consumer repos load it via `@Library('devops-lib@main')` and call a single `eksCICD(repo)` entry point — the library handles language-specific building (Maven, Go, Gradle, Node.js, Python, PHP), code quality gates (Sonar), Docker image publishing to GAR/ECR, Helm chart updates, and ArgoCD-based deployment to GKE/EKS clusters. Build status and deployment metadata are reported back to Ringmaster and Slack.
**Stack:** Groovy (Jenkins Shared Library) · ArgoCD · Helm · GCP (GKE, GAR, GCS, Vault, Sonar) · AWS (EKS, ECR, S3)
## Dependencies
**push_to_jfrog**: By default master, main, gcp-main, and gcp-master branches push artifacts to jfrog/s3 repository, set this parameter to true to push artifacts from non-master branches
---
## config.yaml schema (consumer services)
Every service that uses this library must provide a `config.yaml`:
| Key | Required | Description |
|-----|----------|-------------|
| `repo_name` | yes | GitHub repo slug — must match exactly |
| `build_tool` | yes | `maven`, `go`, `gradle`, `node-*`, `python-*`, `php`, `docker` |
| `dockerBuildVersion` | yes | Drives Dockerfile template: `maven-21`, `go-1.22`, `node-20`, etc. |
| `team` | yes | Team slug — validated against `buTeamMapping` |
| `bu` | yes | Business unit: `supply`, `demand`, `central`, `dataengg`, `datascience`, `mcache`, `infra` |
| `maintainer` | yes | GitHub handle for Slack notifications |
| `deployment_order` | yes | List of ArgoCD application names to deploy |
| `notify_channel` | no | Slack channel (default: `ci-cd-status`) |
| `skip_sonar` | no | Whitelist-gated; see `constructParam.groovy` |
| `deployArgo` | no | Set `false` to skip ArgoCD sync |
| `appConfigEnabled` | no | Required `true` for `stg`; whitelist-gated |
| `skip_test` | no | Skip unit tests (Maven) |
| `push_to_jfrog` | no | Publish JAR to JFrog Artifactory |
| `push_to_s3` | no | Push artifact to S3 |
| `build_packages` | no | System development packages required while compiling (currently consumed by Rust builds; for example `libpq-dev`) |
| `runtime_packages` | no | System runtime libraries required by the compiled binary (currently consumed by Rust builds; for example `libpq5`) |
## Adding this library to a new service
```groovy
// Jenkinsfile
@Library('devops-lib@main') _
eksCICD([
repo_name: 'my-service'
])
```
Place `config.yaml` at the repo root with the required fields above.
## Adding a new build stage
1. Create `src/com/meesho/stages/build<Lang>.groovy` implementing `def run(Map config)`.
2. Add a `case` in `src/com/meesho/stages/buildObjHelper.groovy`.
3. Add a Dockerfile template in `resources/com/meesho/<lang>-Dockerfile` if needed.
+45
View File
@@ -0,0 +1,45 @@
# AI Blitz — devops-lib task catalogue
Tasks identified for the AI Blitz Week 3 capstone (Task 11 from the Week 3 playbook). Each task is a small, agent-doable unit of work grounded in real findings from `BUGS_AND_IMPROVEMENTS_REPORT.md`, `docs/tribal-knowledge.md`, and `review-learnings.md`.
## Mix
5 tasks: 3 small features + 2 bug fixes.
| # | Task | Type | Files touched (rough) | Difficulty |
|---|---|---|---|---|
| 01 | [`task-01-securityScan-hardcoded-ip.md`](task-01-securityScan-hardcoded-ip.md) | Bug fix (P0 security) | 2 | low |
| 02 | [`task-02-checkoutSubmodule-typo.md`](task-02-checkoutSubmodule-typo.md) | Bug fix (correctness) | 1-2 + consumer repos | medium |
| 03 | [`task-03-onlyPushtoJfrog-jdk-versions.md`](task-03-onlyPushtoJfrog-jdk-versions.md) | Small feature | 1 | lowmedium |
| 04 | [`task-04-buildPipeline-agent-label-param.md`](task-04-buildPipeline-agent-label-param.md) | Small feature | 3 | low |
| 05 | [`task-05-cloudFunctionCICD-implementation.md`](task-05-cloudFunctionCICD-implementation.md) | Small feature | 4-5 + 1 consumer | high |
## Capstone picks
| Day | Mode | Task | Why this pick |
|---|---|---|---|
| Day 4 (Thu) | Interactive (war-room) | **Task 05 — Implement `cloudFunctionCICD`** | Real design decisions throughout (gen-1 vs gen-2 syntax, runtime → buildObjHelper mapping, service-account auth path). Maximises learning signal when humans correct the agent in real time. |
| Day 5 (Fri) | Autonomous | **Task 02 — Fix `chekoutSubmodule` typo** | Clear acceptance criteria, mostly mechanical, the deprecated-alias-with-log-warning pattern is recognisable. Agent can solo without supervision. |
## Adding a new task
1. Copy [`_TEMPLATE.md`](_TEMPLATE.md) to `task-NN-<short-slug>.md`.
2. Number sequentially (next is 06).
3. Fill out Goal + Acceptance Criteria. Cite a source — `BUGS_AND_IMPROVEMENTS_REPORT.md` §, `docs/tribal-knowledge.md` §, or a specific `review-learnings.md` rule ID.
4. Update the table above.
## Source documents the catalogue draws from
- [`../BUGS_AND_IMPROVEMENTS_REPORT.md`](../BUGS_AND_IMPROVEMENTS_REPORT.md) — known bugs + tech debt
- [`../docs/tribal-knowledge.md`](../docs/tribal-knowledge.md) — non-obvious conventions
- [`../review-learnings.md`](../review-learnings.md) — PR-review-derived rules
- [`../docs/architecture.md`](../docs/architecture.md) — module map, downstream services, invariants
## Out of scope
Tasks **not** in this catalogue (good candidates, but not picked):
- Full test-harness bootstrap (JenkinsPipelineUnit) + first unit tests — large scaffolding effort, deferred to a dedicated PR.
- ArgoCD `--password-stdin` refactor — pure refactor, no behavioural change; lower learning value.
- Refactor `validate_configs.py` (1207 lines) into modules — too large for a single Blitz task.
- `xq` (XML query) → Groovy parser on Gradle files in `buildGradle.groovy` — same reason.
+20
View File
@@ -0,0 +1,20 @@
# Task NN: <Title>
- **Type:** Feature / Bug fix / Refactor / Test gap
- **Source:** `<file>.md` §<section> or `review-learnings.md` `<RULE_ID>`
## Goal
One sentence: what should be true after this task is done.
## Acceptance Criteria
- [ ] Observable behaviour 1
- [ ] Observable behaviour 2
- [ ] Tests added/updated covering the new behaviour (skip if devops-lib has no test suite — note that here)
- [ ] All existing tests pass (or: smoke-test via `@Library('devops-lib@<branch>')` from a sandbox consumer Jenkinsfile)
- [ ] Docs updated if the task changes a documented behaviour (`README.md`, `CLAUDE.md`, `docs/tribal-knowledge.md`, `docs/architecture.md`, the relevant `review-learnings.md` rule)
## Notes / known gotchas
(Optional — surface design decisions or constraints the agent should know upfront.)
@@ -0,0 +1,22 @@
# Task 01: Replace hard-coded IP in `securityScan.groovy` with DNS hostname
- **Type:** Bug fix (security, P0)
- **Source:** `BUGS_AND_IMPROVEMENTS_REPORT.md` §2.1 + `review-learnings.md` rule `P0_HARDCODED_IP_IN_PIPELINE`
## Goal
`src/com/meesho/stages/securityScan.groovy` no longer references a bare IP for the scanner endpoint; the `P0_HARDCODED_IP_IN_PIPELINE` rule in `review-learnings.md` no longer needs the "known exception" carve-out for this file.
## Acceptance Criteria
- [ ] `securityScan.groovy:11` uses a DNS hostname (e.g. `security-scan.meeshogcp.in` or whatever DevOps allocates) instead of `172.31.5.29:63232`
- [ ] If no DNS exists yet, the task surfaces a DevOps ask before merging — does **not** ship with a placeholder IP
- [ ] `grep -rE '\b(172|10|192)\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+' src/com/meesho/ vars/` returns zero hits for HTTP/curl targets
- [ ] `review-learnings.md` rule `P0_HARDCODED_IP_IN_PIPELINE` has its "known exception: securityScan.groovy:11 uses 172.31.5.29:63232 pending remediation" carve-out **removed** in the same PR
- [ ] No retry / behavioural change — same POST shape, single call, only the hostname changes
- [ ] Smoke-test via a Jenkins job pointed at `@Library('devops-lib@<branch>')` confirming the scan POST succeeds against the new DNS
## Notes / known gotchas
- The receiver is a Meesho-internal scanner — coordinate with the security team to confirm the DNS name and that it's reachable from the Jenkins agent pod's network policy.
- Don't add a fallback to the IP "just in case" — the whole point of the rule is to fail loud on DNS issues, not to silently drop back to a hardcoded address.
@@ -0,0 +1,30 @@
# Task 02: Fix `chekoutSubmodule` method-name typo across all call sites
- **Type:** Bug fix (correctness)
- **Source:** `BUGS_AND_IMPROVEMENTS_REPORT.md` §1.2
## Goal
Method name in `src/com/meesho/stages/checkOut.groovy:20` reads correctly as `checkoutSubmodule`; every caller in the codebase resolves; consumer Jenkinsfiles that reference the old name don't break overnight.
## Acceptance Criteria
- [ ] `checkOut.groovy:20` defines `def checkoutSubmodule(String repo_name)` — not `chekoutSubmodule`
- [ ] Every `grep -rn 'chekoutSubmodule'` hit in `src/`, `vars/`, `resources/` is updated to `checkoutSubmodule`
- [ ] Cross-org sweep: search `Meesho/*` for `chekoutSubmodule` references and either:
- (a) raise companion PRs on each consumer to update the call, **or**
- (b) add a backwards-compat alias in `checkOut.groovy`:
```groovy
def chekoutSubmodule(String repo_name) {
log.warning('chekoutSubmodule is deprecated — use checkoutSubmodule')
return checkoutSubmodule(repo_name)
}
```
- [ ] Alias (if present) is documented in a `CHANGELOG`-style note with a target removal date
- [ ] Smoke-test via a consumer Jenkinsfile that exercises the submodule checkout path
- [ ] No behavioural change in the underlying method body — only the name changes
## Notes / known gotchas
- The alias path is the safer choice in a shared library — consumer Jenkinsfiles get reviewed and merged on their own schedule, and a hard rename will silently fail across the org.
- Once the alias is in, run `gh search code 'chekoutSubmodule' --owner Meesho --json repository,path` periodically to track removal readiness.
@@ -0,0 +1,22 @@
# Task 03: Complete JDK version handling in `onlyPushtoJfrog.groovy`
- **Type:** Small feature
- **Source:** `BUGS_AND_IMPROVEMENTS_REPORT.md` §1.5
## Goal
When a user picks JDK 11 or JDK 21 in the `onlyPushtoJfrog` choice parameter, the job runs under that JDK — not the JDK 8 fall-through default.
## Acceptance Criteria
- [ ] `vars/onlyPushtoJfrog.groovy:16` choice parameter remains `['jdk8', 'jdk11', 'jdk17', 'jdk21']` (no values removed)
- [ ] The if/else at `vars/onlyPushtoJfrog.groovy:48-53` handles **all four** JDK selections, each setting the correct `JAVA_HOME` path that exists in the build-tools pod image
- [ ] An unknown JDK selection (shouldn't happen via the choice param, but defensive) sets `JAVA_HOME` to a fail-fast value and logs `log.error("Unknown jdk_version: ${jdk_version}")` instead of silently defaulting to JDK 8
- [ ] Verify the JDK paths against the actual `devops-tools:lunar-vX.Y.Z` image used by `prd-pod.yaml` / `stg-pod.yaml`. If `jdk11` or `jdk21` aren't in the image, raise a `tribal-knowledge §12` follow-up (bump the build-tools image tag) **before** this task can complete
- [ ] Smoke-test all 4 JDK selections via a sandbox Jenkins job — confirm `java -version` reports the expected JDK in each run
- [ ] No behavioural change for `jdk8` / `jdk17` selections — those branches stay identical
## Notes / known gotchas
- The fall-through bug is silent: builds "succeed" with the wrong JDK, which can mask bytecode-version incompatibilities or break records of which JDK actually built the artifact. The fail-fast on unknown values is intentional.
- `tribal-knowledge.md` §12 calls out that toolchain binaries belong in the pod image, not curled at build time — keep this task aligned with that convention.
@@ -0,0 +1,26 @@
# Task 04: Make Jenkins agent label configurable via `agent_label` parameter
- **Type:** Small feature
- **Source:** `BUGS_AND_IMPROVEMENTS_REPORT.md` §3 (hard-coded `node('slave02')` in `vars/buildPipeline.groovy:16`)
## Goal
`buildPipeline { ... }` consumers can override the Jenkins agent node label without forking the library; existing consumers (no override) still pin to `slave02` so nothing breaks.
## Acceptance Criteria
- [ ] `vars/buildPipeline.groovy:16` reads `param.agent_label` and falls back to `'slave02'` when unset:
```groovy
def agentLabel = param.agent_label ?: 'slave02'
node(agentLabel) { ... }
```
- [ ] `README.md` `## Optional parameter` section documents `agent_label` with the same shape as the other params (key, type, default, description). Default must be the legacy `slave02` for backwards compatibility
- [ ] A consumer Jenkinsfile that passes `agent_label: 'gke-build-agent'` runs on that node; one that omits it still runs on `slave02`
- [ ] `grep -rn "node\('slave02'\)" vars/ src/` shows zero hard-coded uses **other than the fallback string in `buildPipeline.groovy`**. If other files hard-code the same node, list them out and either fix in the same PR or open follow-up tasks
- [ ] Smoke-test: one PR pipeline with the override, one without
- [ ] Add a `## NEVER DO` entry to `CLAUDE.md`: *"Don't add new `node('<literal>')` blocks in `vars/`. New entry points must accept `agent_label` from the param map and fall back to `'slave02'` or a documented default."*
## Notes / known gotchas
- `eksCICD.groovy` and `gkeCICD.groovy` use `podTemplate(yaml: libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml"))` instead of a `node()` block — they're outside the scope of this task. Only `buildPipeline.groovy` (and any other `node('<literal>')` callers grep finds) needs the change.
- Don't change the default to `null` or empty — services in the org currently rely on the implicit `slave02` pin; flipping the default mid-flight will silently move builds to whichever agent the master picks.
@@ -0,0 +1,40 @@
# Task 05: Implement the `cloudFunctionCICD` skeleton
- **Type:** Small feature
- **Source:** `BUGS_AND_IMPROVEMENTS_REPORT.md` §1.6 (`cloudFunctionCICDFlow()` is currently a stub containing only `sh 'ls -al'; echo 'Hello World'`)
## Goal
`vars/cloudFunctionCICD.groovy` actually deploys a GCP Cloud Function. Services that use `cloudFunctionCICD { ... }` get a working pipeline instead of the current no-op skeleton.
## Acceptance Criteria
- [ ] `vars/cloudFunctionCICD.groovy` exposes `def call(Map param)` (not the current zero-arg `cloudFunctionCICDFlow()`), accepting at minimum:
- `repo_name`
- `function_name`
- `runtime` — one of `nodejs20` / `python311` / `go121` / etc.
- `entry_point`
- `region`
- `service_account`
- `maintainer`
- [ ] Stages run in this order:
1. `checkOut` (reuse `src/com/meesho/stages/checkOut.groovy`)
2. Optional: `buildObjHelper.run(param.runtime)` if the runtime needs a transpile/install step (e.g. `node-*``npm install && npm run build`). Else skip.
3. `gcloud functions deploy ${function_name} --gen2 --runtime ${runtime} --entry-point ${entry_point} --region ${region} --service-account ${service_account} --source .`
4. `notify` (reuse `src/com/meesho/stages/notify.groovy`) — same Slack format as `buildPipeline`
- [ ] Pod selection uses `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")` — never an inline `podTemplate`
- [ ] Auth: `gcloud` uses ambient Workload Identity via the pod's `service_account`; **no key files written to disk**
- [ ] Branch gating: only deploy from `master`, `main`, `gcp-main`, or `gcp-master` (match the JFrog/S3 gate in `buildMaven.groovy`) unless `param.deploy_from_any_branch == true`
- [ ] Deployment-tracker callback fires on success/failure (reuse `notify.groovy:postTrackingApi`)
- [ ] One real consumer service uses the new entry point end-to-end as the smoke test
- [ ] `README.md` `## Adding this library to a new service` gains a Cloud Function example block alongside the existing Jenkinsfile sample
- [ ] `docs/architecture.md` § "Entry points (vars/)" updated — `cloudFunctionCICD` is no longer flagged as "stub"
- [ ] `BUGS_AND_IMPROVEMENTS_REPORT.md` §1.6 entry is moved to a Resolved section (or struck-through per repo convention)
- [ ] `docs/index.md` / `docs/wiki/index.md` updated if the entry point gets new dedicated documentation
## Notes / known gotchas
- Use `gcloud functions deploy --gen2` explicitly — gen-1 syntax differs (no `--source .`, different IAM model). Don't write code that works on whichever version happens to be default in the build-tools image.
- The user-authorization gate from `eksCICD.groovy:12-26` (allowed-users list, Ringmaster banner) is intentionally **not** copied to this entry point unless DevOps says otherwise. Cloud Functions are deployed less frequently; the gate may be overkill.
- If `runtime` doesn't match a known `buildObjHelper` case (e.g. `python311` vs `python-3.11`), prefer extending the `buildObjHelper` switch to recognise the GCP runtime naming rather than inlining build logic in `cloudFunctionCICD`.
- This task touches a lot of files (vars/, README, architecture.md, BUGS report) — chunk into reviewable commits if the agent is going autonomous, otherwise one PR is fine.
+38
View File
@@ -0,0 +1,38 @@
plugins {
id 'groovy'
}
repositories {
maven { url 'https://repo.jenkins-ci.org/releases/' }
maven { url 'https://repo.jenkins-ci.org/public/' }
mavenCentral()
}
sourceSets {
// Main sources are Jenkins Shared Library scripts loaded at runtime by JenkinsPipelineUnit.
// They depend on Jenkins API and are not pre-compiled — loadScript() handles them at test time.
main {
groovy { srcDirs = [] }
java { srcDirs = [] }
}
test {
// test/unit — actual test classes
// test/stubs — minimal stub implementations of Jenkins stage classes used by dispatch tests
groovy { srcDirs = ['test/unit', 'test/stubs'] }
}
}
dependencies {
testImplementation 'com.lesfurets:jenkins-pipeline-unit:1.22'
// Match the Groovy version bundled by JenkinsPipelineUnit
testImplementation 'org.codehaus.groovy:groovy-all:2.4.21'
testImplementation 'junit:junit:4.13.2'
}
test {
systemProperty 'user.dir', rootDir.absolutePath
testLogging {
events 'passed', 'skipped', 'failed'
exceptionFormat 'full'
}
}
+262
View File
@@ -0,0 +1,262 @@
# SECURITY.md
<!-- Auto-generated by /m-docs:security-init. Edit freely — re-running preserves your changes. -->
> **Scope:** devops-lib is a Jenkins Shared Library, not a deployable web service. It has no HTTP endpoints of its own. This document covers the security properties of the library's CI/CD execution: how secrets are handled, what trust boundaries exist, what security rules new code must follow, and known gaps.
---
## Authentication
devops-lib has no user-facing HTTP endpoints and performs no JWT or session validation. Authentication is enforced at two points:
### Build trigger gate
`vars/eksCICD.groovy:1226` validates that every Jenkins build is triggered by an authorized caller:
- `ringmaster-bot` — Ringmaster's automated trigger
- `turbo-turtle` — Turbo-Turtle's CI callback trigger
- `allowedUsers` — a hardcoded list of DevOps engineer email addresses for emergency access
Unauthorized triggers are hard-rejected before any pipeline logic runs.
### Outbound credential injection
All outbound API calls use Jenkins' `withCredentials` binding — credentials are never hardcoded in source. Jenkins masks bound variables in console output automatically. Key credential IDs:
| Credential ID | Used for | Scope |
|---|---|---|
| `cicd-github-app` | Cloning `Meesho/whitelists`, `devops-argo-config`, `devops-helm-charts` | All builds |
| `svc-devops-meesho` | GitHub API, JFrog Artifactory | Build + deploy |
| `ringmaster-token` | Ringmaster callback API | Notify stage |
| `argocd-{bu}-prd-creds` / `argocd-dev-creds` | ArgoCD CLI login | Deploy stage |
| `vault-prd-token` / `vault-dev-token` | Vault secret fetch | Node builds only |
| `sonar-token-prod` / `sonar-token-{bu}-dev` | SonarQube analysis | Build stages |
---
## Trust Boundaries
### Build trigger trust boundary
| Layer | What happens | Where | Confidence |
|---|---|---|---|
| Ringmaster / Turbo-Turtle | Validates human approval, triggers Jenkins build | Upstream (Ringmaster infra) | docs-referenced |
| devops-lib `eksCICD` | Validates trigger source against `allowedUsers` list | `vars/eksCICD.groovy:12` | code-confirmed |
| Jenkins pipeline | Executes build stages with injected credentials | Jenkins agents (GKE pods) | code-confirmed |
**Trust assumption:** devops-lib assumes that any build triggered by `ringmaster-bot` or `turbo-turtle` has already been approved by the Ringmaster/Turbo-Turtle authorization flow. It does NOT re-validate the approval — it trusts the trigger identity.
### Supply chain trust boundary (CRITICAL)
`devops-lib@main` is loaded via `@Library('devops-lib@main')` by every Meesho microservice on every build. **A malicious or buggy merge to `main` is an immediate supply chain attack on all 100+ consumer services' CI/CD pipelines.**
- Whoever can merge to `devops-lib@main` controls the full CI/CD path for all Meesho microservices
- Branch protection rules for `main` are enforced at the GitHub repository level (not visible in this repo's source)
- No `.github/CODEOWNERS` file is present in the repository
<!-- TODO: Confirm that devops-lib's main branch has required PR reviews and no direct push access for non-DevOps-leads. This is the highest-risk trust boundary in the library. -->
### Whitelist repo trust boundary
Policy exceptions (sonar skip, multizone, AppConfig, CAC) are fetched from `Meesho/whitelists` at build time via `cicd-github-app` credential. If `Meesho/whitelists` is compromised or the `cicd-github-app` credential is stolen, an attacker could:
- Add any service to `skip-sonar-whitelist` to bypass quality gates
- Add a service to `multizone-enabled-repos` to block its deployments
- Remove a service from `ValidateCacConfig` to bypass config validation
### Jenkins agent trust boundary
Build stages execute in GKE pods (see `resources/org/meesho/prd-pod.yaml`). Secrets injected via `withCredentials` exist in the pod's process environment for the duration of the `withCredentials` block and are cleared afterward. `env.VAULT_TOKEN` is temporarily set during Vault secret fetch and immediately cleared:
```groovy
// buildNode.groovy:548-553
env.VAULT_TOKEN = "${TOKEN}"
sh(script:"${vault_cmd}")
env.VAULT_TOKEN = 'empty' // ← cleared immediately after use
```
**Note:** Assigning to `env.*` persists the value in Jenkins pipeline serialized state for the duration of that block — it is not fully memory-isolated like a `withCredentials` binding.
### Process / runtime boundaries
| Boundary | Inside (trusted) | Outside | Crossing mechanism |
|---|---|---|---|
| `withCredentials` block | Jenkins credential binding (secret) | Pipeline Groovy scope | Automatic unset on block exit |
| Jenkins agent pod | Build process, injected creds | Other pods, external network | K8s network policy, GKE service account |
| `set +x` shell guard | ArgoCD password in shell arg | Jenkins console log | `set +x` before credential use in `deployArgoCD.groovy:493` |
| DinD container | Docker daemon | Build container | TCP socket (`dind-prd-svc`) — not Unix socket (avoids privilege escalation) |
---
## Entry Points
devops-lib has no HTTP entry points. It is invoked as a Jenkins Shared Library.
### Build trigger (sole entry point)
| Trigger | Who sends it | Auth check |
|---|---|---|
| Ringmaster-initiated build | `ringmaster-bot` Jenkins user | `allowedUsers` gate in `eksCICD.groovy` |
| Turbo-Turtle-initiated build | `turbo-turtle` Jenkins user | `allowedUsers` gate in `eksCICD.groovy` |
| DevOps engineer direct trigger | Email in `allowedUsers` list | `allowedUsers` gate |
| Unauthorized user | Any other Jenkins user | Hard-rejected — pipeline aborts immediately |
### Outbound calls (not entry points, but relevant to trust)
All outbound calls are made FROM Jenkins agents TO external services. See [docs/downstreams.md](downstreams.md) for the full inventory.
Security additions to downstreams.md:
| Service | Protocol | Data sent | Risk | Notes |
|---|---|---|---|---|
| ArgoCD | HTTPS + gRPC | App names, image tags | Low | `set +x` guards password in shell |
| Ringmaster | HTTPS | Build result, image tag, repo name, team | Low | Auth via `ringmaster-token` credential |
| Turbo-Turtle | **HTTP** (plain) | Build result, image tag, repo name | Low — accepted risk | Internal VPC only, not reachable externally |
| Deployment Tracker | **HTTP** (plain) | Repo name, deploy timestamp, tag | Low — accepted risk | Internal VPC only, legacy endpoint |
| Security scanner | **HTTP** to `172.31.5.29:63232` | Repo name, branch | Low — accepted risk | Internal scanner, hardcoded IP |
| SonarQube | HTTPS | Source code analysis | Low | Token injected via `withCredentials` |
| Vault | HTTPS | Vault path (not secrets) | Low | Token cleared immediately after fetch |
| GitHub | HTTPS | Git operations | Low | `cicd-github-app` credential |
---
## Authorization
### Policy enforcement model
devops-lib enforces policy through two mechanisms:
1. **Whitelist-controlled gates**`constructParam.groovy` checks `Meesho/whitelists` at runtime for per-repo exceptions. No service can grant itself a bypass; all exceptions require a PR to `Meesho/whitelists` reviewed by DevOps.
2. **Library-level enforcement**`deployArgoCD.groovy` enforces canary for Tier-1 services, `eksCICD.groovy` enforces the trigger gate. These cannot be overridden by service config.
### allowedUsers list
The bypass list at `vars/eksCICD.groovy:12` contains hardcoded engineer email addresses. This list has no expiry mechanism — emails remain valid until manually removed.
<!-- TODO: Confirm that the allowedUsers list is audited periodically to remove email addresses of engineers who have left the organization. -->
---
## Data Classification
devops-lib handles no end-user PII. All data is build metadata:
### Non-PII (safe to log and pass to external services)
| Data | Where it appears | Notes |
|---|---|---|
| `repo_name` | All stages, Ringmaster callback | GitHub org slug — not sensitive |
| `build_tool` | Build stages | Language identifier |
| `cicd_environment` | All stages | prd / stg / int / ftr |
| `TAG` (image tag) | Deploy stages, Slack notifications | `<branch>-<git-sha>` — not sensitive |
| `notify_channel` | Notify stage | Slack channel name |
| `team` / `bu` | Build stages, node pool selection | Org metadata |
| `deployment_order` | ArgoCD deploy | App names in devops-argo-config |
| Build result / duration | Slack, Ringmaster, Turbo-Turtle | Build observability |
### Sensitive (not PII, but must be handled with care)
| Data | Where it exists | Handling |
|---|---|---|
| Jenkins credential values | `withCredentials` blocks | Never logged, masked in console |
| Vault secret paths | `buildNode.groovy:536-537` | Path logged (not value); value only in `sh` subprocess |
| `ARGO_USERNAME` / `ARGO_PASSWORD` | `deployArgoCD.groovy:490-494` | `set +x` guard prevents echo in logs |
| `GITHUB_TOKEN` | Git clone operations | Via `gitUsernamePassword` binding — not logged |
---
## Data Lifecycle & Erasure
No end-user data stored. Build artifacts:
**Known:**
- Jenkins build logs: retained per Jenkins job configuration (managed by Jenkins admins, not devops-lib)
- Docker images in GAR/ECR: no TTL configured in devops-lib — lifecycle managed by GAR cleanup policies outside this library
- Deployment history in Ringmaster: managed by Ringmaster service
**Unknown:** Retention policy for build logs and deployment records is not configurable from devops-lib.
<!-- TODO: Confirm Jenkins build log retention policy with the infrastructure team. -->
---
## Data Storage & Encryption
### At rest
devops-lib has no persistent storage. It reads from GitHub, Jenkins credentials store, and Vault; it writes to GitHub repos (argo-config, helm-charts) and pushes Docker images to GAR/ECR.
| Store | What is stored | Managed by |
|---|---|---|
| Jenkins credentials store | All CI/CD credentials (tokens, passwords) | Jenkins admins |
| GAR / ECR | Docker images | GCP / AWS infra |
| devops-helm-charts / devops-argo-config | Helm values, ArgoCD manifests | devops-lib writes; git is the store |
### In transit
- All GitHub API calls: HTTPS ✓
- ArgoCD CLI: HTTPS + gRPC ✓
- SonarQube, Vault, Ringmaster API: HTTPS ✓
- Turbo-Turtle, Deployment Tracker, security scanner: **plain HTTP** — accepted risk (internal VPC, not reachable externally)
### Secrets management
All secrets are injected at runtime from Jenkins credentials store via `withCredentials`. No secrets in source code, config files, or environment variables baked into the library. Credentials are identified by their Jenkins credential ID (e.g. `ringmaster-token`, `vault-prd-token`) — the actual values are never stored in this repository.
---
## Input Validation
devops-lib takes inputs from two sources:
### Service `config.yaml` (primary input)
Read by `getYamlParameter.getParam()`. Fields are used directly without schema validation — devops-lib trusts the config.yaml from the consumer service's own repository (cloned via authenticated git). Malformed configs produce runtime errors, not silent misbehaviour.
`buTeamMapping.groovy` validates `bu` and `team` fields against a known mapping and throws on invalid values.
### What's NOT validated
- **`repo_name`** in config.yaml: used in ArgoCD app names, Slack messages, and Vault paths. Not sanitized against shell injection — passed directly into `sh()` scripts. Risk is mitigated because `repo_name` comes from the service's own config.yaml in its own GitHub repo (already authenticated).
- **`notify_channel`**: passed directly to Slack API — no format validation. A malformed channel name produces a Slack API error, not a security issue.
- **`deployment_order` app names**: passed to `argocd app sync` — no format validation. An invalid app name produces an ArgoCD error.
---
## Security Headers & CORS
Not applicable — devops-lib has no HTTP server and serves no responses.
---
## Security Rules for New Code
These rules apply to anyone adding code to devops-lib:
**Credentials:** All secrets must be injected via `withCredentials` — never assign a credential value to a variable outside a `withCredentials` block, never interpolate credentials into log statements, and never store them in `env.*` variables beyond the immediate operation that needs them.
**Shell commands with credentials:** Use `set +x` immediately before any `sh()` that includes a credential variable as an argument (as done in `deployArgoCD.groovy:493`). Without `set +x`, Jenkins echoes the full shell command including the credential value to the build log.
**Policy enforcement:** Never add inline conditionals for repo-level policy exceptions. All exceptions must go through `Meesho/whitelists` — see [ADR-0003](adr/0003-policy-exceptions-in-separate-whitelist-repo.md).
**Supply chain hygiene:** Any change to `vars/eksCICD.groovy` or `src/com/meesho/utilities/constructParam.groovy` affects every Meesho microservice build. These files require extra scrutiny — treat them as Tier-1 code.
**DinD image:** The Docker-in-Docker 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 is not security-scanned and will be blocked by network policy. (See `docs/tribal-knowledge.md` TK#14.)
The following security rules are already enforced in CLAUDE.md NEVER DO and are not repeated here:
- **Never hard-code AWS/GCP account IDs, vault tokens, or credentials** — all secrets are passed via Jenkins credentials (`withCredentials`) or injected through `constructParam.run()`. Credentials IDs are defined in `constructParam.groovy`.
- **Never change the string `"ringmaster-bot"`** without coordinating with the Ringmaster team — it is the sole signal that routes callbacks to Ringmaster vs Turbo-Turtle.
---
## Security Debt Tracker
| ID | Gap | Severity | Source | Ticket |
|---|---|---|---|---|
| SEC-DL-001 | `allowedUsers` list in `eksCICD.groovy` contains engineer email addresses with no expiry — stale access risk if engineers leave the org | Low | Code-discovered | — |
| SEC-DL-002 | No `.github/CODEOWNERS` visible in repo — supply chain protection for `main` branch depends entirely on GitHub repo settings not auditable from source | Medium | Code-discovered | — |
| SEC-DL-003 | Security scanner endpoint hardcoded as `172.31.5.29:63232` (plain HTTP, no auth) — if IP changes, scanner silently stops running | Low | Code-discovered | — |
| SEC-DL-004 | `env.VAULT_TOKEN` temporarily assigned in `buildNode.groovy:550` — token exists in Jenkins pipeline serialized state during Vault fetch block (cleared immediately after) | Low | Code-discovered | — |
| SEC-DL-005 | Turbo-Turtle and Deployment Tracker callbacks over plain HTTP | Low | Code-discovered | — |
---
## Past Security Incidents
No known security incidents documented in code, wiki, or Jira at time of generation.
---
<!-- security-init: generated-at=2026-05-12T00:00:00Z base-sha=1495ffb862641ed7220b0cbce5c021e7487f2a1d -->
+18
View File
@@ -0,0 +1,18 @@
# Acronyms
> Domain-specific acronyms used in this repo's code and docs. Generic programming
> acronyms (HTTP, JSON, gRPC, API, etc.) are not listed here.
| Acronym | Definition | Used in |
|---------|------------|---------|
| BU | Business Unit — partitions services for multi-tenancy; drives Helm chart paths, ArgoCD namespaces, and GCP project naming | infrastructure |
| BUILDKIT | Docker BuildKit — advanced build subsystem; referenced only in the Node.js builder (`buildNode.groovy`) where it is explicitly disabled via `DOCKER_BUILDKIT=0` | flows |
| CAC | Config-as-Code — Meesho's convention for validating application YAML configs (`application-*.yml`) against a schema at PR time; gated by the `ValidateCacConfig` whitelist | flows, policy |
| CICDF | CICD Flow — the main pipeline orchestration function (`commonCICDFlow`) that sequences build, deploy, and notify stages | flows, architecture |
| GCPLBP | GCP Load Balancer Project — the GCP project that hosts the load balancer; used as a distinct project reference in `constructParam.groovy` alongside `GCPP` | infrastructure |
| GCPP | GCP Project — the GCP project identifier for the service's deployment target; computed from BU name as `meesho-<bu>-prd-0622` | infrastructure |
| INFRA | Infrastructure environment — `env.INFRA_ENV` controls which Jenkins pod template (`<INFRA_ENV>-pod.yaml`) is selected for the build agent | infrastructure |
| NPMRC | npm configuration file (`.npmrc`) — bundled in the workspace so private registry credentials are available during Node.js builds | flows |
| SCMH | SCM Host — the Source Control Management host URL; passed to checkout stages to resolve repository clone URLs | flows |
| SCMS | Source Control Management System — generic reference to the SCM provider (GitHub) used in checkout and PR-trigger logic | flows |
| SONAR | SonarQube — static analysis tool; skippable via the `skip-sonar-whitelist.yaml` exception list for Maven `prd` builds | policy |
@@ -0,0 +1,46 @@
# ADR-0001: Single Shared Library Consumed by All Services
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Project inception
**Date documented:** 2026-05-12
## Context
Meesho runs 100+ microservices across multiple teams. Each service needs CI/CD: build, test, sonar analysis, Docker image push, and ArgoCD deployment. Before devops-lib, teams owned their own Jenkinsfiles and copy-pasted pipeline definitions from each other, resulting in drift, inconsistent policy enforcement, and no central control over who could bypass sonar gates or deploy directly to production.
## Decision
All Meesho microservices consume a single Jenkins Shared Library — `devops-lib` — via `@Library('devops-lib@main')` in their Jenkinsfile. The library exposes a single entry point (`eksCICD`) that handles the full CI/CD lifecycle. Service teams do not write pipeline logic; they only provide a `config.yaml`.
## Alternatives Considered
- **Per-team Jenkinsfiles**: Rejected because teams were already copy-pasting pipelines and the resulting drift made it impossible to enforce sonar gates, whitelist checks, or deployment policies uniformly.
- **Per-language pipeline templates**: Rejected in favour of a single entry point that dispatches to language-specific stages internally — cross-cutting concerns (sonar, notifications, ArgoCD) remain in one place.
## Consequences
**Positive:**
- Policy enforcement (sonar gates, whitelist checks, Ringmaster gating) is applied uniformly to every service on every build — no team can accidentally opt out.
- Infrastructure changes (new deploy strategy, new ArgoCD step, new policy) roll out to all 100+ services by merging one PR to devops-lib.
- New service onboarding is reduced to writing a `config.yaml` — no pipeline expertise needed from the service team.
**Negative:**
- The library becomes a dependency for every service build — a broken devops-lib main branch blocks all CI/CD.
- Teams needing custom pipeline behaviour have limited escape hatches; they must go through the DevOps Platform team.
**Neutral:**
- devops-lib must support all build types (Maven, Go, Node, Python, Gradle, PHP, Rust) internally, increasing the library's surface area.
## Constraints
Meesho's scale (100+ services) made per-service pipeline ownership operationally unsustainable. The decision was also driven by a need for auditable deployment policy — the DevOps team needed a single control plane for all CI/CD.
## Current Assessment
**Still appropriate** — no changes needed.
## Notes
- Key files: `vars/eksCICD.groovy` (entry point), `src/com/meesho/stages/buildObjHelper.groovy` (language dispatch)
- Consumer services call the library with a single line: `@Library('devops-lib@main') _`
@@ -0,0 +1,58 @@
# ADR-0002: Branch Name as the Sole Environment Selector
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Project inception
**Date documented:** 2026-05-12
## Context
Each Meesho service needs to deploy to different environments (staging, production, integration, feature). A naive approach would let each service declare its target environment in config, but this creates a risk: a misconfigured service could accidentally deploy to production from a feature branch, or fail to promote through the standard develop → stg → main → prd path.
## Decision
`cicd_environment` is derived entirely and deterministically from the branch name. No per-service environment configuration exists. The mapping is:
| Branch | PR target | `cicd_environment` |
|---|---|---|
| `main` / `master` / `gcp-main` | — | `prd` |
| `develop` | — | `stg` |
| any | `main` | `int` |
| any | `develop` | `ftr` |
| `hotfix/*` | — | `prd` (sonar + tests skipped) |
## Alternatives Considered
- **Per-service environment configuration**: Rejected because it creates a class of misconfiguration bugs (wrong env in config.yaml → wrong deploy target) and makes it impossible to enforce the standard branch promotion strategy uniformly.
- **Environment as a Jenkins parameter**: Rejected because it relies on the engineer picking the right environment at trigger time — error-prone and not auditable.
## Consequences
**Positive:**
- Services cannot accidentally deploy to prd from a feature branch — the branch IS the environment contract.
- Standard develop → stg → main → prd promotion is enforced uniformly across all services.
- Environment logic lives in one place (`constructParam.groovy`) — easy to audit and change centrally.
**Negative:**
- Teams with non-standard branch strategies (e.g., release/* branches) cannot use the standard pipeline without DevOps involvement.
- The hotfix path (prd with sonar skipped) requires following the `hotfix/*` naming convention exactly.
**Neutral:**
- The `int` and `ftr` environments are determined by PR target, not branch name — this is the one case where branch name alone is insufficient.
## Constraints
Meesho's deployment policy required a standard promotion path. Allowing per-service environment configuration would have made it impossible to enforce this uniformly across 100+ services.
## Current Assessment
**Still appropriate** — no changes needed.
## Related Decisions
- [ADR-0001: Single Shared Library](0001-single-shared-library-for-all-services.md) — The shared library enforces this mapping; per-service Jenkinsfiles could override it.
## Notes
- Key file: `src/com/meesho/utilities/constructParam.groovy` — environment mapping logic
- PR context is detected via `env.CHANGE_ID` (set by GitHub Branch Source plugin), not by `env.BRANCH_NAME =~ /PR-/`
@@ -0,0 +1,60 @@
# ADR-0003: Policy Exceptions Controlled by a Separate Meesho/whitelists Repo
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Early on
**Date documented:** 2026-05-12
## Context
The shared CI/CD library enforces several policies by default: SonarQube quality gate, AppConfig validation, CAC config validation, multizone deployment restrictions. Some services legitimately need to bypass these policies (e.g., a legacy service that cannot pass sonar without significant rework, or a service that doesn't use AppConfig). The question is where to store these exceptions and who can grant them.
## Decision
Policy exceptions are controlled by YAML files in a separate `Meesho/whitelists` repository, not by flags in each service's `config.yaml`. There are five active whitelist files:
| Whitelist | Controls |
|---|---|
| `skip-sonar-whitelist` | Repos that skip SonarQube scanning |
| `app-config-disabled` | Repos that skip AppConfig validation |
| `multizone-enabled-repos` | Repos that must deploy via Ringmaster (not Jenkins) |
| `allowedNonDevelopPrDeploymentToInt` | Repos allowed to deploy non-develop PRs to int |
| `ValidateCacConfig` | Repos that run CAC config validation |
Adding or removing a repo from any whitelist requires a PR to `Meesho/whitelists` reviewed and merged by the DevOps Platform team.
## Alternatives Considered
- **Flags in each service's config.yaml** (e.g., `skip_sonar: true`): Rejected because service teams could self-grant bypass without DevOps review, making it impossible to audit or enforce policy. Note: `config.yaml` does contain a `skip_sonar` field, but `constructParam.groovy` explicitly throws an exception if a repo sets it to `true` without being on the skip-sonar-whitelist — the whitelist is the authoritative gate.
- **Hardcoded exceptions in devops-lib source**: Rejected because adding an exception would require a devops-lib PR, which is heavier than a whitelists PR and conflates policy with pipeline logic.
## Consequences
**Positive:**
- DevOps Platform team retains ownership of all policy exception grants — service teams cannot bypass sonar or multizone enforcement unilaterally.
- All exceptions are visible in one repo — easy to audit who has what bypass and why.
- Policy can be tightened or relaxed without touching devops-lib or any service repo.
**Negative:**
- Adding a legitimate exception requires a separate PR to a different repo, adding friction for service teams.
- The whitelist repo is a single point of dependency — if it's unreachable, every build that checks whitelists fails.
**Neutral:**
- The whitelist is re-cloned fresh on every build (see ADR-0004), so changes take effect immediately without a devops-lib release.
## Constraints
Audit requirements and DevOps policy ownership drove the decision to separate exception management from service config.
## Current Assessment
**Still appropriate** — no changes needed.
## Related Decisions
- [ADR-0004: Fresh Whitelist Clone Per Build](0004-fresh-whitelist-clone-per-build.md) — How whitelist changes take effect immediately.
## Notes
- Key file: `src/com/meesho/utilities/constructParam.groovy:getWhitelistedRepos()` — all whitelist lookups go through this method
- `config.yaml` has a `skip_sonar` field but it is rejected by `constructParam.groovy` unless the repo is also on the whitelist
@@ -0,0 +1,50 @@
# ADR-0004: Whitelist Cloned Fresh on Every Build — No Caching
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Early on
**Date documented:** 2026-05-12
## Context
`constructParam.groovy` checks several whitelists (sonar skip, multizone, AppConfig, CAC, non-develop PR deploy) on every build. Jenkins agents are long-lived processes that handle many builds sequentially. If the whitelist were cached in agent memory or on disk, a policy change (adding or removing a repo from a whitelist) would not take effect until the agent restarted or the cache expired.
## Decision
`getWhitelistedRepos()` clones `Meesho/whitelists` fresh from GitHub on every build invocation. No in-memory cache, no disk cache, no TTL — each build gets the current live state of the whitelist at that moment.
## Alternatives Considered
- **Cache with TTL (e.g., 5 minutes)**: Rejected because a DevOps engineer who merges a critical policy change (adding a repo to skip-sonar during an incident) would have to wait for the cache to expire — unacceptable for incident response.
- **Cache per Jenkins agent restart**: Rejected for the same reason — agents can run for hours/days, making cache invalidation unpredictable.
- **Webhook-triggered cache invalidation**: Not evaluated — the simplicity of a fresh clone was preferred over building an invalidation mechanism.
## Consequences
**Positive:**
- Policy changes take effect on the very next build after the whitelist PR is merged — no devops-lib release required.
- No cache invalidation complexity; the whitelist state is always authoritative.
**Negative:**
- Each build that checks a whitelist incurs a `git clone` of `Meesho/whitelists`. At high build throughput, this is measurable latency.
- If `Meesho/whitelists` is temporarily unreachable (GitHub outage, network partition), all builds that check whitelists fail.
**Neutral:**
- Multiple whitelist checks in a single build (sonar + AppConfig + multizone) each clone the repo separately — there is no deduplication within a single build.
## Constraints
Incident response requirements made immediate policy enforcement non-negotiable. The extra clone latency was accepted as the cost of correctness.
## Current Assessment
**Still appropriate** — no changes needed.
## Related Decisions
- [ADR-0003: Policy Exceptions in Separate Whitelist Repo](0003-policy-exceptions-in-separate-whitelist-repo.md) — The whitelist repo this decision is about.
## Notes
- Key file: `src/com/meesho/utilities/constructParam.groovy:getWhitelistedRepos()`
- **Never refactor this to cache across calls** — the fresh-clone behaviour is load-bearing for incident response. This is documented in CLAUDE.md under NEVER DO.
@@ -0,0 +1,46 @@
# ADR-0005: Config-Only Change Detection — Skip Binary Build, Reuse Latest Image
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Mid-project
**Date documented:** 2026-05-12
## Context
Meesho services store both application code and deployment configuration (Helm values, AppConfig YAML) in the same repository. Teams frequently push config-only changes — tweaking memory limits, updating feature flags, changing environment variables — that do not require recompiling the binary or rebuilding the Docker image. Without detection, every such commit triggers a full 1015 minute CI run: compile, test, sonar scan, Docker build, image push — then deploy the same binary that was already running.
## Decision
Each build stage checks whether the Git diff contains only `*.yaml` file changes (no source code). If so, the binary build, Docker build, and image push are entirely skipped. The latest image tag is fetched from Google Artifact Registry (GAR) and used directly for the ArgoCD deployment. The full build runs only when source code changes are present.
## Alternatives Considered
- **Always run the full build**: Rejected — config rollouts would take 1015 minutes when the only change is a YAML file, causing friction and delaying incident response (e.g., bumping a memory limit during an OOM incident).
- **Separate repos for code and config**: Considered but rejected — splitting config into a separate repo adds operational complexity (two PRs for one change, out-of-sync risk) without proportional benefit.
## Consequences
**Positive:**
- Config rollouts (AppConfig changes, Helm value tweaks, memory limit bumps) complete in ~2 minutes instead of 1015 minutes.
- Reduces unnecessary Docker image churn — no new SHA for a commit that didn't change the binary.
- Faster incident response: an engineer can bump `memory_request` during an OOM and see it deployed in minutes.
**Negative:**
- The detection is heuristic — it checks file extensions, not semantic content. A YAML file that configures build behaviour (e.g., a hypothetical `.github/workflows/` file) would be misclassified as a config-only change.
- The latest image tag from GAR must exist; if the previous build failed before pushing an image, a config-only change will fail to find a tag to deploy.
**Neutral:**
- The config-only path still triggers the ArgoCD deployment steps — only the build and image push are skipped.
## Constraints
Build time was the primary constraint. Teams were complaining about slow feedback cycles for config changes. The detection logic was the minimal implementation that addressed this without a repo restructure.
## Current Assessment
**Still appropriate** — no changes needed.
## Notes
- Key file: `src/com/meesho/stages/buildGo.groovy:is_config_only_change_and_should_deploy_argo()` (reference implementation; similar logic exists in other build stages)
- The TODO comment in `buildMaven.groovy:70` notes that appConfig changes also currently trigger a build — this is a known gap
@@ -0,0 +1,48 @@
# ADR-0006: Ringmaster as Mandatory Build Trigger Gate
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Early on
**Date documented:** 2026-05-12
## Context
Jenkins is accessible to all engineers in the organisation. Without a trigger gate, any engineer can click "Build Now" in Jenkins and kick off a build — including a production deployment — without any audit trail, approval, or callback to downstream systems. Ringmaster is Meesho's internal deployment orchestration system that tracks every deployment: who triggered it, what tag was deployed, when, and whether it succeeded. Turbo-Turtle is the CI callback system that receives the build result and updates deployment state.
## Decision
`eksCICD.groovy` hard-rejects any build not triggered by `ringmaster-bot`, `turbo-turtle`, or a hardcoded DevOps allowedUsers list. The rejection is immediate and explicit — the build errors with a message directing the engineer to use Ringmaster instead. The string `"ringmaster-bot"` is the sole signal that routes CI callbacks correctly between Ringmaster and Turbo-Turtle.
## Alternatives Considered
- **Allow direct Jenkins triggers with a warning**: Rejected — a warning is easily ignored; the deployment would still bypass Ringmaster's tracking and the Turbo-Turtle callback would have no caller to report to.
- **Restrict Jenkins UI access via RBAC**: Considered but not implemented — coarse-grained Jenkins RBAC would still allow authorized engineers to trigger builds directly, and doesn't solve the callback problem.
- **Audit log only (no rejection)**: Rejected — audit-only does not prevent the problem; it only discovers it after the fact.
## Consequences
**Positive:**
- Every production deployment is tracked in Ringmaster — who triggered it, what tag, when — creating a full deployment ledger.
- Turbo-Turtle always receives the CI result callback because the trigger is always one of the known callers.
- Engineers cannot bypass deployment holds or approval flows by triggering Jenkins directly.
**Negative:**
- DevOps engineers must maintain the hardcoded `allowedUsers` list for emergency access (e.g., debugging a pipeline issue directly from Jenkins).
- The gate adds a hard dependency on Ringmaster being operational for any build to run.
**Neutral:**
- The string `"ringmaster-bot"` is load-bearing — changing it without coordinating with the Ringmaster team would break the callback routing between Ringmaster and Turbo-Turtle.
## Constraints
Meesho's deployment audit and compliance requirements necessitated a full deployment ledger. The Ringmaster/Turbo-Turtle architecture was already in place; the gate was added to enforce its use.
## Current Assessment
**Still appropriate** — no changes needed.
## Notes
- Key file: `vars/eksCICD.groovy:12-26` — allowedUsers list and trigger validation
- **Never change the string `"ringmaster-bot"`** without coordinating with the Ringmaster team — documented in CLAUDE.md under NEVER DO
- Ringmaster UI: `https://ringmaster.meeshogcp.in/applications/cicd/home`
@@ -0,0 +1,56 @@
# ADR-0007: GitOps Deployments via Strict 4-Step ArgoCD Sync Sequence
**Status:** Accepted
**Category:** INFRA
**Date decided:** Early on
**Date documented:** 2026-05-12
## Context
Meesho migrated GCP service deployments from direct `kubectl apply` / Helm install to GitOps via ArgoCD. The key requirement was continuous reconciliation — the cluster state should always reflect what's in Git, and any manual `kubectl` changes should be automatically reverted. The deployment pipeline needed to update two separate Git repositories (argo-config for ArgoCD Application manifests, helm-repo for Helm chart values) and trigger ArgoCD to sync, without leaving the cluster in an inconsistent intermediate state.
## Decision
All GCP service deployments go through a strict 4-step sequence in `deployArgoCD.groovy`:
1. **`update_argo_repo`** — Push the updated ArgoCD Application manifest to devops-argo-config
2. **`refresh_app_of_apps`** — Trigger ArgoCD to sync the app-of-apps, creating any new Application objects
3. **`update_helm_repo`** — Push the new Helm chart (with the new image tag) to the Helm repo
4. **`refresh_and_sync`** — Trigger ArgoCD to sync the specific application
The order is non-interchangeable. Steps 2 and 4 cannot be swapped.
## Alternatives Considered
- **Direct `kubectl apply`**: Rejected — any manual change to the cluster would persist indefinitely; no drift detection or automatic reconciliation.
- **Helm install from Jenkins directly**: Rejected — Helm state would live only in the cluster's release history, not in Git; no GitOps audit trail or rollback via git revert.
- **Skipping step 2 (app-of-apps refresh)**: Not a conscious alternative — the hard requirement emerged from debugging. For new services, step 2 must run before step 3 because the ArgoCD Application object doesn't exist yet; if step 3 (Helm push) runs first, step 4 (sync) targets a non-existent application and fails silently.
## Consequences
**Positive:**
- Every deployment is a Git commit — rollback is a git revert, and the cluster state is always reproducible from Git history.
- ArgoCD continuously reconciles cluster state — manual `kubectl apply` changes are automatically reverted, preventing configuration drift.
- Deployment failures are localised: the 4-step sequence makes it clear which step failed (argo-config push? app-of-apps refresh? Helm push? sync?) for faster debugging.
**Negative:**
- The 4-step sequence is opaque without documentation — engineers debugging a deploy failure must know which step corresponds to which operation.
- Steps 2 and 4 being non-interchangeable is tribal knowledge; swapping them for new services causes a silent sync failure that is hard to diagnose.
- ArgoCD dependency: if ArgoCD is degraded, all deployments are blocked regardless of build success.
**Neutral:**
- The sequence touches two separate Git repositories (devops-argo-config and helm-repo) in a single pipeline run — partial failures leave one repo updated and the other stale.
## Constraints
ArgoCD was the organisational standard for GCP deployments. The 4-step sequence was designed to handle both the case of existing services (steps 1, 3, 4 are the hot path) and new services being onboarded for the first time (step 2 is required to create the Application object before step 4 can sync it).
## Current Assessment
**Still appropriate** — no changes needed.
## Notes
- Key file: `src/com/meesho/stages/deployArgoCD.groovy` (480+ lines) — all 4 steps are defined here
- The step ordering constraint is documented in CLAUDE.md and `docs/tribal-knowledge.md` (TK#10) as load-bearing tribal knowledge
- See also: `docs/wiki/pages/deploy/argocd-sync.md` for a detailed walkthrough of each step
@@ -0,0 +1,56 @@
# ADR-0008: Canary Deploy Mandatory for Tier-1 (sp0/up0) Services in Production
**Status:** Accepted
**Category:** RELIABILITY
**Date decided:** Mid-project
**Date documented:** 2026-05-12
## Context
Meesho's services are classified by priority tier (`sp0`, `up0`, `sp1`, `up1`, etc.). Tier-1 services (`sp0`/`up0`) handle the highest traffic volumes and are critical to core business flows. A bad deploy that hits 100% of production traffic on a Tier-1 service has a catastrophic blast radius — full outage, revenue impact, and customer-facing failure. Teams were inconsistently configuring canary rollouts: some enabled them, some skipped them, and some configured them with `skipAnalysis: true` which bypasses the automated rollout analysis.
This inconsistency was the contributing factor in at least one production incident where a bad deploy on a Tier-1 service reached full traffic before the issue was detected.
## Decision
`deployArgoCD.groovy` enforces canary deployment as a hard requirement for all services with `priority_v2: sp0` or `priority_v2: up0` deploying to the `prd` environment. The enforcement checks:
1. `canary.enabled: true` must be set
2. `canary.skipAnalysis: false` — analysis cannot be bypassed
3. `canary.enableManualPromotion: true` — a human must promote the canary to full traffic
If any of these conditions are not met, the deployment is blocked with an explicit error: `"Enable canary and retry"`. Enforcement is applied at the library level — service teams cannot override it.
## Alternatives Considered
- **Documentation and guidelines only**: Rejected — teams were already aware of canary best practices but inconsistently applied them; a documented recommendation had failed to produce uniform behaviour.
- **Enforcement in Ringmaster only**: Considered but rejected — enforcement at the library level means it applies to all deploy paths, including any future tooling that calls `deployArgoCD.groovy`.
- **Enforce for all services, not just sp0/up0**: Considered but rejected as too disruptive — lower-priority services have smaller blast radii and the overhead of canary analysis was not justified for all tiers.
## Consequences
**Positive:**
- Tier-1 bad deploys cannot reach 100% of production traffic without a human promotion step.
- Canary analysis (metrics, error rate) runs automatically before promotion, catching regressions before they impact all users.
- Enforcement is consistent across all Tier-1 services — no team can skip it.
**Negative:**
- Canary rollouts add time to Tier-1 deployments — promotion requires human action, which can delay hotfixes.
- The hotfix path (`hotfix/*` branches) sets `skipAnalysis: true` to allow bypassing canary analysis in emergencies, which re-introduces the risk for the hotfix scenario.
- Services that newly cross the sp0/up0 threshold must configure canary before their next prd deploy or they will be blocked.
**Neutral:**
- The enforcement only applies to non-cron, non-worker, non-scheduler, non-consumer deployments — background jobs are excluded.
## Constraints
A production incident on a Tier-1 service drove this decision. Post-incident, the risk of leaving canary configuration to team discretion was deemed unacceptable. The library-level enforcement was the fastest way to guarantee coverage across all affected services without requiring each team to update their configuration proactively.
## Current Assessment
**Still appropriate** — no changes needed.
## Notes
- Key file: `src/com/meesho/stages/deployArgoCD.groovy:407-429` — canary enforcement block
- Hotfix bypass: `value_binding1['canary']['skipAnalysis'] = (env.hot_fix) ? true : ...` at line 355 — hotfixes can bypass canary analysis
- The `addHeadless` flag and Node services are also excluded from enforcement
@@ -0,0 +1,53 @@
# ADR-0009: JVM Heap Auto-Derived from Pod memory_request
**Status:** Accepted
**Category:** RELIABILITY
**Date decided:** Mid-project
**Date documented:** 2026-05-12
## Context
Java services running in Kubernetes pods are subject to two memory limits: the pod's `memory_limit` (enforced by the kubelet — exceed it and the pod is OOM-killed) and the JVM's heap size (`-Xmx`). By default, the JVM sets heap to 1/4 of the physical RAM it detects — but inside a container, it detects the node's physical RAM, not the pod's memory limit. A Java service in a pod with `memory_limit: 2Gi` running on a 64Gi node would default to a 16Gi heap, far exceeding its limit and triggering immediate OOM kill.
Meesho had multiple incidents where Java services were OOM-killed because:
1. The JVM was using the wrong default (node RAM, not pod limit)
2. Teams were setting `-Xmx` manually but forgetting to update it when `memory_request` changed
3. Teams were setting `-Xmx` too high, causing heap to exceed the pod limit
## Decision
`deployArgoCD.groovy` automatically computes `xms` and `xmx` from the service's `memory_request` value in `deployment.yaml`. The derived values are injected into the Helm chart at deploy time. Services do not need to set `-Xmx` in `JAVA_OPTS` manually. The escape hatch `jvm_memory_override: true` in `deployment.yaml` allows a service to opt out and set its own JVM flags.
## Alternatives Considered
- **Require teams to set -Xmx manually**: Rejected — teams consistently forgot to update `-Xmx` when changing `memory_request`, causing OOM kills after pod resource changes. This happened across multiple services.
- **JVM container awareness flag (-XX:+UseContainerSupport)**: This flag (available in JDK 11+) allows the JVM to read the cgroup limit instead of physical RAM. Not adopted as the primary solution because it requires all services to use JDK 11+ and the flag needs to be explicitly set in each service's startup config — still a per-service manual step.
- **Fixed default heap values**: Rejected — services have wildly different memory requirements; a fixed default would be wrong for most.
## Consequences
**Positive:**
- Java services cannot be OOM-killed due to JVM heap misconfiguration — the heap is always proportional to the pod's actual memory allocation.
- Teams never need to update `-Xmx` manually when changing `memory_request` — the library keeps them in sync automatically.
- Eliminates a whole class of incident: "service OOM-killed because someone bumped memory_request but forgot to update -Xmx."
**Negative:**
- The auto-derived heap may not be optimal for services with unusual heap vs non-heap memory ratios (e.g., services with large off-heap caches). These services must use `jvm_memory_override: true`.
- The calculation logic is not immediately transparent to service teams — they may not know why their `-Xmx` is what it is.
**Neutral:**
- If `deployment_args` already contains an `-Xmx` or `-Xms` flag, the auto-derive reads and preserves those values rather than overwriting them. `jvm_memory_override` is the clean opt-out for services that need full control.
## Constraints
Repeated OOM incidents across multiple Java services drove this decision. The library-level fix was preferred over per-service remediation because the root cause was systemic (wrong JVM defaults in containers) and would recur as long as teams configured heap manually.
## Current Assessment
**Still appropriate** — no changes needed.
## Notes
- Key file: `src/com/meesho/stages/deployArgoCD.groovy:205-268``xms`/`xmx` calculation
- Opt-out: set `jvm_memory_override: true` in `deployment.yaml` to manage JVM flags manually
- Documented in CLAUDE.md: "JVM memory: `deployArgoCD.groovy` auto-calculates `xmx`/`xms` from pod `memory_request`."
@@ -0,0 +1,59 @@
# ADR-0010: Cloud-and-branch namespaced artifact paths
**Status:** Accepted
**Category:** DATA
**Date decided:** Project inception
**Date documented:** 2026-05-13
## Context
Every service build in `devops-lib` produces a deployable artifact (Maven JAR / language-equivalent) plus a Docker image; the pipeline must decide where to store these and whether a re-run on the same commit can short-circuit a rebuild. The library was written when both AWS and GCP backends were on the table, so the storage layer was parameterised by provider scheme; in practice the platform has since standardised on GCP and the `s3://` code paths are vestigial. PRs and release branches share the same Jenkins jobs but have very different trust levels — a PR artifact must never be promotable to a production tag.
## Decision
Artifacts are stored under fully-namespaced paths of the form `<scheme>://<bucket>/<repo_name>/<branch_name>/<TAG>/`, where `<scheme>` is `gs://` in active use (`s3://` branches remain in the codebase but are not executed today). Release branches (`main` / `master` / `gcp-main`) and `develop` reuse cached artifacts on re-run via `checkS3()`; PR builds intentionally bypass that check and force a fresh build every time.
## Alternatives Considered
No alternatives were explicitly evaluated by the team during this interview. The branch-namespaced layout was the day-one design and has not been revisited.
## Consequences
**Positive:**
- A PR's image cannot be confused with a release image at the bucket-path level — provides a structural guarantee against accidental promotion.
- Re-running a build on the same release branch is free (artifact reuse), keeping incremental commits cheap.
- Multi-cloud scheme prefix is harmless even when only one cloud is active; switching back would be a config change, not a rewrite.
**Negative:**
- `s3://` code paths sit unused in `buildMaven.groovy` and friends — invisible tech debt that confuses new readers and grows the surface area for stale-config bugs.
- The `(branch_name, TAG)` key means renaming a branch or rebasing a PR can leak artifacts into the wrong namespace if `branch_name` is computed loosely.
**Neutral:**
- Cache reuse is implicit (artifact-exists ⇒ skip build) rather than declared — see ADR-0005 for the config-only fast-path that uses the same mechanism.
## Constraints
- Branch identity is the cache key, so `branch_name` must be a stable string for the lifetime of a build chain. PR-target detection (`env.CHANGE_ID`) is load-bearing here.
- AWS code paths exist for historical reasons; today's platform is GCP-only and the team has not undertaken a cleanup pass.
## Current Assessment
- **Adequate with caveats** — the strategy is sound; the vestigial AWS branches are noise that should be removed in a separate cleanup.
## Related Decisions
- [ADR-0005: Config-Only Change Detection — Skip Binary Build, Reuse Latest Image](0005-config-only-change-detection-skip-build.md) — the same artifact-existence check powers the config-only fast-path.
## Notes
- Key files: `src/com/meesho/stages/buildMaven.groovy`
- The `s3://` branches in `buildMaven.groovy` are dead code in current production — flag for cleanup, not for documentation as an alternative.
- Discovery id: DATA-1
<!-- adr-generator-meta
discovery_id: DATA-1
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:10:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
@@ -0,0 +1,66 @@
# ADR-0011: Build-user identity routes post-build callbacks
**Status:** Accepted
**Category:** COMMUNICATION
**Date decided:** Early on
**Date documented:** 2026-05-13
## Context
Once a build completes, the pipeline must (a) notify the service team, and (b) tell the deployment-orchestration layer that a new image is ready to roll out. Multiple orchestrators consume this signal — Ringmaster and Turbo-Turtle (the actively maintained CD systems), the legacy Deployment Tracker (still backing some downstream tooling), and a toolchain-environment build-callback service used by Node toolchain builds. Each consumer has a different API contract, a different Slack message shape, and a different policy on whether the team channel should be notified at all. The shared library is invoked by both bot-driven CD (Ringmaster / Turbo-Turtle trigger builds via dedicated bot users) and human-driven Jenkins runs (manual deploys, hotfixes, retries by anyone with allowedUsers), so there is no single contract that fits all callers.
## Decision
`notify.groovy` routes by the Jenkins `build_user`, which is set by whoever triggered the build:
- `build_user == "ringmaster-bot"` or `"turbo-turtle"` → call `deployRingmaster.run()` and emit a Ringmaster-flavored Slack message (production gets a richer message with the deploy URL); the generic team notification is suppressed.
- Any other user (manual deploy, hotfix, retry) → fall through to a generic `slackSend` on the team's notify channel.
- `INFRA_ENV == 'toolchain'` → take a completely separate path that POSTs to the toolchain build-callback service and returns early, bypassing the build-user routing entirely.
In parallel, for release branches (`gcp-main` / `main` / `gcp-master` / `master` / `farmiso-main`), the legacy Deployment Tracker (`postTrackingApi`) is always called, and the Ringmaster history-DB (`postTrackingRingmasterApi`) is called when `env.SERVICES` is set.
## Alternatives Considered
No alternatives were explicitly evaluated during this interview. Routing on `build_user` was chosen because it is the only signal available without coordinating an extra config flag with every consumer service.
## Consequences
**Positive:**
- New consumers can plug in without re-wiring every consumer service's `config.yaml` — they only need to claim a dedicated bot identity.
- The legacy Deployment Tracker path stays intact for backward compatibility while new traffic flows through Ringmaster.
- Manual / human-triggered builds get the generic Slack notification path so engineers always see a team-channel message regardless of CD orchestrator.
**Negative:**
- The routing is implicit and not documented in `config.yaml` — a reader of a service's pipeline cannot tell which CD orchestrator will get the callback without grepping `notify.groovy`.
- Renaming or replacing either bot identity is breaking: the strings `"ringmaster-bot"` and `"turbo-turtle"` are hard-coded compare targets (the existing `NEVER DO` list in `CLAUDE.md` calls this out for `ringmaster-bot`).
**Neutral:**
- The `INFRA_ENV == 'toolchain'` branch sits outside the build-user routing — it is a parallel routing axis (build environment, not trigger identity).
## Constraints
- Caller identity is the only signal available at notify time — no config flag is in scope.
- Strict string comparison on bot usernames couples this code to Ringmaster / Turbo-Turtle naming.
## Current Assessment
- **Adequate with caveats** — the routing works and survives new CD orchestrators being added, but the dispatch should ideally be table-driven rather than chained `if`s, and the bot-name strings should be configurable rather than hard-coded.
## Related Decisions
- [ADR-0006: Ringmaster as Mandatory Build Trigger Gate](0006-ringmaster-mandatory-build-trigger-gate.md) — Ringmaster also gates the *trigger* side of builds; this ADR covers the *callback* side.
- [ADR-0007: GitOps Deployments via Strict 4-Step ArgoCD Sync Sequence](0007-gitops-via-argocd-4-step-sync-sequence.md) — the deploy mechanism that Ringmaster invokes downstream.
## Notes
- Key files: `src/com/meesho/stages/notify.groovy`, `src/com/meesho/stages/deployRingmaster.groovy`
- The release-branch dual-write (`postTrackingApi` + `postTrackingRingmasterApi`) coexists with the build-user routing but is orthogonal to it (gated on `BRANCH_NAME` and `env.SERVICES`).
- Discovery id: COMMUNICATION-1
<!-- adr-generator-meta
discovery_id: COMMUNICATION-1
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:15:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
@@ -0,0 +1,61 @@
# ADR-0012: String-interpolated Helm values from user config.yaml
**Status:** Accepted
**Category:** COMMUNICATION
**Date decided:** Project inception
**Date documented:** 2026-05-13
## Context
ArgoCD deploys are driven by Helm charts whose `values.yaml` files are produced per-build by `deployArgoCD.groovy`. The inputs are a service's `deployment.yaml` plus a handful of pipeline-derived fields (image tag, pod resources, JVM heap, etc.). Helm chart `values.yaml` is itself a templating surface — Helm's own `{{ .Values.x }}` syntax reads these files at install time — so anything produced here is interpreted as a template by the next layer, not as plain data.
## Decision
`deployArgoCD.groovy` builds the rendered `values.yaml` by string-substituting fields into a stub template via chained `.replaceAll()` calls, rather than constructing it via a YAML library or a typed DTO and re-serialising. The team treats Helm's chart-values surface as a template-on-template stack: re-serialising via a YAML library would re-introduce template-syntax escaping problems (quoting `{{ }}`, preserving multi-line string semantics, handling Helm-specific structural tags) — keeping the entire pipeline string-native is the simpler invariant.
## Alternatives Considered
No alternatives were explicitly evaluated by the team during this interview. The "use a typed YAML library / DTO and re-serialise" approach was acknowledged as the obvious counter-proposal but was rejected on the template-on-template grounds above.
## Consequences
**Positive:**
- The renderer stays a one-layer string substitution — easy to read, easy to debug from a Jenkins console log.
- No risk of a typed re-serialisation silently re-escaping Helm `{{ }}` templates or stripping comments.
- Matches the rest of the pipeline, which is string-and-`sh` heavy.
**Negative:**
- User-supplied config values are not validated against a schema before substitution — a service's `deployment.yaml` containing unescaped quotes, colons, or newlines can produce a malformed `values.yaml` (review-learnings PR #343 flagged this).
- The implicit YAML-injection risk depends on every consumer service writing well-formed `deployment.yaml` — there is no guardrail in the library itself.
- Adding a new field requires editing the template stub AND the substitution chain in `deployArgoCD.groovy` together — easy to drift.
**Neutral:**
- The decision lives entirely in `deployArgoCD.groovy`; switching strategies in future would be local to that file.
## Constraints
- Helm chart values are interpreted as templates downstream — any solution must preserve template literals without escaping them.
- The shared library runs in the Jenkins sandbox, which constrains which Java / Groovy serialisation APIs are safely callable.
## Current Assessment
- **Adequate with caveats** — the strategy is defensible, but the lack of input-validation guardrails (the YAML-injection surface called out in review learnings) remains an open risk. A targeted schema-validation pass before substitution would mitigate it without changing the rendering strategy.
## Related Decisions
- [ADR-0007: GitOps Deployments via Strict 4-Step ArgoCD Sync Sequence](0007-gitops-via-argocd-4-step-sync-sequence.md) — the deploy mechanism that consumes the rendered `values.yaml`.
- [ADR-0009: JVM Heap Auto-Derived from Pod memory_request](0009-jvm-heap-auto-derived-from-pod-memory-request.md) — another pipeline-derived input to the same `values.yaml`.
## Notes
- Key files: `src/com/meesho/stages/deployArgoCD.groovy`
- Open risk: review-learnings PR #343 flagged the YAML-injection surface — input validation is the recommended mitigation.
- Discovery id: COMMUNICATION-2
<!-- adr-generator-meta
discovery_id: COMMUNICATION-2
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:18:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
@@ -0,0 +1,62 @@
# ADR-0013: Multi-zone deployables gated out of direct Jenkins ArgoCD
**Status:** Accepted
**Category:** RELIABILITY
**Date decided:** Alongside the multi-zone initiative
**Date documented:** 2026-05-13
## Context
Meesho ran a multi-zone initiative to deploy production services across two GCP zones in parallel and split traffic between them. The initiative immediately exposed that not every workload type is safe to multiply across zones: schedulers, cron jobs, and consumers cause correctness issues when more than one zone runs them at the same time; `cache` and `database` service types are explicitly blocked by Turbo-Turtle's `ValidateDeploymentConfigActivity`; some workloads (e.g. Deepgram, with volume affinity that pins it to a single zone) can't go multi-zone at all. Meanwhile, `deployArgoCD.groovy` in this shared library only knows how to call `argocd sync` against a single application — it has no concept of split-by-service-type or per-zone sequencing.
## Decision
`deployArgoCD.groovy` fetches the `multizone-enabled-repos` whitelist from `Meesho/whitelists` at the start of every build. If the current deployable is on the list, the pipeline aborts with `"Multi-zone enabled for this deployable. Please use Ringmaster for deployment."` rather than attempting a sync. Multi-zone-enabled services are required to go through Ringmaster, which owns the split-deploy logic, per-service-type validation, and zone-affinity awareness.
## Alternatives Considered
No alternatives were explicitly evaluated by the team during this interview. The gate was introduced alongside the multi-zone initiative itself, not retrofitted after an incident.
## Consequences
**Positive:**
- Direct Jenkins ArgoCD is structurally incapable of getting multi-zone wrong because it never gets the chance — the gate fails closed.
- The split-deploy logic and the service-type-validation logic live in exactly one place (Ringmaster); we do not maintain two copies.
- A team can flip a service to multi-zone by adding it to `multizone-enabled-repos.yaml` without modifying `devops-lib` — the policy change takes effect on the next build.
**Negative:**
- Engineers who routinely use `cicd-` Jenkins jobs see an unfamiliar refusal once their service is added to the whitelist; the error message is the only signal pointing them at Ringmaster.
- The library has no way to attempt a partial deploy or to surface what would-have-been-deployed; the gate is binary.
**Neutral:**
- The decision lives across two repos — this code in `devops-lib`, the source-of-truth list in `Meesho/whitelists`. See ADR-0003 for the broader policy-in-a-separate-repo pattern.
## Constraints
- Multi-zone correctness requires service-type awareness that the Jenkins ArgoCD path does not have.
- The whitelist is fetched fresh per build (ADR-0004), so policy changes in `Meesho/whitelists` take effect on the next build with no library release.
## Current Assessment
- **Still appropriate** — fail-closed gating is the right posture for a capability the library cannot safely implement.
## Related Decisions
- [ADR-0003: Policy Exceptions Controlled by a Separate Meesho/whitelists Repo](0003-policy-exceptions-in-separate-whitelist-repo.md) — explains the whitelist-source-of-truth pattern this gate uses.
- [ADR-0004: Whitelist Cloned Fresh on Every Build — No Caching](0004-fresh-whitelist-clone-per-build.md) — why this list takes effect immediately.
- [ADR-0006: Ringmaster as Mandatory Build Trigger Gate](0006-ringmaster-mandatory-build-trigger-gate.md) — Ringmaster's parallel role on the trigger side.
- [ADR-0011: Build-user identity routes post-build callbacks](0011-build-user-identity-routes-post-build-callbacks.md) — the callback flow that Ringmaster uses on success.
## Notes
- Key files: `src/com/meesho/stages/deployArgoCD.groovy`
- Whitelist source: `https://github.com/Meesho/whitelists/blob/main/multizone-enabled-repos.yaml`
- Discovery id: RELIABILITY-1
<!-- adr-generator-meta
discovery_id: RELIABILITY-1
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:21:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
@@ -0,0 +1,58 @@
# ADR-0014: Open Dependabot CRITICAL alerts block builds
**Status:** Accepted
**Category:** RELIABILITY
**Date decided:** Mid-project
**Date documented:** 2026-05-13
## Context
Every Meesho service ships its source dependencies as part of its container image. GitHub's Dependabot continuously scans those dependency manifests against the GitHub Advisory Database and surfaces alerts by severity. Without an enforcement teeth in the pipeline, CRITICAL CVEs can sit open for arbitrary time — merge gates can be bypassed (admin merges, hotfixes, repos that don't enforce branch protection), and a periodic audit only tells security org-wide rather than putting accountability on the team that's actively trying to ship.
## Decision
`deployArgoCD.groovy:dependabotCriticalCheck()` calls the GitHub Dependabot Alerts API for the repo on every build. If the API returns any open alert with severity `CRITICAL`, the pipeline aborts with `"Critical vulnerabilities found in repo: <name>. Please resolve the alerts marked with CRITICAL here and retry: <github dependabot URL>"`. There is no whitelist, no override flag, and no manual bypass available from the pipeline side.
## Alternatives Considered
N/A — organizational mandate. The Meesho security org required hard enforcement of the CRITICAL-CVE SLA on shipping code; the build-time gate is the implementation choice that satisfies it.
## Consequences
**Positive:**
- The deploying team is forced to act on the CVE (fix, escalate, or coordinate with the dependency owner) before they can ship — accountability lands on whoever is actively trying to push, not on a central security team.
- Confirmed firing in practice on real services (Farmiso-Backend, supplier_platform_insights, meesho-web-reels, supplier_platform_payouts as seen in #devops-tech) — the gate is not theoretical.
**Negative:**
- Engineers blocked by a CRITICAL alert mid-deploy have no in-pipeline bypass — even for hotfixes that are unrelated to the vulnerable dependency.
- The check depends on GitHub Dependabot's classification; a false-positive at CRITICAL would force a real outage detour.
**Neutral:**
- The check is per-build, not per-PR, so a recently-disclosed CVE can suddenly start blocking deploys for repos that have not changed.
## Constraints
- Meesho security org policy: open CRITICAL Dependabot alerts must be resolved before code is shipped. The build-time gate is the enforcement surface for that policy.
- Dependabot must remain enabled on every repo for the gate to be meaningful; that side of the policy lives in repo settings, not in this library.
## Current Assessment
- **Still appropriate** — the mandate stands and the gate is the correct enforcement point.
## Related Decisions
- [ADR-0006: Ringmaster as Mandatory Build Trigger Gate](0006-ringmaster-mandatory-build-trigger-gate.md) — another build-time hard gate; same architectural pattern of "fail closed at the pipeline boundary."
## Notes
- Key files: `src/com/meesho/stages/deployArgoCD.groovy`
- The check is unconditional — there is no whitelist of repos exempted from this gate.
- Discovery id: RELIABILITY-2
<!-- adr-generator-meta
discovery_id: RELIABILITY-2
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:23:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
@@ -0,0 +1,59 @@
# ADR-0015: Node install logic paired across buildNode.groovy and Dockerfile
**Status:** Accepted
**Category:** PATTERN
**Date decided:** Early on
**Date documented:** 2026-05-13
## Context
Meesho Node services use both `npm` and `pnpm` depending on the repo. Some teams need to pass extra flags (e.g. `--legacy-peer-deps`) and the shared library cannot anticipate every combination. The install step has to run inside the Docker build (the image needs `node_modules` baked in), but the choice of *which* package manager and *which* flags to run is information the Dockerfile alone can't recover from a clean container — it has to be told.
## Decision
The install step is split deliberately across the two files. `src/com/meesho/stages/buildNode.groovy` does the detection: it reads `config.yaml`, sees whether the repo uses `npm` or `pnpm`, and resolves the optional `npm_install_arg` override; it then passes the resolved values to `resources/com/meesho/node-Dockerfile` as Docker build-args. The Dockerfile does the execution: it consumes those build-args and runs the actual install, falling back to a sane default in an `else` branch when no value was passed. Detection lives at the layer that can see the config; execution lives at the layer that runs inside the container.
## Alternatives Considered
No alternatives were explicitly evaluated by the team during this interview. The split mirrors what is naturally separable — config interpretation vs runtime install — and has not been revisited.
## Consequences
**Positive:**
- Teams can override the install command for their service through `npm_install_arg` in `config.yaml` without forking the library Dockerfile — observed in #devops-tech (Nov 2025) when a team added `--legacy-peer-deps` purely via config.
- The Dockerfile's `else` fallback means a developer can `docker build` locally without Jenkins or `config.yaml` and still get a working install.
- Neither layer needs to know more than it actually does: Groovy doesn't run `npm`, the Dockerfile doesn't read YAML.
**Negative:**
- Any change to install behaviour has to land in both files at once (tribal-knowledge #11). The pair-edit invariant is real but has not yet caused a production incident.
- Build-args are stringly-typed, so a typo on either side fails late — at install time inside the Docker build.
**Neutral:**
- The "detection in Groovy, execution in Dockerfile" split is mirrored elsewhere in `devops-lib` (e.g. Maven, Go); Node just happens to be the most config-driven and therefore the most visible example.
## Constraints
- The package-manager choice has to be visible at Docker-build time inside the container; build-args are the cleanest way to inject it.
- The library must support repos that build locally (no Jenkins, no `config.yaml`) for developer ergonomics.
## Current Assessment
- **Still appropriate** — the split works, the override knob is being used as intended, and no incident has surfaced. A future hardening could add a CI check that any PR touching the install logic on one side also touches the other, but it is not load-bearing today.
## Related Decisions
None directly. The "detection in Groovy, execution in container" split is a pattern repeated in other build stages (Maven, Go), but each has its own per-language ADR scope.
## Notes
- Key files: `src/com/meesho/stages/buildNode.groovy`, `resources/com/meesho/node-Dockerfile`
- `npm_install_arg` in `config.yaml` is a documented public extension point — see service-team usage in #devops-tech (Nov 2025) for the `--legacy-peer-deps` case.
- Discovery id: PATTERN-2
<!-- adr-generator-meta
discovery_id: PATTERN-2
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:28:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
@@ -0,0 +1,60 @@
# ADR-0016: Per-environment Helm chart versioning (values_v2 vs values_v3)
**Status:** Accepted
**Category:** INFRA
**Date decided:** During the multi-zone initiative
**Date documented:** 2026-05-13
## Context
Production was the first (and so far only) environment where Meesho rolled out multi-zone deployments — running services in two GCP zones in parallel with split-deploy and per-service-type validation (see ADR-0013). Multi-zone awareness required a new shape of Helm chart values: per-zone overlay files (`gcp-ase1a-values.yaml`, etc.), service-type metadata, and additional tuning. Lower environments (`stg`, `int`, `ftr`) do not run multi-zone today and have no plan to. Forcing them onto the multi-zone-shaped chart would either require multi-zone setup they don't have, or carry chart fields they never use.
## Decision
`constructParam.groovy` picks the Helm chart values path per environment: `helmChartsPath = (env == 'prd') ? 'values_v3' : 'values_v2'`. Production reads from `values_v3/` in `devops-helm-charts` (multi-zone-aware); every other environment reads from `values_v2/` (single-zone). Both versions are actively maintained — `values_v2` is the live chart for non-prod, not a deprecated legacy path.
## Alternatives Considered
No alternatives were explicitly evaluated by the team during this interview. A unified chart that handles both single-zone and multi-zone via conditional logic was the obvious counter-proposal but was not taken; the team kept the two versions side-by-side instead.
## Consequences
**Positive:**
- Non-prod chart stays simple — no multi-zone-only fields polluting the values tree for engineers and reviewers who don't need them.
- Multi-zone schema can evolve in `values_v3` without coordinating breaking changes against non-prod chart consumers.
- Production deploys exercise a chart shape that matches production's runtime topology exactly; no "unused config" surface.
**Negative:**
- A service that exists in prd has its values defined in two places (`values_v2/<bu>/.../values.yaml` and `values_v3/<bu>/.../values.yaml`); a config change relevant to both has to land in both, and drift between them is silent.
- Engineers debugging an issue have to know which version their environment uses before they can find the right file.
**Neutral:**
- The decision lives in one line of `constructParam.groovy`; flipping a new env onto `values_v3` is trivial when its multi-zone story is ready.
## Constraints
- Multi-zone requires per-zone overlay files and service-type validation that `values_v2` does not have.
- Non-prod environments do not run multi-zone and have no roadmap to; the v3 shape would carry dead configuration there.
## Current Assessment
- **Adequate with caveats** — splitting by environment maps cleanly to the runtime topology, but the dual maintenance burden grows linearly with active services and is invisible to anyone not in the DevOps loop. Worth revisiting if/when non-prod gains multi-zone.
## Related Decisions
- [ADR-0013: Multi-zone deployables gated out of direct Jenkins ArgoCD](0013-multi-zone-deployables-gated-out-of-direct-jenkins-argocd.md) — the multi-zone initiative that drove the `values_v3` rollout.
- [ADR-0007: GitOps Deployments via Strict 4-Step ArgoCD Sync Sequence](0007-gitops-via-argocd-4-step-sync-sequence.md) — the deploy mechanism that consumes whichever chart values path is selected here.
## Notes
- Key files: `src/com/meesho/utilities/constructParam.groovy`
- Chart source: `https://github.com/Meesho/devops-helm-charts``values_v2/` and `values_v3/` are sibling top-level directories.
- Discovery id: INFRA-1
<!-- adr-generator-meta
discovery_id: INFRA-1
run_id: 90b9a400-266c-4786-b793-d811efa99276
last_completed_at: 2026-05-13T12:31:00Z
mode: create
cache_uri: gs://ai-blitz-agent-readability/adr/devops-lib/adr-discovery.json
-->
+43
View File
@@ -0,0 +1,43 @@
# Architecture Decision Records
This directory contains Architecture Decision Records (ADRs) for devops-lib.
These were retroactively documented on 2026-05-12 through codebase analysis and developer interviews.
## What is an ADR?
An Architecture Decision Record captures an important architectural decision along with its context, alternatives, and consequences. They help new team members understand WHY the system is built the way it is — not just what it does.
## Decisions
| # | Decision | Category | Status | Assessment |
|---|----------|----------|--------|------------|
| [0001](0001-single-shared-library-for-all-services.md) | Single shared library consumed by all services | PATTERN | Accepted | Still appropriate |
| [0002](0002-branch-name-as-sole-environment-selector.md) | Branch name as sole environment selector | PATTERN | Accepted | Still appropriate |
| [0003](0003-policy-exceptions-in-separate-whitelist-repo.md) | Policy exceptions in separate Meesho/whitelists repo | PATTERN | Accepted | Still appropriate |
| [0004](0004-fresh-whitelist-clone-per-build.md) | Whitelist cloned fresh on every build — no caching | PATTERN | Accepted | Still appropriate |
| [0005](0005-config-only-change-detection-skip-build.md) | Config-only change detection — skip build, reuse latest image | PATTERN | Accepted | Still appropriate |
| [0006](0006-ringmaster-mandatory-build-trigger-gate.md) | Ringmaster as mandatory build trigger gate | PATTERN | Accepted | Still appropriate |
| [0007](0007-gitops-via-argocd-4-step-sync-sequence.md) | GitOps via strict 4-step ArgoCD sync sequence | INFRA | Accepted | Still appropriate |
| [0008](0008-canary-mandatory-for-tier1-services-in-prd.md) | Canary deploy mandatory for Tier-1 (sp0/up0) services in prd | RELIABILITY | Accepted | Still appropriate |
| [0009](0009-jvm-heap-auto-derived-from-pod-memory-request.md) | JVM heap auto-derived from pod memory_request | RELIABILITY | Accepted | Still appropriate |
| [0010](0010-cloud-and-branch-namespaced-artifact-paths.md) | Cloud-and-branch namespaced artifact paths | DATA | Accepted | Adequate with caveats |
| [0011](0011-build-user-identity-routes-post-build-callbacks.md) | Build-user identity routes post-build callbacks | COMMUNICATION | Accepted | Adequate with caveats |
| [0012](0012-string-interpolated-helm-values-from-user-config.md) | String-interpolated Helm values from user config.yaml | COMMUNICATION | Accepted | Adequate with caveats |
| [0013](0013-multi-zone-deployables-gated-out-of-direct-jenkins-argocd.md) | Multi-zone deployables gated out of direct Jenkins ArgoCD | RELIABILITY | Accepted | Still appropriate |
| [0014](0014-open-dependabot-critical-alerts-block-builds.md) | Open Dependabot CRITICAL alerts block builds | RELIABILITY | Accepted | Still appropriate |
| [0015](0015-node-install-logic-paired-across-buildnode-groovy-and-dockerfile.md) | Node install logic paired across buildNode.groovy and Dockerfile | PATTERN | Accepted | Still appropriate |
| [0016](0016-per-environment-helm-chart-versioning.md) | Per-environment Helm chart versioning (values_v2 vs values_v3) | INFRA | Accepted | Adequate with caveats |
## How to use
- **New to the project?** Read these to understand why things are the way they are before touching the code.
- **Making a change?** Check if an existing ADR covers the area you're modifying — especially ADR-0003 (whitelists), ADR-0004 (whitelist caching), ADR-0007 (ArgoCD sequence order), ADR-0013 (multi-zone gating), ADR-0014 (Dependabot CRITICAL gate).
- **Making a new architectural decision?** Create a new ADR using the template in an existing file as a guide.
## Adding a new ADR
1. Copy an existing ADR as a template
2. Use the next sequential number (currently: 0017)
3. Fill in all sections — if you don't have info for a section, say so rather than leaving it blank
4. Get a review from the DevOps Platform team lead
5. Add a row to the table above
+254
View File
@@ -0,0 +1,254 @@
<!--
Auto-generated by /meesho-init Phase 8.
Long-form architecture for devops-lib. CLAUDE.md links here but does not
duplicate the contents.
-->
# devops-lib — Architecture
A Jenkins shared library that every Meesho service repo consumes via
`@Library('devops-lib') _` in its `Jenkinsfile`. It encapsulates the
per-language build, the Helm/ArgoCD deploy ceremony, secret + key handling,
notification routing, and security-scan integration.
## 1. High-level shape
```
service Jenkinsfile (lives in service repo)
└─ buildPipeline { repo_name: ..., build_tool: ..., maintainer: ... }
┌───────────────────────────────────┘
vars/<entry>.groovy ← global step
│ delegates to
src/com/meesho/stages/<stage>.groovy ← stage object
│ uses
src/com/meesho/utilities/<helper>.groovy
│ loads
resources/com/meesho/<template>.{yaml,Dockerfile}
```
### Module boundaries
| Directory | Owns | Outside callers |
|---|---|---|
| `vars/` | Public Jenkins global steps (the "API") | Service Jenkinsfiles |
| `src/com/meesho/stages/` | Stage logic — build, deploy, notify, security scan | `vars/` |
| `src/com/meesho/utilities/` | Pure helpers — git, params, node-pool selection, templating | Stages |
| `resources/com/meesho/` | Per-language Dockerfile + Helm template + values.yaml | Stages (rendered into service workspace) |
| `resources/org/meesho/` | Jenkins agent pod templates (`*-pod.yaml`) | `vars/` (via `libraryResource`) |
| `resources/com/meesho/validate_configs.py` | Helm-values schema validator (monolithic Python) | Stages (invoked via `sh`) |
### Architectural philosophy signals
- **Convention over configuration.** Almost every per-service behaviour is
encoded in `vars/<entry>.groovy` + the language switch in
`buildObjHelper.groovy`. New tech stacks require new files, not new flags.
- **CPS-aware Groovy.** Helpers that touch non-serialisable Java APIs (regex
engines, template engines) sit behind `@NonCPS` boundaries
(`constructTemplate._construct`). Cross those boundaries inside a
`parallel` block and you'll get a serialisation error in production.
- **Per-call freshness vs caching.** `getWhitelistedRepos()` re-clones
`Meesho/whitelists` on every invocation (no caching). The five whitelist
checks each clone independently. This is by design: a DevOps whitelist
change must take effect on the *next* build without a library release.
- **Hard-coded `'Meesho'` org.** Many helpers embed the org as a literal —
parametrising it has knock-on effects across every service Jenkinsfile.
- **Single Python file.** `validate_configs.py` (1207 lines) holds every
CAC/values-schema rule; splitting it has been flagged
([BUGS_AND_IMPROVEMENTS_REPORT §10](../BUGS_AND_IMPROVEMENTS_REPORT.md)).
## 2. Entry points (vars/)
| `vars/<file>.groovy` | Used by | Notes |
|---|---|---|
| `buildPipeline.groovy` | The legacy entry — service `Jenkinsfile` calls `buildPipeline { ... }` | Pins to `node('slave02')` (hard-coded — flagged) |
| `eksCICD.groovy` | EKS-targeted services | Calls `commonCICDFlow()` |
| `gkeCICD.groovy` | GKE-targeted services | Mirror of `eksCICD` for GCP |
| `cloudFunctionCICD.groovy` | GCP cloud-function deploys | Currently a stub (`sh 'ls -al'; echo 'Hello World'` — flagged in BUGS report) |
| `onlyPushtoJfrog.groovy` | One-off artifact push without full pipeline | JDK 11 / JDK 21 selections fall through to JDK 8 (flagged) |
| `createEKSconfigs.groovy` | Bootstrap EKS pod-template configs | |
| `gcpMigration.groovy` | One-off migration helper | |
| `log.groovy` | `log.info` wrapper used across stages | Wraps `echo` — there is no real logger |
| `stageName.groovy` | Stage-name helper | |
| `buildDockerGroovyGke.groovy` | Docker build helper for GKE | |
## 3. Stage layer (src/com/meesho/stages/)
`buildObjHelper.groovy:run(String build_tool)` is the dispatch switch:
```
build_tool → Stage class
─────────────────────────────────
maven → buildMaven
maven-* → buildMaven
gradle → buildGradle
docker → buildDocker
python-* → buildPython
node-* → buildNode
go* → buildGo
php → buildPhp
<other> → defaultBuild (silent no-op — flagged)
```
Other stage files (orthogonal to the build dispatch):
- `checkOut.groovy` — Jenkins SCM checkout. Contains a method named
`chekoutSubmodule` (misspelled — flagged) at line 20.
- `deployArgoCD.groovy` — the deploy ceremony.
- `deployRingmaster.groovy` — Ringmaster / Turbo-Turtle callback.
- `deployJar.groovy` — non-container artifact deploy.
- `notify.groovy` — Slack notification + tracking-API callback. Reads
`config.maintainer` and `config.notify_channel` (defaults to
`ci-cd-status`).
- `securityScan.groovy` — invokes the in-house scanner. Hard-codes
`final String url = '172.31.5.29:63232/scans'` (P0 violation — flagged).
- `automationTest.groovy` — integration-test trigger.
- `hotFix.groovy` — sets `env.hot_fix = true` to skip Sonar / quality gate.
- `helmGenerator.groovy` — renders Helm values; line 103 contains a typo
(`catch (Exceptione)` — flagged).
- `multiBranchPipeline.groovy` — wires `GitHubSCMSource`.
### deployArgoCD step order (load-bearing)
```
update_argo_repo (push new app definition)
refresh_app_of_apps (sync app-of-apps so the new Application object exists)
update_helm_repo (compute xms/xmx from memory_limit, push values)
refresh_and_sync (per-service sync)
```
Steps 2 and 4 are not interchangeable — see [tribal-knowledge §10](tribal-knowledge.md).
Canary enforcement (lines 408-430): if `priority_v2 ∈ {sp0, up0}` and
`envrn == 'prd'`, the deploy fails fast unless canary is properly
configured (`canary.enabled=true`, `skipAnalysis=false`,
`enableManualPromotion=true`). No whitelist or bypass.
JVM memory (lines 220-255): `xms = xmx = memory_limit * 0.5`. There is no
`memory_request * 0.75` formula, no 64m rounding (the older claim in
tribal-knowledge has been reconciled).
## 4. Utilities (src/com/meesho/utilities/)
| File | Owns |
|---|---|
| `constructParam.groovy` | The big one. Loads CAC config, sets `env.BU`, `env.GCPProject`, `env.GCPLBProject`, computes `cicd_environment`, runs the five whitelist gates (`skip-sonar`, `app-config-disabled`, `multizone-enabled`, `allowedNonDevelopPrDeployment`, `ValidateCacConfig`). |
| `gitActions.groovy` | Clone, fetch, status — hard-codes the `Meesho` GitHub org in clone URLs and PR-merge URLs. |
| `nodePoolSelection.groovy` | Maps `cicd_environment` → node-pool selector. `dev`/`ftr`/`stg` all collapse to `${BU}-shared`. |
| `constructTemplate.groovy` | `@NonCPS` template engine wrapper over `SimpleTemplateEngine`. |
| `addSSHKey.groovy` | Writes a Jenkins SSH credential to `./id_github_jenkins`. **Does not** cat the key to stdout (reconciled — the historical PR #634 leak has been remediated). |
| `getDockerParams.groovy` | Helper to assemble `docker run` bindings. |
| `getYamlParameter.groovy` | Reads a single key from a YAML file. |
| `validateBuTeam.groovy` | Cross-checks the BU against team ownership. |
## 5. Resources
### `resources/com/meesho/`
Templates rendered into the service workspace:
- Per-language `Dockerfile` (`java-Dockerfile`, `go-Dockerfile`,
`node-Dockerfile`, `php-Dockerfile`, `python-{2.7,3.7,3.10.12,3.13}-Dockerfile`)
- Per-language Helm values (`go-values.yaml`, `node-values.yaml`,
`python-values.yaml`, `php-values.yaml`, `values.yaml`, `cron-values.yaml`)
- Per-language deployment manifests (`deployment.yaml`, `go-deployment.yaml`,
`node-deployment.yaml`, `php-deployment.yaml`, `python-deployment.yaml`,
`gradle-deployment.yaml`)
- ArgoCD Application template (`argoApp.yaml`)
- `Dockerfile` and `Jenkinsfile` fallbacks (rare path)
- `config.yaml` — default service shape consumed by `constructParam`
- `validate_configs.py` / `validate_configs_v2.py` — the schema validator
(v2 is the eventual replacement; both are referenced today)
### `resources/org/meesho/`
- `dev-pod.yaml`, `stg-pod.yaml`, `prd-pod.yaml` — Jenkins agent pod
templates loaded via `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")`.
- `templates/maven-3.3-jdk-8.sh`, `templates/node-12.22.sh` — bootstrap
shell scripts copied into the agent.
## 6. Downstream services
| Caller | Downstream | Endpoint / mechanism | Resilience |
|---|---|---|---|
| `deployArgoCD.groovy` | ArgoCD | `argocd login ${env.argoURL}:443 --grpc-web`, `argocd app sync`, `argocd app refresh` | `--http-retry-max 3 --retry-backoff-duration 1m` |
| `deployRingmaster.groovy` | Ringmaster *or* Turbo-Turtle | `POST http://turbo-turtle.meeshogcp.in/...` (chosen by `getUserId() == "ringmaster-bot"`) | None — direct `curl` |
| `notify.groovy` | Slack | `slackSend channel: ..., message: ...` | None |
| `notify.groovy` | Deployment Tracker | `POST https://deployment-tracker.meeshoint.in/...` (and `.prd.meesho.int`) | None |
| `buildMaven.groovy` / `buildNode.groovy` / `buildGo.groovy` | JFrog | `mvn deploy` / `npm publish` / artifact upload | Branch-gated: master, main, gcp-main, gcp-master |
| `buildMaven.groovy` / `buildGradle.groovy` etc. | S3 | `aws s3 cp …` | Same branch gate |
| `buildNode.groovy` | Docker registry | `docker push` | `retryDockerPush` retry wrapper |
| `buildMaven.groovy` etc. | SonarQube | `withSonarQubeEnv { … }``sonarqube-prd` | Skipped via `skip-sonar-whitelist.yaml` for Maven prd |
| `constructParam.groovy` | Vault | `vault-prd.meeshogcp.in`, `vault-dev.meeshogcp.in` | None |
| `securityScan.groovy` | In-house scanner | `POST http://172.31.5.29:63232/scans` (P0 — should be DNS, flagged) | None |
## 7. Critical invariants & gotchas
(Most are also enumerated in [docs/tribal-knowledge.md](tribal-knowledge.md);
this section captures the ones that change the *shape* of the code.)
1. **`config.yaml` is read once by `constructParam.run()`.** Everything
downstream reads from `env.*` it set. Don't introduce a second config
read; mutate `env.*` instead.
2. **`hot_fix` shortcuts.** `env.hot_fix = true` (set by `hotFix.groovy:11`)
skips Sonar, quality gates, and several validation steps in
`buildMaven` / `buildGo`. Use it deliberately, not as a "skip everything"
knob.
3. **`branch_name = 'repo'`** appears as a hard-coded string in
`buildGradle.groovy:252,285` (flagged in BUGS report). Do not assume
`branch_name` is dynamic — the comparison against `'master'/'main'`
never matches there.
4. **`rm -rf *`** appears in six locations across `buildPython`, `buildMaven`,
`buildGradle` (flagged). Be aware of the working directory when adding
stages near these — there is no directory guard.
5. **`generic catch (Exception e)`** is used ~87 times across stages
(flagged). New code should prefer typed exceptions, but existing handlers
suppress everything — be cautious assuming a stage "succeeded".
## 8. Configuration touch points
| Config | Where read | Drives |
|---|---|---|
| `config.bu` | `constructParam:178,255,308,318` | Helm chart path, ArgoCD namespace, GCP project name |
| `env.INFRA_ENV` | `eksCICD:57`, `createEKSconfigs:5`, `onlyPushtoJfrog:4` | Jenkins agent pod template |
| `env.CHANGE_ID` | `constructParam:107-110` | PR vs branch detection; `cicd_environment` remap (`prd→int` for main/master PRs, `stg→ftr` for develop PRs) |
| `env.hot_fix` | `buildGo:25-28`, `buildMaven:37-40`, `hotFix:11` | Skip Sonar / quality gate |
| `param.build_tool` | `buildObjHelper:run`, `vars/buildPipeline:12` | Stage class selection |
| `param.maintainer` | `notify:9,31` | Slack mention |
| `param.skip_test` | `buildMaven:17,206-208` | Maven `-DskipTests` |
| `param.skip_sonar` | `buildMaven:18,240-243` | Sonar bypass |
| `param.skip_security_scan` | `securityScan:7-9` | Security scan bypass |
| `param.skip_notify` | `notify:10,26-28` | Slack bypass |
| `param.notify_channel` | `notify:11` | Slack channel (default `ci-cd-status`) |
| `param.push_to_jfrog` | `buildMaven:19,377` | Allow non-default-branch JFrog push |
| `param.push_to_s3` | `buildMaven:20,462` | Allow non-default-branch S3 push |
## 9. What's missing (and known)
`BUGS_AND_IMPROVEMENTS_REPORT.md` is the authoritative catalogue. Highlights
that affect *how* you should approach changes:
- **No test suite** (P0). Don't fabricate test commands.
- **No retry on most stages** (P1) — only ArgoCD sync and Docker push retry.
- **Hard-coded IPs** (P0) — `securityScan.groovy:11` is a known violation
pending remediation.
- **Inconsistent logging** — mix of `log.info()` (which is a thin wrapper)
and bare `echo`. Prefer `log.info` for new code.
- **Commented-out blocks** — `buildPython:13-38`, `buildNode:27-31`,
`buildGradle:470-477` carry large dead sections. Don't extend them; if a
block is genuinely dead, delete it in a separate PR.
## See also
- [`docs/acronyms.md`](acronyms.md)
- [`docs/tribal-knowledge.md`](tribal-knowledge.md)
- [`../review-learnings.md`](../review-learnings.md)
- [`../BUGS_AND_IMPROVEMENTS_REPORT.md`](../BUGS_AND_IMPROVEMENTS_REPORT.md)
+115
View File
@@ -0,0 +1,115 @@
# Downstream services
> External APIs and services called by the devops-lib pipeline stages. Infrastructure (storage, registries) lives in [infrastructure.md](infrastructure.md).
## ArgoCD
Used by `deployArgoCD.groovy` to sync Helm releases to GKE clusters.
| Environment | URL | Credentials |
|-------------|-----|-------------|
| `prd` | `argocd-{bu}-prd.meeshogcp.in` | `argocd-{bu}-prd-creds` |
| `int` | `argocd-shared-int.meeshogcp.in` | `argocd-shared-int-creds` |
| `stg` / `ftr` | `argocd-dev.meeshogcp.in` | `argocd-dev-creds` |
Calls: `argocd app sync <app-name>` via CLI authenticated against the above URLs.
## Ringmaster
Used by `deployRingmaster.groovy` and `notify.groovy` for deployment tracking and the CD approval portal.
| Environment | API base URL |
|-------------|-------------|
| `prd` / `int` | `https://ringmaster-api.meeshogcp.in` |
| `stg` / `ftr` | `https://ringmaster-api.admin.meeshogcp.in` |
Endpoints called:
- `POST /api/v1/key/cicd/cd/update?workingEnv={env}` — build result + image tag + applications deployed
- `POST /api/v1/key/update/deployment-history?workingEnv={env}` — deployment history (prd branch builds only)
Credentials: Jenkins credential `ringmaster-token` (username + password).
## Turbo-Turtle (new CICD callback)
Used by `deployRingmaster.groovy` for builds triggered by users other than `ringmaster-bot`.
| Environment | Base URL |
|-------------|---------|
| `prd` / `int` | `http://turbo-turtle.meeshogcp.in` |
| `stg` / `ftr` | `http://turbo-turtle.admin.meeshogcp.in` |
Endpoint called: `POST /api/v1/ci/jenkins/callback`
## Deployment Tracker (legacy)
Used by `notify.groovy` for prd branch builds.
| Cloud | URL |
|-------|-----|
| GCP | `http://deployment-tracker.prd.meesho.int` |
| AWS | `http://deployment-tracker.meeshoint.in` |
Endpoint called: `POST /api/1.0/deployment-tracker/jenkins/create`
## SonarQube
Used by all build stages for code quality gating.
| Environment | URL | Token credential |
|-------------|-----|-----------------|
| `prd` / `int` | `https://sonarqube-prd.meeshogcp.in` | `sonar-token-prod` |
| `stg` / `ftr` | `https://sonarqube-{bu}-dev.meeshogcp.in` | `sonar-token-{bu}-dev` |
## Vault
Used by build stages to retrieve service secrets at deploy time.
| Environment | URL | Token credential |
|-------------|-----|-----------------|
| `prd` / `int` | `https://vault-prd.meeshogcp.in` | `vault-prd-token` |
| `stg` / `ftr` | `https://vault-dev.meeshogcp.in` | `vault-dev-token` |
## JFrog Artifactory
Used by `buildMaven.groovy` and `onlyPushtoJfrog.groovy` for Maven JAR publishing and resolution.
Credential: `svc-devops-meesho` (env `GITHUB_CRED`).
## GitHub — Meesho/whitelists
Cloned at runtime by `constructParam.groovy` to fetch per-repo policy YAML files.
```
url: https://github.com/Meesho/whitelists.git
branch: main
credentialsId: cicd-github-app
```
Files read:
- `skip-sonar-whitelist.yaml`
- `app-config-disabled.yaml`
- `multizone-enabled-repos.yaml`
- `allowedNonDevelopPrDeploymentToInt.yaml`
- `ValidateCacConfig.yaml`
## GitHub — devops-helm-charts / devops-argo-config
Cloned by `deployArgoCD.groovy` to update Helm values and ArgoCD app manifests.
| Repo | Branch (prd) | Branch (stg) |
|------|-------------|-------------|
| `devops-helm-charts` | `main` | `develop` |
| `devops-argo-config` | `main` | `develop` |
Credential: `svc-devops-meesho`.
## Athens (Go module proxy)
Used by `buildGo.groovy` via `GOPRIVATE=github.com/Meesho` + `GOPROXY`.
| Environment | URL |
|-------------|-----|
| `prd` / `int` | `https://athens-prd.meeshogcp.in` |
| `stg` / `ftr` | `https://athens-dev.meeshogcp.in` |
<!-- meesho-init: generated-at=2026-05-07T08:39:42Z base-sha=805350dfbd5b354663e8e2a90bfc219c4c267e00 -->
+108
View File
@@ -0,0 +1,108 @@
# Golden PRs — devops-lib
Curated benchmark PRs for evaluating LLM coding agents on this service.
Selected on 2026-05-07 from the Golden PR Selection pipeline (16 from
the suggested set + 0 user-added).
## Picks (16)
### #504 — Include common priority(CP) in dependabot blocking logic
- URL: https://github.com/Meesho/devops-lib/pull/504
- Author: @dhyey-meesho
- Stratum: feature
- Merged: 2025-05-09
### #616 — Feat/pbac enabled flag support
- URL: https://github.com/Meesho/devops-lib/pull/616
- Author: @amansrivastava118
- Stratum: feature
- Merged: 2025-11-04
### #643 — go sonar scans
- URL: https://github.com/Meesho/devops-lib/pull/643
- Author: @AryamanParida-Meesho
- Stratum: feature
- Merged: 2026-01-13
### #664 — Add repoType handling to build scripts
- URL: https://github.com/Meesho/devops-lib/pull/664
- Author: @vg-meesho
- Stratum: feature
- Merged: 2026-02-05
### #687 — Ft/toolchain integration mvn pyth
- URL: https://github.com/Meesho/devops-lib/pull/687
- Author: @AryamanParida-Meesho
- Stratum: feature
- Merged: 2026-02-25
### #694 — sidecar cotnaienr
- URL: https://github.com/Meesho/devops-lib/pull/694
- Author: @AryamanParida-Meesho
- Stratum: feature
- Merged: 2026-03-05
### #710 — Node changes
- URL: https://github.com/Meesho/devops-lib/pull/710
- Author: @AryamanParida-Meesho
- Stratum: feature
- Merged: 2026-04-08
### #730 — Update validate_configs.py
- URL: https://github.com/Meesho/devops-lib/pull/730
- Author: @abhinandanv13-meesho
- Stratum: feature
- Merged: 2026-04-14
### #667 — secret path fix for node applcaiton
- URL: https://github.com/Meesho/devops-lib/pull/667
- Author: @ShrutiKoshta-meesho
- Stratum: bugfix
- Merged: 2026-02-05
### #696 — Mq before vs
- URL: https://github.com/Meesho/devops-lib/pull/696
- Author: @AryamanParida-Meesho
- Stratum: bugfix
- Merged: 2026-03-04
### #711 — Configure Git to use SSH for GitHub URLs in Dockerfile
- URL: https://github.com/Meesho/devops-lib/pull/711
- Author: @sahil-meesho
- Stratum: bugfix
- Merged: 2026-03-24
### #713 — node-fix
- URL: https://github.com/Meesho/devops-lib/pull/713
- Author: @ShrutiKoshta-meesho
- Stratum: bugfix
- Merged: 2026-03-26
### #716 — Update Dockerfile to configure SSH for GitHub access
- URL: https://github.com/Meesho/devops-lib/pull/716
- Author: @sahil-meesho
- Stratum: bugfix
- Merged: 2026-03-30
### #702 — adding command level
- URL: https://github.com/Meesho/devops-lib/pull/702
- Author: @AryamanParida-Meesho
- Stratum: refactor
- Merged: 2026-03-11
### #721 — Remove 2 mvn clean
- URL: https://github.com/Meesho/devops-lib/pull/721
- Author: @mahak4jain
- Stratum: refactor
- Merged: 2026-04-07
### #727 — Refactor npm install command in Dockerfile and buildNode.groovy
- URL: https://github.com/Meesho/devops-lib/pull/727
- Author: @yeleswaramteja
- Stratum: refactor
- Merged: 2026-04-17
---
*Generated by `meesho-golden-pr-register` on 2026-05-07.*
*Source run hash: `c8e9537048ec599b`.*
+43
View File
@@ -0,0 +1,43 @@
# Documentation Index
> Catalogue of every doc reachable from this repo. CLAUDE.md's `## Required reading` lists the must-reads; everything else lives here. Auto-generated by `/meesho-init` Phase 10 — edits to the body are reset on next run. To add a doc, drop the file into `docs/` and re-run `/meesho-init`.
## Authoritative
| Doc | What it covers |
| --- | --- |
| [docs/architecture.md](architecture.md) | System design, module boundaries, deployArgoCD 4-step ceremony, downstream services, critical invariants |
| [docs/wiki/index.md](wiki/index.md) | Synthesized concept pages — owned by `/m-wiki` |
| [docs/adr/README.md](adr/README.md) | Architecture Decision Records (16 ADRs, retroactively captured) — owned by `/m-docs:adr-generator` |
## Reference
| Doc | What it covers |
| --- | --- |
| [docs/acronyms.md](acronyms.md) | Domain-specific acronyms used in this repo's code and docs (BU, BUILDKIT, CAC, GCPP, INFRA, …) |
| [docs/downstreams.md](downstreams.md) | External APIs and services called by the devops-lib pipeline stages (ArgoCD, Ringmaster, JFrog, SonarQube, Vault, Slack, Deployment Tracker) |
| [docs/golden-prs.md](golden-prs.md) | Curated benchmark PRs for evaluating LLM coding agents on this service |
| [docs/infrastructure.md](infrastructure.md) | Storage and registry infrastructure used by the devops-lib pipeline per environment (GAR, ECR, S3, GCS) |
| [docs/review-learnings.md](review-learnings.md) | PR-review-derived skill proposals — auto-generated by `/m-docs:pr-learnings` (different from `review-learnings.md` at repo root, which is the manual learnings log) |
| [docs/SECURITY.md](SECURITY.md) | Security policy and contact — auto-generated by `/m-docs:security-init` |
| [docs/tribal-knowledge.md](tribal-knowledge.md) | Non-obvious conventions, design decisions, operational patterns (15 numbered sections — load-bearing reads for anyone touching the pipeline) |
## Skills proposals
| Doc | What it covers |
| --- | --- |
| [docs/skills/README.md](skills/README.md) | Index of proposed Claude Code skills for devops-lib |
| [docs/skills/pipeline-tracer/](skills/pipeline-tracer/) | Trace a service's complete execution path through devops-lib |
| [docs/skills/library-impact-analyzer/](skills/library-impact-analyzer/) | Identify build-tool / stage / consumer blast radius for a devops-lib PR |
| [docs/skills/build-failure-debugger/](skills/build-failure-debugger/) | Map a Jenkins job log to the exact devops-lib code path that produced the failure |
## Other (outside `docs/`)
| Doc | What it covers |
| --- | --- |
| [README.md](../README.md) | Service blurb + `config.yaml` schema + adding-a-build-stage walkthrough |
| [CLAUDE.md](../CLAUDE.md) | Claude-facing repo guide — must-read for agents working here |
| [review-learnings.md](../review-learnings.md) | Manual PR-review learnings log (root-level — note: distinct from `docs/review-learnings.md`, which is the auto-generated skill proposals) |
| [BUGS_AND_IMPROVEMENTS_REPORT.md](../BUGS_AND_IMPROVEMENTS_REPORT.md) | Known bug + tech-debt catalogue — read before "fixing" anything that looks suspect |
<!-- meesho-init: plugin-version=1.0.33-with-phase10-backport generated-at=2026-05-22T20:00:00Z base-sha=e504a2b428e4 -->
+67
View File
@@ -0,0 +1,67 @@
# Infrastructure
> Storage and registry infrastructure used by the devops-lib pipeline per environment. External service dependencies live in [downstreams.md](downstreams.md).
Note: `devops-lib` is a Jenkins Shared Library — it does not own databases, caches, or message queues. The infrastructure listed here is used transiently during pipeline execution (artifact storage, container registry, Docker daemon).
## Artifact storage
Built JARs and other build outputs are stored in object storage before being referenced at deploy time.
| Type | Environment | Bucket / Path |
|------|-------------|--------------|
| GCS | `prd` / `int` | `gcs-infr-dvps-meesho-artifacts-prd` / `gcs-infr-dvps-meesho-artifacts-int` |
| GCS | `stg` | `gcs-infr-dvps-meesho-artifacts-stg` |
| GCS | `ftr` | `gcs-infr-dvps-meesho-artifacts-ftr` |
| S3 (AWS prd) | `prd` / `int` | `meesho-prod-artifacts` (ap-southeast-1, account `847438129436`) |
| S3 (AWS dev) | `stg` / `ftr` | `meesho-stg-artifacts` (ap-south-1, account `766380763301`) |
Set as `env.objBucket` by `constructParam.run()`.
## Container registry
Docker images are built and pushed here by all build stages.
| Type | Registry URL | Used for |
|------|-------------|---------|
| GAR (GCP) | `asia-southeast1-docker.pkg.dev/meesho-devops-admin-0622` | All GCP builds — push target |
| GAR admin repo | `asia-southeast1-docker.pkg.dev/meesho-devops-admin-0622/admin` | Build-time base images |
| ECR (AWS prd) | `847438129436.dkr.ecr.ap-southeast-1.amazonaws.com` | AWS prd / int |
| ECR (AWS dev) | `766380763301.dkr.ecr.ap-south-1.amazonaws.com` | AWS stg / ftr |
Set as `env.registry` / `env.buildRegistry` by `constructParam.run()`.
## Docker daemon (dind)
Build stages connect to a remote Docker daemon rather than a local socket. The host is injected as `env.DOCKER_HOST`.
| Environment | Docker host |
|-------------|------------|
| `prd` | `dind-prd-svc` |
| `int` | `dind-int-svc` |
| `stg` / `ftr` | `dind-dev-new-svc.jenkins-new.svc.cluster.local` |
## Jenkins agent pods (GCP)
GCP builds run in Kubernetes pods defined in `resources/org/meesho/`. Pod specs are referenced by environment:
| Environment | Pod spec file |
|-------------|--------------|
| `prd` | `resources/org/meesho/prd-pod.yaml` |
| `int` | `resources/org/meesho/int-pod.yaml` |
| `stg` | `resources/org/meesho/stg-pod.yaml` |
| `ftr` | `resources/org/meesho/ftr-pod.yaml` |
The `container('devops-tools')` container inside these pods runs all pipeline stages.
## GCP projects
| Environment | GCP project |
|-------------|------------|
| `prd` | `meesho-{bu}-prd-0622` |
| `int` | `meesho-shared-int-0525` |
| `stg` / `ftr` | `meesho-{bu}-dev-0622` |
Set as `env.GCPProject` by `constructParam.run()`.
<!-- meesho-init: generated-at=2026-05-07T08:39:42Z base-sha=805350dfbd5b354663e8e2a90bfc219c4c267e00 -->
+27
View File
@@ -0,0 +1,27 @@
<!-- auto-generated by pr-learnings -->
## buildNode.groovy: pnpm must be globally installed before invoking pnpm commands
**Takeaway:** When auto-selecting the install command in `buildNode.groovy` based on lockfile presence, pnpm must be installed globally first (`npm install -g pnpm@<version>`) — emitting `pnpm install --frozen-lockfile` alone will fail in environments where pnpm is not on the PATH.
**Pattern:** Any time `buildNode.groovy` detects a `pnpm-lock.yaml` and constructs an `npm_install_arg` that invokes `pnpm`, the command string must prefix with a global install step. The version should be pinned to avoid silent drift.
**Rationale:** Jenkins agents running Node builds do not have pnpm pre-installed. The auto-selection logic that branches on `fileExists("${repo_name}/pnpm-lock.yaml")` is responsible for producing a self-contained, executable command string — if pnpm is absent from the agent PATH the build fails with a command-not-found error. Reviewers also flagged that lock-file cleanup helpers should cover `pnpm-lock.yaml` alongside `package-lock.json`.
**Concrete example** (from [PR #727](https://github.com/Meesho/devops-lib/pull/727), `src/com/meesho/stages/buildNode.groovy:L193`):
```groovy
// Before (incomplete — pnpm not guaranteed on PATH)
if (fileExists("${repo_name}/pnpm-lock.yaml")) {
npm_install_arg = 'pnpm install --frozen-lockfile'
}
// After (reviewer-suggested correction)
if (fileExists("${repo_name}/pnpm-lock.yaml")) {
npm_install_arg = 'npm install -g pnpm@10.33.0 && pnpm install --frozen-lockfile'
}
```
**Examples from PRs:**
- [PR #727](https://github.com/Meesho/devops-lib/pull/727): Auto-selection logic added for pnpm/npm-ci/npm-install; reviewer caught that pnpm must be globally installed first.
- [PR #728](https://github.com/Meesho/devops-lib/pull/728): Reviewer noted cleanup helpers should also handle `pnpm-lock.yaml`.
+9
View File
@@ -0,0 +1,9 @@
# devops-lib Skill Proposals
Proposed Claude Code skills for devops-lib. Each proposal targets a specific daily friction point observed from code review patterns, task-replay data, and tribal knowledge gaps.
| Skill | One-line description | Priority | Owner |
|---|---|---|---|
| [`pipeline-tracer`](./pipeline-tracer/SPEC.md) | Given a service config.yaml and branch/trigger context, trace the complete execution path through devops-lib — every stage, every decision point, every policy check — with exact code locations | High | DevOps Platform |
| [`library-impact-analyzer`](./library-impact-analyzer/SPEC.md) | Given a devops-lib PR, identify which build_tool types, pipeline stages, and consumer service categories are affected — so reviewers know the blast radius before merging | High | DevOps Platform |
| [`build-failure-debugger`](./build-failure-debugger/SPEC.md) | Given a Jenkins job log from a failed devops-lib build, identify exactly which stage failed, trace the code path through devops-lib that produced the failure, and output a concrete fix | High | DevOps Platform |
+129
View File
@@ -0,0 +1,129 @@
# Skill: build-failure-debugger
**One-line description:** Given a Jenkins job log from a failed devops-lib build, identify exactly which stage failed, trace the code path through devops-lib that produced the failure, and output a concrete fix with the exact file and line driving the error.
**Owner:** DevOps Platform team
---
## Why this skill exists
Jenkins job logs are long, noisy, and mixed-language (Groovy stack traces, Maven output, Docker build output, ArgoCD sync output). When a build fails, an engineer must:
1. Scroll through 2000+ lines to find the failure point
2. Know that "Build" maps to `buildMaven.groovy`, "Deploying to ArgoCD" maps to `deployArgoCD.groovy`, etc.
3. Know which code path was taken based on `build_tool` and `cicd_environment`
4. Know which external system (GAR, SonarQube, ArgoCD, Helm repo) caused the failure vs which devops-lib logic caused it
The mapping from Jenkins stage names to devops-lib source files is non-obvious and not documented anywhere. A "SonarQube Quality Gate" failure could mean: the threshold was breached, the project doesn't exist in Sonar yet, or a whitelist entry is missing. Three different fixes, identical log output.
This skill knows the stage-to-file mapping, the code paths for each failure mode, and the set of conditions that trigger each error.
---
## Trigger
- User pastes a Jenkins job log (or the relevant failure excerpt)
- `"this Jenkins build failed, help me debug it"` with log attached
- `"sonar quality gate failing for auth-service on develop"`
- `"build stuck at ArgoCD sync for catalog-service in prd"`
- Proactively: when a user shares a build URL, fetch the log and diagnose
---
## Input
- Jenkins job log (pasted text or URL to the Jenkins job)
- Optionally: the service `config.yaml` (to know `build_tool`, `skip_sonar`, `deployArgo`, etc.)
The skill can infer build_tool and environment from the log itself if config.yaml is not provided (Jenkins prints `build_tool` and `cicd_environment` at pipeline start via `constructParam.run()`).
---
## Expected output
Given: Jenkins log showing failure in "Deploying to ArgoCD" stage for `catalog-service` on `develop`
```
Build Failure Debug — catalog-service | branch: develop | env: stg
Stage failed: Deploying to ArgoCD
devops-lib: src/com/meesho/utilities/deployArgoCD.groovy
Step that failed: update_helm_repo (step 3 of 4)
Error in log: ERROR: Helm push failed — chart version 0.1.142 already exists
Root cause: The Helm chart version was not bumped between this push and the previous
build. deployArgoCD.groovy uses the image SHA as the chart version suffix, but the
SHA collision occurred because the source didn't change (config-only build — latest
tag was reused from GAR without a new image build).
Fix: Force a source change to generate a new image SHA, OR bump the chart version
manually in devops-argo-config.
deployment_order check: 'catalog-service-stg' — verify this matches the app name
in devops-argo-config (wrong name → silent sync failure at step 4, not step 3).
Relevant code:
deployArgoCD.groovy:update_helm_repo() — pushes chart with SHA-derived version
constructParam.groovy:getConfigOnlyChange() — sets SKIP_BUILD flag when only YAMLs changed
```
---
## Stage-to-file mapping (built-in knowledge)
| Jenkins stage name | devops-lib file |
|---|---|
| `Build` | `buildMaven.groovy` / `buildGo.groovy` / `buildNode.groovy` / etc. (by `build_tool`) |
| `Docker Build & Push` | `buildMaven.groovy:dockerBuildAndPush()` / equivalent in each build stage |
| `SonarQube Analysis` | `buildMaven.groovy:sonar_scan()` / `buildGo.groovy:sonar_scan()` |
| `SonarQube Quality Gate` | `constructParam.groovy:waitForQualityGate()` |
| `CAC Validation` | `buildMaven.groovy:cac_validation()` / `buildGo.groovy:cac_validation()` |
| `Deploying to ArgoCD` | `deployArgoCD.groovy` (4-step: update_argo_repo → refresh_app_of_apps → update_helm_repo → refresh_and_sync) |
| `Notify` | `notify.groovy` / `notifySlack.groovy` |
| `AppConfig Validation` | `constructParam.groovy:validateAppConfig()` |
---
## Failure mode taxonomy
The skill classifies every failure into one of these categories before diagnosis:
| Category | Signal in log | devops-lib location |
|---|---|---|
| Build compilation | `BUILD FAILURE` / `go build failed` | build<Lang>.groovy |
| Docker push to GAR | `denied` / `UNAUTHORIZED` on push | build<Lang>.groovy:dockerBuildAndPush() |
| Sonar threshold | `Quality Gate status: FAILED` | constructParam.groovy:waitForQualityGate() |
| Sonar project missing | `Project not found` | buildGo.groovy:sonar_scan() auto-create logic |
| ArgoCD step 1 (argo_repo update) | `git push failed` in argo-config repo | deployArgoCD.groovy:update_argo_repo() |
| ArgoCD step 2 (app-of-apps refresh) | `app not found` / `no Application object` | deployArgoCD.groovy:refresh_app_of_apps() — new service, ArgoCD app not yet created |
| ArgoCD step 3 (helm push) | `chart version already exists` | deployArgoCD.groovy:update_helm_repo() |
| ArgoCD step 4 (sync) | `OutOfSync` / `Helm values error` | deployArgoCD.groovy:refresh_and_sync() |
| Wrong deployment_order | Silent sync on wrong app name | config.yaml:deployment_order vs devops-argo-config |
| Multizone gate | `must be deployed via Ringmaster` | constructParam.groovy:isMultizoneEnabled() |
---
## Dependencies
- Full read access to devops-lib source (stage-to-file mapping, failure message strings)
- Jenkins log (pasted by engineer or fetched via Jenkins API)
- Optionally: `Meesho/whitelists` (to check if skip_sonar or multizone entries explain the failure)
- Optionally: `Meesho/devops-argo-config` (to validate `deployment_order` app names for ArgoCD failures)
---
## Design notes
- The skill must handle truncated logs (Jenkins UI often shows the last N lines). It should ask for the full log if the failure point is not visible.
- For hotfix builds: sonar and quality gate failures are expected to be suppressed; if they appear, the hotfix path was not taken — the skill should check the branch name pattern.
- The skill should distinguish between a devops-lib bug (code path is wrong) vs a configuration error (wrong value in config.yaml or whitelist) vs an external system error (GAR down, Sonar unreachable).
---
## Open questions
- Should it auto-fetch the Jenkins log via the Jenkins API if given a job URL?
- For ArgoCD step 4 failures (Helm values schema errors): should it parse the exact Helm error and cross-reference with the deployment.yaml schema?
- Should it suggest a rerun command or ArgoCD force-sync as a recovery action?
@@ -0,0 +1,73 @@
# Skill: library-impact-analyzer
**One-line description:** Given a devops-lib PR, identify which build_tool types, pipeline stages, and consumer service categories are affected — so reviewers know the blast radius before merging.
**Owner:** DevOps Platform team
---
## Why this skill exists
devops-lib is consumed via `@Library('devops-lib@main')` by every Meesho microservice. A change to a shared file like `constructParam.groovy` or `buildObjHelper.groovy` can silently affect hundreds of services across all build types and environments. There is currently no tooling to answer the basic pre-merge question: **"what does this change actually affect?"**
Common dangerous patterns caught too late:
- A change to `constructParam.groovy:run()` that's tested on maven but breaks node builds (different code path)
- A change to `buildGo.groovy` that fixes prd but changes stg behavior (env-conditional logic)
- A change to `eksCICD.groovy:allowedUsers` that accidentally narrows who can trigger builds
The review process today is manual — a senior DevOps engineer reads the diff and mentally simulates which build types it touches. This is error-prone and doesn't scale as the library grows.
---
## Trigger
- PR opened against devops-lib (automatic on any PR touching `src/`, `vars/`, or `resources/`)
- Manual: `"what does this PR affect?"` with a PR number or diff pasted
- `"impact analysis for PR #643"`
---
## Expected output
Given PR touching `src/com/meesho/utilities/constructParam.groovy` and `src/com/meesho/stages/buildGo.groovy`:
```
Impact Analysis — PR #643
Files changed: constructParam.groovy, buildGo.groovy
Affected build paths:
✦ constructParam.groovy is called by ALL build types on EVERY build
→ Changes here affect: maven, go, node-*, python-*, gradle, php, rust, docker
→ Changes here affect: ALL environments (prd, stg, int, ftr)
Changed method: skipSonarCheckForGo() — new method, additive, low risk
✦ buildGo.groovy affects: go, go-1.22, go-1.21 (any build_tool matching /^go.*/)
→ Environments: prd + stg (sonar_scan() only runs in these envs)
Changed: sonar_scan() — adds exclusion logic and auto-project creation
Risk assessment:
constructParam.groovy: LOW (new method only, no existing method modified)
buildGo.groovy: MEDIUM (modifies sonar_scan() which runs in prd)
Suggested test coverage before merge:
□ Trigger a Go service build in stg to verify sonar exclusions work
□ Trigger a Maven service build to confirm constructParam changes are neutral
□ Check sonar_scan() does not break for services without sonar-project.properties
```
---
## Dependencies
- Reads devops-lib source to build a call graph (which methods call what, which build_tool routes to which stage)
- `git diff` of the PR (from `gh pr diff <number>` or GitHub API)
- Optional: `Meesho/whitelists` to identify which consumer repos are on relevant whitelists
---
## Open questions
- Should it post the impact analysis as a PR comment automatically (requires GitHub token), or output to stdout?
- Should it attempt to enumerate actual consumer services affected (requires access to consumer repos), or stop at build_tool categories?
- Should it flag changes to `allowedUsers` lists as HIGH risk automatically (any change to who can trigger builds is sensitive)?
+112
View File
@@ -0,0 +1,112 @@
# Skill: pipeline-tracer
**One-line description:** Given a service's config.yaml and a branch/trigger context, trace the complete execution path through devops-lib — every stage, every decision point, every policy check — and output a human-readable flow with the exact code locations driving each step.
**Owner:** DevOps Platform team
---
## Why this skill exists
devops-lib's pipeline is a routing tree, not a linear script. A single `eksCICD` call dispatches to different stages based on `build_tool`, takes different paths based on `env.BRANCH_NAME` and PR target, checks multiple whitelists, conditionally runs sonar, CAC validation, ArgoCD sync, and sends different notifications depending on the environment. The full execution path for a given service in a given context spans 15+ files.
No one has a complete mental model of this tree for every service type. Consequences:
- Engineers add a whitelist exception but don't know which of the 3 sonar-skip checks it actually bypasses
- Reviewers approve a stage change without knowing it only runs in `prd` (not `stg`)
- New team members spend days understanding why their build skips certain stages
- Debugging requires mentally simulating the entire dispatch chain from `eksCICD.groovy` down
This skill is the complement to `library-impact-analyzer`: impact-analyzer answers "what does a code change affect?" — pipeline-tracer answers "for this specific service in this specific context, what exact path does the code take?"
---
## Trigger
- `"trace pipeline for payment-service on develop branch"`
- `"what stages run for a hotfix build of catalog-service?"`
- `"why is sonar being skipped for auth-service?"`
- `"show me the full pipeline path for a PR from feature/x to main in supply-chain-service"`
- Proactively: attached to `service-onboarder` output — show the new service's expected pipeline before its first build
---
## Input
- A service `config.yaml` (file path or pasted content) — provides `build_tool`, `team`, `bu`, `skip_sonar`, `deployArgo`, `deployment_order`, etc.
- A trigger context: branch name (`develop`, `main`, `hotfix/x`, `feature/y`) and optionally a PR target (`main` or `develop`)
---
## Expected output
Given: `config.yaml` for `payment-service` (build_tool: maven, bu: supply), branch: `develop`
```
Pipeline Trace — payment-service | branch: develop | env: stg
Entry point: vars/eksCICD.groovy
Trigger check: PASS — develop branch, no PR target → env = stg
Pod selection: resources/org/meesho/stg-pod.yaml
image: build-tools:lunar-v2.0.21
node pool: supply-shared (BU-scoped stg pool)
Build stage: src/com/meesho/stages/buildMaven.groovy
build_tool 'maven' → buildObjHelper → buildMaven
Config-only change check: RUNS (skips build if only *.yaml changed)
Docker image: stg/payments/payment-service:<sha>
Policy checks:
skip_sonar: false → sonar WILL run
skip-sonar-whitelist: payment-service NOT on list → sonar runs
CAC validation: payment-service on ValidateCacConfig whitelist → RUNS
appConfigEnabled: true → AppConfig validation RUNS
multizone: payment-service NOT on multizone list → deploy proceeds normally
ArgoCD deploy:
deployArgo: true → WILL deploy
deployment_order: [payment-service]
4-step sequence: update_argo_repo → refresh_app_of_apps → update_helm_repo → refresh_and_sync
ArgoCD app: payment-service-stg
Notification:
notify_channel: #payments-alerts
Ringmaster callback: NO (stg build, not prd)
Turbo-Turtle callback: YES (stg deploy confirmation)
Total stages: 7 | Estimated duration: 1218 min
```
---
## Dependencies
- Full read access to devops-lib source (the skill builds a live call graph from the source)
- `Meesho/whitelists` read access (to check live whitelist membership for the specific service)
- `src/com/meesho/utilities/buTeamMapping.groovy` (for node pool selection)
- `resources/org/meesho/*.yaml` (for pod spec and image resolution)
---
## Design notes
The skill must understand the devops-lib environment mapping table precisely:
| Branch | PR target | `cicd_environment` |
|---|---|---|
| `main`/`master`/`gcp-main` | — | `prd` |
| `develop` | — | `stg` |
| any | `main` | `int` |
| any | `develop` | `ftr` |
| `hotfix/*` | — | `prd` (sonar + tests skipped) |
And must correctly simulate the hotfix path (sonar skipped, quality gate skipped, no CAC validation) vs the standard path.
---
## Open questions
- Should the trace show actual code line numbers for each decision, or just method names?
- For `int` and `ftr` environments where ArgoCD deploy is often skipped: should it explain why?
- Should it compare two contexts side-by-side (e.g., "what's different between develop and hotfix builds for this service")?
- Could this skill power an interactive pipeline visualiser (Mermaid diagram output)?
+133
View File
@@ -0,0 +1,133 @@
# 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.
+39
View File
@@ -0,0 +1,39 @@
{
"built_against_sha": "e504a2b428e4",
"built_at": "2026-05-21T20:00:39+00:00",
"by_file": {},
"by_page": {
"docs/wiki/pages/01-ARCHITECTURE.md": [],
"docs/wiki/pages/01-overview.md": [],
"docs/wiki/pages/02-ENTRYPOINTS.md": [],
"docs/wiki/pages/02-entry-points.md": [],
"docs/wiki/pages/03-BUILD-STAGES.md": [],
"docs/wiki/pages/03-build-dispatch.md": [],
"docs/wiki/pages/04-DEPLOY-ARGOCD.md": [],
"docs/wiki/pages/04-deploy-flow.md": [],
"docs/wiki/pages/05-ENVIRONMENT-MAPPING.md": [],
"docs/wiki/pages/05-cross-cutting.md": [],
"docs/wiki/pages/06-CONFIG-POLICY.md": [],
"docs/wiki/pages/07-LANGUAGE-BUILDS.md": [],
"docs/wiki/pages/08-DOCKERFILE-TEMPLATES.md": [],
"docs/wiki/pages/09-INFRA-PODS.md": [],
"docs/wiki/pages/10-NOTIFICATIONS.md": [],
"docs/wiki/pages/adr/adr-index.md": [],
"docs/wiki/pages/build/build-dispatch.md": [],
"docs/wiki/pages/build/config-only-detection.md": [],
"docs/wiki/pages/build/docker-tagging.md": [],
"docs/wiki/pages/build/node-paired-files.md": [],
"docs/wiki/pages/build/scm-variable-scope.md": [],
"docs/wiki/pages/concepts/observability.md": [],
"docs/wiki/pages/concepts/secrets-and-auth.md": [],
"docs/wiki/pages/concepts/whitelists.md": [],
"docs/wiki/pages/deploy/argocd-sync.md": [],
"docs/wiki/pages/deploy/ringmaster-integration.md": [],
"docs/wiki/pages/infra/node-pool-selection.md": [],
"docs/wiki/pages/policy/cac-validation.md": [],
"docs/wiki/pages/policy/multi-tenancy.md": [],
"docs/wiki/pages/policy/whitelist-system.md": [],
"docs/wiki/pages/security/security-overview.md": []
},
"schema_version": "v0.7.0"
}
View File
+106
View File
@@ -0,0 +1,106 @@
# MANIFEST — devops-lib wiki
<!-- m-wiki: manifest base-sha=e504a2b428e4 generated-at=2026-05-22 -->
Auto-generated by `/m-wiki:wiki-init`. Each run rewrites this file in place.
## Run summary (2026-05-22, update mode)
| Metric | Value |
|---|---|
| Mode | update |
| base-sha | e504a2b428e4 |
| Pages on disk | 36 (10 top-level + 26 concept across `concepts/`, `build/`, `policy/`, `security/`, `deploy/`, `infra/`, `adr/`) |
| Pages newly synthesized this run | **0** — see Synthesis note below |
| Pages touched by drift refresh | 0 (B17 precheck found 0 drifted citations) |
| LINE→Symbol promotions | 0 |
| Raw inputs picked up | 0 (`raw/` empty) |
| External inputs picked up | 0 (`raw/external/` empty) |
| Reconcile inventory consumed | 0 hints across 0 pages |
| Drift-queue entries drained | 0 (empty) |
| Auto-synthesize targets folded | 0 — `docs/tribal-knowledge.md` content already represented in existing pages |
| Warnings | 3 (see below) |
## Synthesis note (this run)
Phase 1 step 3.6 classified `docs/tribal-knowledge.md` as `auto_synthesize` (allowlist mode is `synthesize` and the file had no `sources=[...]` provenance hit in existing pages). Phase 2 §2.6 fired the decision tree against sections §1-15 and found that **every section already has equivalent coverage in pages added by commit `4a7c4001` (2026-05-12)** and the dual-bootstrap union created by the PR #861 merge:
| Tribal-knowledge section | Existing coverage |
|---|---|
| §1 Whitelists | `pages/concepts/whitelists.md` + `pages/policy/whitelist-system.md` |
| §2 ringmaster-bot constant | `pages/04-deploy-flow.md` + `pages/deploy/ringmaster-integration.md` |
| §3 buildObjHelper fall-through | `pages/03-build-dispatch.md` + `pages/build/build-dispatch.md` |
| §4 JVM memory formula | `pages/04-deploy-flow.md` + `pages/04-DEPLOY-ARGOCD.md` |
| §5 Canary mandatory for sp0/up0 | `pages/04-deploy-flow.md` + `pages/04-DEPLOY-ARGOCD.md` |
| §6 Turbo-Turtle inline JSON | `pages/04-deploy-flow.md` + `pages/deploy/ringmaster-integration.md` |
| §7 `env.CHANGE_ID` | `pages/05-cross-cutting.md` + `pages/05-ENVIRONMENT-MAPPING.md` |
| §8 BU-scoped node pools | `pages/05-cross-cutting.md` + `pages/infra/node-pool-selection.md` + bullet in `09-INFRA-PODS.md` |
| §9 `constructTemplate._construct()` `@NonCPS` | `pages/01-overview.md` + `pages/01-ARCHITECTURE.md` |
| §10 ArgoCD app-of-apps step order | `pages/04-deploy-flow.md` + `pages/04-DEPLOY-ARGOCD.md` |
| §11 Node build / Dockerfile pairing | `pages/build/node-paired-files.md` |
| §12 build-tools pod image versioning | bullet in `pages/09-INFRA-PODS.md` |
| §13 Go sonar skip in `constructParam` | bullets in `pages/06-CONFIG-POLICY.md` + `pages/policy/cac-validation.md` |
| §14 DinD sidecar `DOCKER_HOST` | bullet in `pages/09-INFRA-PODS.md` |
| §15 No `scm` in library code | `pages/build/scm-variable-scope.md` |
Per the atomic-concept rule, no new pages were created — they would all duplicate existing ones. The Decision tree's "doesn't fit anywhere" branch did not fire because every section had at least one existing page covering its scope.
I initially wrote 5 new concept pages (`node-build-dockerfile-pairing.md`, `build-tools-image.md`, `sonar-skip-routing.md`, `dind-sidecar-endpoints.md`, `no-scm-in-library.md`) before spotting the duplicates in the older topic-based hierarchy. Those 5 pages have been removed and are not in the final commit.
## Allowlist source
`docs/wiki/allowlist.yaml` (unchanged this run · 5 entries · last touched in PR #861)
## Warnings
1. **qmd registration via daemon.** The qmd HTTP MCP daemon on port 7733 serves the `devops-lib-wiki` collection (35 files indexed). `/m-wiki:wiki-search` works from a fresh Claude Code session.
2. **`.citation-index.json` `by_file` is empty by upstream design for `.groovy` files.** `m-wiki/scripts/code_truth_precheck.py:73` defines `EXT_RE` without `groovy`. ~70 `.groovy:LINE` citations in this wiki aren't indexed. B19 pre-commit drift detection is therefore inactive for `.groovy` changes. **Upstream issue.** File on `Meesho/spells` to add `groovy` to `EXT_RE`.
3. **Dual concept hierarchy.** This wiki currently has two parallel concept structures from independent bootstraps that got unioned in a merge:
- `pages/concepts/` (3 pages: whitelists, secrets-and-auth, observability) — added in PR #861 from `wiki: bootstrap at base-sha=5399a5ddc36b`
- `pages/{build,policy,security,deploy,infra,adr}/` (~13 pages) — added in commit `4a7c4001` from 2026-05-12 (`wiki: regenerate via /m-wiki:wiki at base-sha=28f54cf7`)
Both structures cover overlapping concepts (e.g. whitelists are documented in both `concepts/whitelists.md` and `policy/whitelist-system.md`). qmd indexes both, so search results may return duplicates. A future cleanup PR should pick one hierarchy as canonical and graduate any unique content from the other. This run does NOT touch this — out of scope for "update mode picks up new tribal-knowledge content".
## Pages
```
docs/wiki/
├── SCHEMA.md
├── index.md
├── log.md
├── MANIFEST.md ← this file
├── allowlist.yaml
├── .citation-index.json (regenerated this run; by_file empty per warning #2)
├── .drift-queue/.gitkeep
└── pages/
├── 01-overview.md ┐
├── 01-ARCHITECTURE.md │ Dual top-level set — see warning #3
├── 02-entry-points.md │
├── 02-ENTRYPOINTS.md │
├── 03-build-dispatch.md │
├── 03-BUILD-STAGES.md │
├── 04-deploy-flow.md │
├── 04-DEPLOY-ARGOCD.md │
├── 05-cross-cutting.md │
├── 05-ENVIRONMENT-MAPPING.md┘
├── 06-CONFIG-POLICY.md
├── 07-LANGUAGE-BUILDS.md
├── 08-DOCKERFILE-TEMPLATES.md
├── 09-INFRA-PODS.md
├── 10-NOTIFICATIONS.md
├── adr/adr-index.md
├── build/ 5 pages
├── concepts/ 3 pages (unchanged this run)
├── deploy/ 2 pages
├── infra/ 1 page
├── policy/ 3 pages
└── security/ 1 page
```
## What this run actually did
Net result on disk: only `.citation-index.json` regenerated + `log.md`, `MANIFEST.md`, `index.md` refreshed with the new base-sha and the dual-hierarchy note. No content additions; no content modifications outside the four metadata files.
The user invoked `/m-wiki:wiki-init` expecting the bootstrap-deferred tribal-knowledge synthesis to fire; the script's discovery did identify the target, but the decision tree found no scope-extension work to do. Surfacing this honestly is more useful than fabricating a synthesis pass.
+60
View File
@@ -0,0 +1,60 @@
# SCHEMA — devops-lib wiki
<!-- m-wiki: schema base-sha=5399a5ddc36b generated-at=2026-05-21 -->
LLM-maintained wiki for the **devops-lib** Jenkins shared library, generated by `/m-wiki:wiki-init` (bootstrap mode).
## Layout
```
docs/wiki/
├── SCHEMA.md ← this file (schema, layout, conventions)
├── index.md ← table of contents (auto-regenerated)
├── log.md ← per-run sync log
├── MANIFEST.md ← run statistics + page counts
├── allowlist.yaml ← qmd index allowlist (scaffolded, hand-editable)
├── .citation-index.json ← symbol-to-page mapping (auto-regenerated; v0.6+)
├── .drift-queue/ ← staged drift signals (committed, drained by next wiki-init)
└── pages/
├── 01-overview.md
├── 02-entry-points.md
├── 03-build-dispatch.md
├── 04-deploy-flow.md
├── 05-cross-cutting.md
└── concepts/
├── whitelists.md
├── secrets-and-auth.md
└── observability.md
```
## Page types
| Type | Path | Soft cap |
|---|---|---|
| `top-level` | `pages/NN-NAME.md` | narrative; no hard limit |
| `concept` | `pages/concepts/<slug>.md` | ≤600 words; lint warns >1500 |
## Provenance line
Every page starts with the provenance comment + a human-readable Generated line. The skill validates these on every re-run.
```
<!-- m-wiki: type=top-level slug=overview topic=null base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:vars/buildPipeline.groovy, code:src/com/meesho/stages/buildObjHelper.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: top-level. 2 sources.
```
## Settings
- `max-hierarchy-depth: 1` — single-service repo, no monorepo subpaths.
- `mode: bootstrap` (this run). Subsequent runs flip to `update` once `SCHEMA.md` exists.
- `qmd-collection: devops-lib-wiki`.
## Boundary rules
- m-wiki **writes** only inside `docs/wiki/`.
- m-wiki **indexes** the files declared in `allowlist.yaml` (`mode: index`) and **folds** the bodies of files declared `mode: synthesize` into concept pages (on the next update run — bootstrap defers synthesis).
- `raw/` is the team's input layer; humans drop curated source docs there. None exist for this repo yet.
- `.pre-commit-config.yaml` is gitignored (per-dev); teammates must run `/m-wiki:wiki-setup` on their own clones to get drift hooks.
See `/Users/roshan.v/.claude/plugins/marketplaces/meesho-skills/m-wiki/references/doctrine.md` for the full doctrine.
+38
View File
@@ -0,0 +1,38 @@
# m-wiki Index Allowlist — scaffolded by wiki-init on first run.
# Edit freely; subsequent wiki-init runs will read this file as-is.
#
# Schema:
# path — repo-relative file path or glob (no leading `/`, no `..`)
# skill — informational tag naming the skill that generates the file
# required — true → MANIFEST.md reports the file as missing if absent
# false → silently skipped when absent
# mode — synthesize: fold body into wiki pages (extends/novel/
# contradicts/doesn't-fit decision tree, same as raw/external/).
# index: register with qmd for search only; body never read.
# Absent: legacy/undecided — wiki-init's discovery gate will
# prompt the user once per file with no provenance.
#
# Files inside docs/wiki/ MUST NOT be listed here — m-wiki indexes its
# own pages automatically and including them double-indexes.
entries:
- path: README.md
skill: meesho-init
required: true
mode: index
- path: CLAUDE.md
skill: meesho-init
required: true
mode: index
- path: docs/architecture.md
skill: meesho-init
required: true
mode: index
- path: docs/tribal-knowledge.md
skill: pr-learnings
required: false
mode: synthesize
- path: docs/acronyms.md
skill: acronyms-docs
required: false
mode: index
+56
View File
@@ -0,0 +1,56 @@
# devops-lib wiki — index
<!-- m-wiki: index base-sha=e504a2b428e4 generated-at=2026-05-22 -->
> Auto-regenerated by `/m-wiki:wiki-init`. Hand-editing this file is discouraged — your edits will be overwritten on the next sync.
## Top-level pages
| # | Page | What it covers |
|---|---|---|
| 01 | [Overview](pages/01-overview.md) | What devops-lib is, how services consume it, the load/runtime model |
| 02 | [Entry points (vars/)](pages/02-entry-points.md) | The 10 Jenkins-global steps service Jenkinsfiles call |
| 03 | [Build dispatch](pages/03-build-dispatch.md) | The `buildObjHelper` switch from `build_tool` → stage class |
| 04 | [Deploy flow](pages/04-deploy-flow.md) | The load-bearing 4-step ArgoCD deploy ceremony and the canary enforcement gate |
| 05 | [Cross-cutting patterns](pages/05-cross-cutting.md) | Retries, BU multi-tenancy, downstream auth, feature flags, parallel build |
## Concept pages
This wiki has two overlapping concept hierarchies — see warning #3 in [MANIFEST.md](MANIFEST.md). The `concepts/` set was added by PR #861 (my bootstrap) and the older topic-based set (`build/`, `policy/`, `security/`, `deploy/`, `infra/`) was added by `4a7c4001`. Most readers should start with whichever set comes first in their search results; both are still indexed by qmd.
### My set (post-#861)
| Topic | Page |
|---|---|
| Whitelists | [The five whitelist gates](pages/concepts/whitelists.md) |
| Security | [Secrets, SSH keys, hard-coded IPs](pages/concepts/secrets-and-auth.md) |
| Observability | [Logging via `log.groovy`](pages/concepts/observability.md) |
### Older topic-based set (from `4a7c4001`)
| Topic | Pages |
|---|---|
| Build | [build-dispatch](pages/build/build-dispatch.md) · [docker-tagging](pages/build/docker-tagging.md) · [config-only-detection](pages/build/config-only-detection.md) · [node-paired-files](pages/build/node-paired-files.md) · [scm-variable-scope](pages/build/scm-variable-scope.md) |
| Policy | [cac-validation](pages/policy/cac-validation.md) · [multi-tenancy](pages/policy/multi-tenancy.md) · [whitelist-system](pages/policy/whitelist-system.md) |
| Security | [security-overview](pages/security/security-overview.md) |
| Deploy | [argocd-sync](pages/deploy/argocd-sync.md) · [ringmaster-integration](pages/deploy/ringmaster-integration.md) |
| Infra | [node-pool-selection](pages/infra/node-pool-selection.md) |
| ADR | [adr-index](pages/adr/adr-index.md) |
### Tribal-knowledge fold-in (this run)
`docs/tribal-knowledge.md` sections §11-15 (added to that file via the same #861 merge) are already covered by the older topic-based set above — specifically `build/node-paired-files.md` (§11), top-level `09-INFRA-PODS.md` (§12 + §14), and `build/scm-variable-scope.md` (§15). Sections §13 (`sonar-skip-routing`) and §14 (`dind-sidecar-endpoints`) are covered as bullets in `09-INFRA-PODS.md` and `06-CONFIG-POLICY.md`. No new pages were synthesized this run — see MANIFEST for the per-section mapping.
## Other repo docs (indexed by qmd, not part of this wiki)
| Path | Owner skill | Role |
|---|---|---|
| [`README.md`](../../README.md) | meesho-init | Service blurb for GitHub browsers |
| [`CLAUDE.md`](../../CLAUDE.md) | meesho-init | Claude-facing repo guide |
| [`docs/architecture.md`](../architecture.md) | meesho-init | Long-form architecture |
| [`docs/acronyms.md`](../acronyms.md) | acronyms-docs | Domain abbreviations |
| [`docs/tribal-knowledge.md`](../tribal-knowledge.md) | pr-learnings | Non-obvious conventions — all sections (§1-15) already represented in existing wiki pages from earlier bootstrap commits (`4a7c4001`, `127b59a3`); no fresh fold-in this run |
| [`review-learnings.md`](../../review-learnings.md) | pr-learnings | PR-review-derived rules |
| [`BUGS_AND_IMPROVEMENTS_REPORT.md`](../../BUGS_AND_IMPROVEMENTS_REPORT.md) | (manual) | Known bug catalogue |
Run `/m-wiki:wiki-search "<query>"` to retrieve across this wiki + the indexed files above.
+6
View File
@@ -0,0 +1,6 @@
# Sync log
| Date (UTC) | base-sha | Mode | Pages | Externals | Citations | Raws | Warnings | Notes |
|---|---|---|---|---|---|---|---|---|
| 2026-05-21 | 5399a5ddc36b | bootstrap | 8 | 0 | 24 | 0 | 1 | First bootstrap. 1 auto-synthesize target (docs/tribal-knowledge.md) deferred per bootstrap rule — re-run wiki-init to fold it in. |
| 2026-05-22 | e504a2b428e4 | update | 36 (=) | 0 | (n/a) | 0 | 2 | No new pages this run. The B15 auto-synthesize target docs/tribal-knowledge.md (§1-15) is already fully represented in the existing wiki — the merge of PR #861 unioned two independent wiki branches (mine at base-sha 5399a5ddc36b and `4a7c4001` from 2026-05-12), and the older one already covered §11-15 via build/node-paired-files.md, build/scm-variable-scope.md, and bullets in 09-INFRA-PODS.md / 06-CONFIG-POLICY.md. Regenerated .citation-index.json (still empty by_file because `.groovy` is not in the upstream EXT_RE). Surface the dual-hierarchy state in MANIFEST warning #3 so a future cleanup PR can dedupe. |
+86
View File
@@ -0,0 +1,86 @@
<!-- m-wiki: type=top-level slug=architecture topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Architecture
devops-lib is a Jenkins Shared Library that implements the entire CI/CD pipeline for all Meesho microservices. Consumer services load it via `@Library('devops-lib@main')` and delegate their full builddeploynotify lifecycle to it through a single `eksCICD(repo)` call.
## TL;DR
- One entry point (`vars/eksCICD.groovy`) routes to GCP (pod-based) or AWS (EKS node) infra via `CLOUD_PROVIDER`.
- A sequential flow: checkout → config parse → build stage → ArgoCD deploy → Ringmaster/Turbo-Turtle notify.
- Language-specific build logic lives in `src/com/meesho/stages/build*.groovy`; infra and policy env vars in `src/com/meesho/utilities/constructParam.groovy`.
- All deployments go through ArgoCD; no `kubectl apply` ever runs directly.
- Secrets and whitelists are injected at runtime — never hard-coded.
## Mental model
Think of `eksCICD` as a dispatcher: it doesn't contain any build or deploy logic itself. It (1) authenticates the triggering user, (2) selects the right infrastructure pod or node, and (3) hands off to `commonCICDFlow`, which assembles and runs the actual pipeline stages in sequence.
Each stage is a separate Groovy class with a `run(Map config)` method. `constructParam.run()` populates `env.*` variables (registry URL, vault endpoint, ArgoCD credentials, etc.) so every downstream stage can read a consistent environment without reconfiguring itself.
## Structure / data flow
```
Consumer Jenkinsfile
└─ @Library('devops-lib@main') → eksCICD(repo)
├─ [auth guard] allowedUsers check
├─ CLOUD_PROVIDER=GCP → gcpInfra() → podTemplate(INFRA_ENV-pod.yaml) → node(POD_LABEL)
│ └─ container('devops-tools') → commonCICDFlow(repo)
└─ CLOUD_PROVIDER=AWS → awsInfra() → node('EKS') → commonCICDFlow(repo)
commonCICDFlow(repo)
├─ checkOut.run(repo) ← clone service + submodules
├─ getYamlParameter.getParam() ← parse config.yaml
├─ buildObjHelper.run(build_tool) ← dispatch to language builder
├─ constructParam.run(param) ← resolve env.* vars
├─ hotFix.run(repo_name) ← skip tests/sonar on hotfix/*
└─ buildObj.run(param) ← build + deploy + notify
├─ build (Maven/Go/Node/…)
├─ deployArgoCD.run()
└─ notify.run() → deployRingmaster.run()
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `call` | `vars/eksCICD.groovy:call` | Top-level entry — auth guard + infra routing |
| `gcpInfra` | `vars/eksCICD.groovy:gcpInfra` | Loads pod YAML from `resources/org/meesho/` and wraps in `podTemplate` |
| `awsInfra` | `vars/eksCICD.groovy:awsInfra` | Runs on static EKS node labelled `EKS` |
| `commonCICDFlow` | `vars/eksCICD.groovy:commonCICDFlow` | Orchestrates the full stage sequence |
| `run` | `src/com/meesho/stages/buildObjHelper.groovy:run` | Dispatches to language builder by `build_tool` |
| `run` | `src/com/meesho/utilities/constructParam.groovy:run` | Sets all `env.*` vars for build + deploy |
| `run` | `src/com/meesho/stages/deployArgoCD.groovy:run` | 4-step ArgoCD deploy per deployable |
| `run` | `src/com/meesho/stages/notify.groovy:run` | Slack + Ringmaster/Turbo-Turtle callback |
## Sharp edges
- **Authorization is strict**: only `ringmaster-bot`, `turbo-turtle`, and the `allowedUsers` list can trigger builds. Builds not in this list are rejected immediately with a message directing to Ringmaster.
- **`env.*` mutation is CPS-bound**: `constructParam.run()` sets `env.*` in a CPS method. Any utility that needs `@NonCPS` cannot read from `env.*` inside the annotation — use method parameters instead.
- **GCP vs AWS env diverge**: `constructParam.run()` has two separate `accountDetails` maps for GCP and AWS. Registry URL, bucket name, and ArgoCD cluster coordinates differ between clouds.
- **`useSidecar` flag**: services that need a sidecar container can pass `useSidecar: true` in the repo Map. This switches the pod template from `INFRA_ENV-pod.yaml` to `INFRA_ENV-sidecar-pod.yaml`.
## Related concepts
- [Build dispatch](build/build-dispatch.md) — how `buildObjHelper` matches `build_tool` strings
- [Environment mapping](05-ENVIRONMENT-MAPPING.md) — branch/PR → `cicd_environment` table
- [Config policy](06-CONFIG-POLICY.md) — `constructParam` env resolution + whitelist gates
- [Deploy ArgoCD](04-DEPLOY-ARGOCD.md) — 4-step deploy sequence
- [Infra pods](09-INFRA-PODS.md) — GCP pod spec selection
- [ADR index](adr/adr-index.md) — the "why" behind devops-lib's core design decisions
- [Security overview](security/security-overview.md) — trust boundaries and credential handling
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](01-ARCHITECTURE.md) · [Index](../index.md) · [Next →](02-ENTRYPOINTS.md)
+57
View File
@@ -0,0 +1,57 @@
<!-- m-wiki: type=top-level slug=overview topic=null base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:README.md, code:vars/buildPipeline.groovy, code:src/com/meesho/stages/buildObjHelper.groovy, code:resources/com/meesho/config.yaml] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: top-level. 4 sources.
# Overview
**devops-lib** is the Jenkins shared library backing every Meesho service's CI/CD pipeline. Service repos import it via `@Library('devops-lib') _` at the top of their `Jenkinsfile`, then call one of the `vars/` globals (most commonly `buildPipeline { ... }` or `eksCICD { ... }`).
It is **not** a service, has no `Dockerfile`, no `Makefile`, no test suite, no local run command. The only "runtime" is Jenkins itself: a Jenkins controller loads the library, evaluates the Jenkinsfile, and executes stages on a pod template selected by `env.INFRA_ENV`.
## How a service repo consumes it
```groovy
// <service-repo>/Jenkinsfile
@Library('devops-lib') _
buildPipeline {
repo_name = 'order-service'
build_tool = 'maven' // or 'go', 'node', 'python', 'gradle', 'docker', 'php'
maintainer = 'roshan.v' // Slack handle for #ci-cd-status mentions
skip_test = false
skip_sonar = false
push_to_jfrog = false // default-branch-only push otherwise
deployArgo = true
}
```
The map flows into [`vars/buildPipeline.groovy`](../../../vars/buildPipeline.groovy) which:
1. Pins to `node('slave02')` ([line 16](../../../vars/buildPipeline.groovy)).
2. Sets `env.msg = 'Job Passed'` and wraps everything in `ansiColor` + `timestamps`.
3. Calls `checkOut``buildObjHelper.run(param.build_tool)` → the selected stage class's `run(param)``notify`.
## What lives where
| Path | Contains | Read by |
|---|---|---|
| [`vars/`](../../../vars/) | 10 entry-point Groovy scripts — the "public API" | Service Jenkinsfiles |
| [`src/com/meesho/stages/`](../../../src/com/meesho/stages/) | 19 stage classes (build, deploy, notify, scan) | `vars/` entry points |
| [`src/com/meesho/utilities/`](../../../src/com/meesho/utilities/) | 8 helpers — `constructParam`, `gitActions`, `nodePoolSelection`, `constructTemplate`, etc. | Stages |
| [`resources/com/meesho/`](../../../resources/com/meesho/) | Per-language Dockerfile + values.yaml + deployment.yaml; the Python validator | Stages (rendered into the service workspace) |
| [`resources/org/meesho/`](../../../resources/org/meesho/) | `{dev,stg,prd}-pod.yaml` Jenkins agent pod templates | `vars/` (via `libraryResource`) |
## Why "no local build / no test suite"
This is a Groovy library loaded by Jenkins, not a JVM app. There is no `build.gradle`, no `pom.xml`, no `package.json` at the repo root — Jenkins discovers `vars/` and `src/` by convention. The only way to "test" a change is to push the branch and point a Jenkins job at `@Library('devops-lib@<branch>')`.
`BUGS_AND_IMPROVEMENTS_REPORT.md` flags the absent test suite as a P0 gap.
## Critical conventions to know before changing anything
- `vars/` files are the public API. Renaming or removing a global is a breaking change for every service Jenkinsfile in the org.
- The `build_tool` switch is in [`src/com/meesho/stages/buildObjHelper.groovy`](../../../src/com/meesho/stages/buildObjHelper.groovy). Unknown values fall through to `defaultBuild` silently.
- The deploy step order (`update_argo_repo``refresh_app_of_apps``update_helm_repo``refresh_and_sync`) is load-bearing — see [04-deploy-flow](04-deploy-flow.md).
- `constructTemplate._construct()` is `@NonCPS` ([constructTemplate.groovy:13-20](../../../src/com/meesho/utilities/constructTemplate.groovy)) — do not call it across a `parallel` boundary.
See also: [02-entry-points](02-entry-points.md), [03-build-dispatch](03-build-dispatch.md), [04-deploy-flow](04-deploy-flow.md), [05-cross-cutting](05-cross-cutting.md), [`docs/tribal-knowledge.md`](../../tribal-knowledge.md), [`docs/acronyms.md`](../../acronyms.md).
+72
View File
@@ -0,0 +1,72 @@
<!-- m-wiki: type=top-level slug=entrypoints topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Entry Points
The `vars/` directory contains every Groovy script that is callable from a consumer Jenkinsfile. Each file in `vars/` becomes a global function in the Jenkins pipeline namespace.
## TL;DR
- `eksCICD` is the primary entry point used by all microservices.
- `buildPipeline` is a legacy wrapper kept for backward compatibility.
- `gkeCICD`, `gcpMigration`, `cloudFunctionCICD` are specialised for specific infra targets.
- `createEKSconfigs` and `onlyPushtoJfrog` are utility entry points, not full CI/CD flows.
- `log`, `stageName`, `automationTest` are helper utilities exposed as global functions.
## Mental model
Any `.groovy` file placed in `vars/` is automatically loaded by Jenkins as a global variable/function. Consumer Jenkinsfiles call these directly: `eksCICD(repo)`, `buildPipeline(repo)`, etc. There is no package declaration in `vars/` files — they are scripts, not classes.
## Structure / data flow
```
vars/
├─ eksCICD.groovy Primary entry — all active microservices
├─ buildPipeline.groovy Legacy alias → wraps eksCICD
├─ gkeCICD.groovy GKE-specific pipeline (minimal stub)
├─ gcpMigration.groovy AWS→GCP migration helper
├─ cloudFunctionCICD.groovy Cloud Functions CI/CD
├─ createEKSconfigs.groovy EKS kubeconfig bootstrapper
├─ onlyPushtoJfrog.groovy Pushes JAR to JFrog without full pipeline
├─ automationTest.groovy Automation test runner entry
├─ buildDockerGroovyGke.groovy Docker build for GKE target
├─ log.groovy Global log.info/log.error/log.warn helpers
└─ stageName.groovy Returns stage name string for display
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `call` | `vars/eksCICD.groovy:call` | Main CI/CD flow — auth guard, infra routing |
| `call` | `vars/buildPipeline.groovy:18` | Legacy entry — delegates to eksCICD internals |
| `call` | `vars/gkeCICD.groovy:1` | GKE variant (thin stub) |
| `call` | `vars/cloudFunctionCICD.groovy:1` | Cloud Functions deployment flow |
| `call` | `vars/createEKSconfigs.groovy:1` | EKS kubeconfig bootstrap utility |
| `call` | `vars/log.groovy:1` | Global logging helper (info/error/warn) |
## Sharp edges
- **`vars/` scripts run in the Jenkins CPS interpreter.** Any non-serializable Java object (iterators, closures with complex state) passed from `vars/` to a `@NonCPS` method will throw `NotSerializableException` at runtime.
- **`buildPipeline` is legacy** — do not add new consumers to it. All new services should use `eksCICD`.
- **`log.groovy` shadows Jenkins' built-in `echo`** in some contexts — if you see unexpected log formatting, check whether `log.info` or `echo` was used.
- **`automationTest.groovy` is independent** — it does not go through `eksCICD`; automation test repos call it directly.
- **`scm` is only available in consumer Jenkinsfiles**: the `scm` variable (branch, remote URL, credentials) is injected by the GitHub Branch Source plugin into consumer Jenkinsfiles only — not into `vars/` or `src/` of the shared library. Referencing `scm.branches` in library code causes `MissingPropertyException` at runtime. Use `env.BRANCH_NAME`, `env.GIT_URL`, or `env.CHANGE_*` instead.
See also: [SCM variable scope](build/scm-variable-scope.md)
## Related concepts
- [Architecture](01-ARCHITECTURE.md) — how eksCICD orchestrates the pipeline
- [Build stages](03-BUILD-STAGES.md) — what runs after the entry point selects infra
- [Environment mapping](05-ENVIRONMENT-MAPPING.md) — branch-to-env resolution
- [SCM variable scope](build/scm-variable-scope.md) — why `scm` is unavailable in library code
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](01-ARCHITECTURE.md) · [Index](../index.md) · [Next →](03-BUILD-STAGES.md)
+40
View File
@@ -0,0 +1,40 @@
<!-- m-wiki: type=top-level slug=entry-points topic=null base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:vars/buildPipeline.groovy, code:vars/eksCICD.groovy, code:vars/gkeCICD.groovy, code:vars/onlyPushtoJfrog.groovy, code:vars/createEKSconfigs.groovy, code:vars/cloudFunctionCICD.groovy, code:vars/log.groovy, code:vars/stageName.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: top-level. 8 sources.
# Entry points (vars/)
Every file under [`vars/`](../../../vars/) is automatically exposed by Jenkins as a global step bearing the file's name (no `.groovy` suffix). Service Jenkinsfiles call them like top-level functions; renaming a file is a breaking change for every consumer.
| File | Call signature | What it does |
|---|---|---|
| [`buildPipeline.groovy`](../../../vars/buildPipeline.groovy) | `def call(Map param)` | Legacy primary entry. Pins to `node('slave02')` ([line 16](../../../vars/buildPipeline.groovy)), runs checkout → buildObjHelper dispatch → optional automation tests → notify. |
| [`eksCICD.groovy`](../../../vars/eksCICD.groovy) | `def call(Map repo)` | Authorized-user gate ([lines 12-26](../../../vars/eksCICD.groovy) — explicit allowedUsers list), then routes GCP → pod template OR AWS → EKS node selector, then `commonCICDFlow()`. Used by most modern services. |
| [`gkeCICD.groovy`](../../../vars/gkeCICD.groovy) | `def Podcall(Map stepParams)` | Sparse legacy entry — Maven build + Docker push with hardcoded pod template. New services should not use this. |
| [`createEKSconfigs.groovy`](../../../vars/createEKSconfigs.groovy) | `def call(Map params)` | EKS Helm config generator. Validates BU/team and renders config files. Pod from `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")` ([line 5](../../../vars/createEKSconfigs.groovy)). |
| [`onlyPushtoJfrog.groovy`](../../../vars/onlyPushtoJfrog.groovy) | declarative pipeline (no `call()`) | One-off artifact push without the full pipeline. Choice parameter declares `['jdk8', 'jdk11', 'jdk17', 'jdk21']` ([line 16](../../../vars/onlyPushtoJfrog.groovy)) but the JAVA_HOME switch ([lines 48-53](../../../vars/onlyPushtoJfrog.groovy)) only handles `jdk17` explicitly — jdk11 and jdk21 fall through to the jdk8 default. ⚠ flagged in BUGS report. |
| [`gcpMigration.groovy`](../../../vars/gcpMigration.groovy) | `def call(Map params)` | One-off migration helper that emits a Jenkinsfile + Helm chart for a GCP-main branch. |
| [`buildDockerGroovyGke.groovy`](../../../vars/buildDockerGroovyGke.groovy) | `def run(Map config)` | Multi-module Docker build helper for GKE with artifact-version detection. |
| [`cloudFunctionCICD.groovy`](../../../vars/cloudFunctionCICD.groovy) | `def cloudFunctionCICDFlow()` | **Skeleton only.** Body is `sh 'ls -al'; echo 'Hello World'` ([lines 19-30](../../../vars/cloudFunctionCICD.groovy)) — flagged in BUGS report as not-yet-implemented. |
| [`log.groovy`](../../../vars/log.groovy) | `def info/warning/error(msg)` | ANSI-colour logging wrapper. See [concepts/observability](concepts/observability.md). |
| [`stageName.groovy`](../../../vars/stageName.groovy) | `def call(String description)` | Labels stages with environment + step counter. Reads `cicd_environment``INFRA_ENV``BUILD_ENV``ENVIRONMENT` (default `ftr`). |
## The user-authorization gate (eksCICD only)
[`eksCICD.groovy:12-26`](../../../vars/eksCICD.groovy) holds an explicit allowlist of users (`'turbo-turtle'`, `'ringmaster-bot'`, and a handful of named engineers). Builds triggered by anyone outside that list are **failed with a hard `error()`** and a banner directing them to Ringmaster. `buildPipeline` does **not** enforce this gate — services on the legacy entry point can be triggered directly.
## Pod template wiring
`env.INFRA_ENV` selects the Jenkins agent template via `libraryResource("org/meesho/${env.INFRA_ENV}-pod.yaml")`. Used in [`eksCICD.groovy:57`](../../../vars/eksCICD.groovy), [`createEKSconfigs.groovy:5`](../../../vars/createEKSconfigs.groovy), and [`onlyPushtoJfrog.groovy:4`](../../../vars/onlyPushtoJfrog.groovy). The resolution table:
| `env.INFRA_ENV` | Resource | Use |
|---|---|---|
| `dev` | [`resources/org/meesho/dev-pod.yaml`](../../../resources/org/meesho/dev-pod.yaml) | Dev / sandbox |
| `stg` | [`resources/org/meesho/stg-pod.yaml`](../../../resources/org/meesho/stg-pod.yaml) | Staging |
| `prd` | [`resources/org/meesho/prd-pod.yaml`](../../../resources/org/meesho/prd-pod.yaml) | Production |
All three are single-container pods (`devops-tools` from the `asia-southeast1-docker.pkg.dev/meesho-devops-admin-0622/admin/devops/build-tools:lunar-v1.0.1*` image), with `nodeSelector: {dedicated: jenkins}` and the matching toleration.
Don't add inline `podTemplate` blocks in stage code — they bypass the central agent inventory.
See also: [03-build-dispatch](03-build-dispatch.md), [`docs/architecture.md`](../../architecture.md).
+78
View File
@@ -0,0 +1,78 @@
<!-- m-wiki: type=top-level slug=build-stages topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Build Stages
Build logic lives in `src/com/meesho/stages/`. `buildObjHelper.groovy` is the dispatch router — it inspects the `build_tool` string from `config.yaml` and returns the appropriate builder class instance.
## TL;DR
- `buildObjHelper.run(build_tool)` returns an object with a `.run(param)` method.
- Matching uses Groovy's `switch/case` with regex patterns (e.g., `~/^go.*/`).
- Eight language builders exist: Maven, Go, Gradle, Node, Python, PHP, Rust, Docker.
- Each builder follows the same interface: `def run(Map config)`.
- Config-only change detection runs inside each builder before invoking the compiler/test runner — if only YAML files changed, the build step is skipped.
## Mental model
`buildObjHelper` is a factory. It reads one string (`build_tool`) and returns an object. The caller (`commonCICDFlow`) doesn't need to know which concrete class was returned — it just calls `.run(param)`. This is an interface-based dispatch pattern in Groovy without formal interfaces.
Each builder is responsible for the complete build lifecycle of its language: compile, test, Sonar scan, Docker image build, and pushing to the registry. After building, each builder typically calls `deployArgoCD.run()` or similar to kick off the deploy phase.
## Structure / data flow
```
config.yaml: build_tool: "go-1.22"
buildObjHelper.run("go-1.22")
switch "go-1.22":
case ~/^go.*/ → return new buildGo()
buildGo.buildDckr(param)
├─ [config-only check] git diff configs/ → skip build if no src change
├─ go build / go test
├─ sonar scan (constructParam.skipSonarCheckForGo)
├─ docker build + push (constructTemplate + dockerUtilities)
└─ deployArgoCD.run(...) or skip if deployArgo=false
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `run` | `src/com/meesho/stages/buildObjHelper.groovy:run` | Regex dispatch to builder class |
| `run` | `src/com/meesho/stages/buildMaven.groovy:run` | Maven/Java builder |
| `buildDckr` | `src/com/meesho/stages/buildGo.groovy:buildDckr` | Go builder |
| `buildDckr` | `src/com/meesho/stages/buildNode.groovy:buildDckr` | Node.js builder |
| `buildDckr` | `src/com/meesho/stages/buildPython.groovy:buildDckr` | Python builder |
| `run` | `src/com/meesho/stages/buildGradle.groovy:run` | Gradle/Java builder |
| `buildDckr` | `src/com/meesho/stages/buildPhp.groovy:buildDckr` | PHP builder |
| `buildDckr` | `src/com/meesho/stages/buildRust.groovy:buildDckr` | Rust builder |
| `run` | `src/com/meesho/stages/buildDocker.groovy:run` | Docker-only builder |
| `run` | `src/com/meesho/stages/hotFix.groovy:run` | Sets hot_fix flag — skips tests/sonar |
| `run` | `src/com/meesho/stages/checkOut.groovy:run` | Git checkout stage |
## Sharp edges
- **The dispatch is regex-based, not exact-match for most languages.** `~/^go.*/` matches `go`, `go-1.22`, `go-1.21`, etc. Typos in `config.yaml` silently fall through to `defaultBuild()` — there is no explicit error for unrecognized `build_tool` values. A mis-spelled `build_tool` produces a silent no-op build that succeeds with no artifact.
- **Go and Node entry points are `buildDckr`, not `run`**: `buildGo.groovy` and `buildNode.groovy` expose `buildDckr(Map config)` as their primary method. `buildMaven.groovy`, `buildGradle.groovy`, and `buildPython.groovy` use `run(Map config)`. This asymmetry is historical.
- **`hotFix.run()` runs before `buildObj.run()`** in `commonCICDFlow`. It sets `env.hot_fix = true` for hotfix/* branches, which causes each builder to skip tests and Sonar.
- **`buildPython` has multiple variants.** `python-3.10`, `python-3.12`, etc. all match `~/^python-.*/` and resolve to the same `buildPython` class, which reads `dockerBuildVersion` internally to select the right base image.
## Related concepts
- [Build dispatch](build/build-dispatch.md) — detailed regex pattern table for buildObjHelper
- [Config-only detection](build/config-only-detection.md) — how builders skip builds when only YAML changed
- [Docker tagging](build/docker-tagging.md) — `getDockerParams.getTag()` tag format
- [Dockerfile templates](08-DOCKERFILE-TEMPLATES.md) — how builder selects and renders the right Dockerfile
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](02-ENTRYPOINTS.md) · [Index](../index.md) · [Next →](04-DEPLOY-ARGOCD.md)
+66
View File
@@ -0,0 +1,66 @@
<!-- m-wiki: type=top-level slug=build-dispatch topic=null base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:src/com/meesho/stages/buildObjHelper.groovy, code:src/com/meesho/stages/buildMaven.groovy, code:src/com/meesho/stages/buildGo.groovy, code:src/com/meesho/stages/buildNode.groovy, code:src/com/meesho/stages/buildPython.groovy, code:src/com/meesho/stages/buildGradle.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: top-level. 6 sources.
# Build dispatch
[`buildObjHelper.groovy`](../../../src/com/meesho/stages/buildObjHelper.groovy) is the central switch from the `build_tool` parameter (set in the service Jenkinsfile) to the stage class that actually runs. `vars/buildPipeline.groovy:12` calls `buildObjHelper.run(param.build_tool)` and assigns the return to `buildObj`; everything from then on flows through that object.
## The switch (buildObjHelper.groovy:5-30)
| `build_tool` value | Stage class | Source |
|---|---|---|
| `maven` | `buildMaven` | [`src/com/meesho/stages/buildMaven.groovy`](../../../src/com/meesho/stages/buildMaven.groovy) |
| `maven-*` (e.g. `maven-3.3-jdk-17`) | `buildMaven` | same |
| `docker` | `buildDocker` | [`src/com/meesho/stages/buildDocker.groovy`](../../../src/com/meesho/stages/buildDocker.groovy) |
| `python-*` (e.g. `python-3.10`) | `buildPython` | [`src/com/meesho/stages/buildPython.groovy`](../../../src/com/meesho/stages/buildPython.groovy) |
| `node-*` (e.g. `node-16`) | `buildNode` | [`src/com/meesho/stages/buildNode.groovy`](../../../src/com/meesho/stages/buildNode.groovy) |
| `go*` (e.g. `go1.21`) | `buildGo` | [`src/com/meesho/stages/buildGo.groovy`](../../../src/com/meesho/stages/buildGo.groovy) |
| `gradle` | `buildGradle` | [`src/com/meesho/stages/buildGradle.groovy`](../../../src/com/meesho/stages/buildGradle.groovy) |
| `php` | `buildPhp` | [`src/com/meesho/stages/buildPhp.groovy`](../../../src/com/meesho/stages/buildPhp.groovy) |
| any other value | `defaultBuild` (silent no-op) | (default case at line 28) |
There is **no `sbt`** case and **no `rust`** case, despite what older docs may have implied (both have been reconciled out — see [`review-learnings.md`](../../../review-learnings.md) for the audit trail).
## Per-language quirks worth knowing
### `buildMaven`
- CAC validation runs before build ([`buildMaven.groovy:186-201`](../../../src/com/meesho/stages/buildMaven.groovy)).
- Sonar scan triggered with `withSonarQubeEnv` ([line 245](../../../src/com/meesho/stages/buildMaven.groovy)) against `sonarqube-prd` via [`constructParam.groovy:172`](../../../src/com/meesho/utilities/constructParam.groovy).
- Quality-gate timeout: 600s on prd, 360s elsewhere ([lines 286-296](../../../src/com/meesho/stages/buildMaven.groovy)).
- JFrog push gated on branch ∈ `{master, main}` OR `push_to_jfrog=true` ([lines 377, 410](../../../src/com/meesho/stages/buildMaven.groovy)). S3 push gated on branch ∈ `{master, main, gcp-main, gcp-master}` OR `push_to_s3=true` ([lines 462, 497](../../../src/com/meesho/stages/buildMaven.groovy)).
### `buildGo`
- `sonar_scan()` ([lines 212-270](../../../src/com/meesho/stages/buildGo.groovy)) downloads the Go binary AND the sonar-scanner zip via `curl` inside the build stage itself ([lines 227, 236](../../../src/com/meesho/stages/buildGo.groovy)) — there is no fileExists check on `sonar-project.properties` before the scan ([line 220](../../../src/com/meesho/stages/buildGo.groovy)).
- Go test failures are caught and logged but **do not propagate** as pipeline failure ([lines 232-235](../../../src/com/meesho/stages/buildGo.groovy)): `echo "Go tests failed, but the pipeline will continue."`.
- `env.hot_fix = true` (set by [`hotFix.groovy:11`](../../../src/com/meesho/stages/hotFix.groovy)) skips Sonar AND the quality gate ([lines 25-28](../../../src/com/meesho/stages/buildGo.groovy)).
- Multi-module builds run in `parallel { }` ([lines 161-179](../../../src/com/meesho/stages/buildGo.groovy)).
- Docker push wrapped by `retryDockerPush` (5 attempts, 3s sleep) — see [05-cross-cutting](05-cross-cutting.md).
### `buildNode`
- `getNpmRc` ([lines 63-68](../../../src/com/meesho/stages/buildNode.groovy)) fetches `MEESHO_NPMRC_SECRET` from AWS Secrets Manager or Vault and writes it to `.npmrc` in the workspace.
- Inline `.env` writes for `GITHUB_TOKEN` and `SONAR` credentials ([lines 283-312](../../../src/com/meesho/stages/buildNode.groovy)) — note: there is **no** `truncate -s 0 .env` cleanup pattern; that was a historical PR concern that has been resolved.
- `DOCKER_BUILDKIT=0` is set explicitly ([lines 387, 392, 400, 405](../../../src/com/meesho/stages/buildNode.groovy)) — BuildKit is **disabled**, not enabled. The `BUILDKIT` acronym entry in [`docs/acronyms.md`](../../acronyms.md) was reconciled accordingly.
- Docker push via `retryDockerPush` ([line 389](../../../src/com/meesho/stages/buildNode.groovy)).
### `buildPython`
- Docker build with module support ([lines 89-210](../../../src/com/meesho/stages/buildPython.groovy)) — no Sonar scan (unlike Maven / Go / Node).
- Duplicate-key bug in `docker_bindings` map at [lines 75 and 78](../../../src/com/meesho/stages/buildPython.groovy) — second assignment overwrites the first. Flagged in BUGS report.
### `buildGradle`
- `branch_name = 'repo'` hard-coded at [lines 252 and 285](../../../src/com/meesho/stages/buildGradle.groovy) — the subsequent comparison against `'master'/'main'` therefore never matches the real branch. Flagged in BUGS report; do not assume `branch_name` is dynamic there.
- Uses `xq` to query `build.gradle` ([lines 255-256, 260, 288-289, 293](../../../src/com/meesho/stages/buildGradle.groovy)) — `xq` is an XML query tool and Gradle files are Groovy/Kotlin DSL, not XML. Flagged.
## Adding a new build_tool
1. Create `src/com/meesho/stages/build<Lang>.groovy` with a `run(Map param)` method.
2. Add a `case` in [`buildObjHelper.groovy`](../../../src/com/meesho/stages/buildObjHelper.groovy) — either a literal (`case 'rust'`) or a regex (`case ~/^rust-.*/`) for versioned variants.
3. Add `resources/com/meesho/<lang>-Dockerfile`, `<lang>-values.yaml`, `<lang>-deployment.yaml`.
4. There is no unit test to add — push the branch and validate via `@Library('devops-lib@<branch>')` in a sandbox service Jenkinsfile.
See also: [04-deploy-flow](04-deploy-flow.md), [05-cross-cutting](05-cross-cutting.md), [`docs/architecture.md`](../../architecture.md).
+88
View File
@@ -0,0 +1,88 @@
<!-- m-wiki: type=top-level slug=deploy-argocd topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Deploy via ArgoCD
All Meesho microservice deployments go through ArgoCD. `deployArgoCD.groovy` orchestrates a 4-step sequence per deployable: it first commits an ArgoCD Application manifest to `devops-argo-config`, then triggers a Helm values update in `devops-helm-charts`, and finally hard-refreshes and syncs the ArgoCD application.
## TL;DR
- 4 steps per deployable: `update_argo_repo``refresh_app_of_apps``update_helm_repo``refresh_and_sync`.
- Every step commits to a separate Git repo (argo-config or helm-charts), opens a PR, merges it, and deletes the branch.
- The deploy waits for user input (checkbox UI) to choose which deployables to deploy, with a 300s timeout.
- Multizone deployables are **blocked** — they must use Ringmaster.
- JVM heap (`xms`/`xmx`) defaults to 50% of `memory_limit`, overridable via `deployment_args`.
## Mental model
`deployArgoCD` treats Git as the deployment API. Every config change becomes a PR in `devops-argo-config` or `devops-helm-charts`, which ArgoCD polls and syncs. The pipeline commits the changes atomically for one deployable at a time in a `for` loop.
The user input step allows partial deploys — you can select "All" or individual apps from the checkbox list. If you skip input (timeout or `skip_user_input=true`), all apps in `deployment_order` are deployed.
## Structure / data flow
```
deployArgoCD.run(repo_name, deployment_order, tag, ...)
├─ [whitelist check] constructParam.allowedNonDevelopPrDeploymentToIntRepos()
├─ [user input] wait_for_user_input(deployment_order) — 300s timeout
└─ for each deployable:
├─ constructParam.isMultizoneEnabled(deployment) → ERROR if true
├─ constructParam.perDeploymentVars(value_binding) ← sets env.argoURL, env.argoIncubator, etc.
├─ stage: update_argo_repo()
│ ├─ render argoApp.yaml template → devops-argo-config/applications_v2/<cluster>/<team>-<app>.yaml
│ └─ commit → PR → merge → delete branch
├─ stage: refresh_app_of_apps()
│ └─ argocd app sync <argoIncubator>
├─ stage: update_helm_repo()
│ ├─ render values.yaml template → devops-helm-charts/<env>/bu/team/app/values.yaml
│ ├─ [canary enforcement] enforce skipAnalysis=false for sp0/up0 services
│ ├─ [dependabot check] block if CRITICAL CVEs on prd sp0-sp1 deploys
│ └─ commit → PR → merge → delete branch
└─ stage: refresh_and_sync()
├─ argocd app get --hard-refresh <env>-<app_name>
└─ argocd app sync <env>-<app_name>
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `run` | `src/com/meesho/stages/deployArgoCD.groovy:run` | Main entry — user input + per-deployable loop |
| `update_argo_repo` | `src/com/meesho/stages/deployArgoCD.groovy:update_argo_repo` | Renders ArgoCD Application YAML and commits to argo-config |
| `refresh_app_of_apps` | `src/com/meesho/stages/deployArgoCD.groovy:refresh_app_of_apps` | Syncs the incubator app-of-apps |
| `update_helm_repo` | `src/com/meesho/stages/deployArgoCD.groovy:update_helm_repo` | Renders Helm values and commits to helm-charts |
| `refresh_and_sync` | `src/com/meesho/stages/deployArgoCD.groovy:refresh_and_sync` | Hard-refreshes and syncs the ArgoCD app |
| `enable_backward_compatibility` | `src/com/meesho/stages/deployArgoCD.groovy:enable_backward_compatibility` | Fills in missing deployment.yaml keys with defaults |
| `dependabotCriticalCheck` | `src/com/meesho/stages/deployArgoCD.groovy:dependabotCriticalCheck` | Blocks deploy if CRITICAL CVEs found |
| `calculate_active_processors` | `src/com/meesho/stages/deployArgoCD.groovy:calculate_active_processors` | Converts cpu_request string to JVM -XX:ActiveProcessorCount |
## Sharp edges
- **Branch naming is environment-derived**: `helm_branch_name` maps `main/master→main`, `develop→develop`, PR→`feature` or `pre-prod`. This is independent of `cicd_environment` — confusion between the two causes PR target mismatches.
- **Feature deployments use ingress namespacing**: for `envrn=ftr`, Helm values go into `values_properties.yaml` under `<ingress_val>/` subdirectory, and the app branch is prefixed with `<ingress_val>-`.
- **AppConfig gate**: `appConfigDisabledForbidden()` blocks stg deploys for Maven/Gradle repos that have `appConfigEnabled=false` and are not in the `app-config-disabled` whitelist.
- **xms/xmx auto-calculation**: for Maven/Gradle, heap is set to 50% of `memory_limit`. This can be overridden by `Xms<val>` or `Xmx<val>` tokens in `deployment_args`. If a service hard-codes `-Xmx` in `JAVA_OPTS`, the auto-calculated value will collide; use `jvm_memory_override: true` in `deployment.yaml` to suppress auto-calc.
- **Canary is mandatory for sp0/up0**: `deployArgoCD.groovy:run` (`src/com/meesho/stages/deployArgoCD.groovy:run`) blocks a non-canary `prd` deploy when `priority_v2` is `sp0` or `up0`. There is no whitelist or flag to bypass this check — it happens before any Helm update.
- **The 4-step order is load-bearing**: steps 2 (`refresh_app_of_apps`) and 4 (`refresh_and_sync`) are not interchangeable. Skipping step 2 on a first deploy means the ArgoCD Application object hasn't been created yet, causing step 4 to target a non-existent app.
## Related concepts
- [ArgoCD sync](deploy/argocd-sync.md) — detailed step-by-step sync sequence
- [Ringmaster integration](deploy/ringmaster-integration.md) — when deployRingmaster runs instead
- [Whitelist system](policy/whitelist-system.md) — multizone + allowedNonDevelop gates
- [Node pool selection](infra/node-pool-selection.md) — how nodeSelectorValue is computed
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](03-BUILD-STAGES.md) · [Index](../index.md) · [Next →](05-ENVIRONMENT-MAPPING.md)
+87
View File
@@ -0,0 +1,87 @@
<!-- m-wiki: type=top-level slug=deploy-flow topic=null base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:src/com/meesho/stages/deployArgoCD.groovy, code:src/com/meesho/stages/deployRingmaster.groovy, code:src/com/meesho/stages/notify.groovy, code:src/com/meesho/stages/helmGenerator.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: top-level. 4 sources.
# Deploy flow
The deploy phase is owned by [`deployArgoCD.groovy`](../../../src/com/meesho/stages/deployArgoCD.groovy) (the ArgoCD ceremony) and [`deployRingmaster.groovy`](../../../src/com/meesho/stages/deployRingmaster.groovy) (the callback to the higher-level deploy controllers). Two things to never mess with: the four-step order, and the canary gate.
## The four-step ArgoCD ceremony — load-bearing
`deployArgoCD.run()` ([lines 73-107](../../../src/com/meesho/stages/deployArgoCD.groovy)) calls these in strict order:
```
1. update_argo_repo (line 91) pushes the ArgoApplication YAML into devops-argo-config
so the app-of-apps registry sees the new app.
2. refresh_app_of_apps (line 94) argocd app sync ${appofapps} — materialises the new
Application object before the per-service sync needs it.
3. update_helm_repo (line 97) computes xms/xmx, renders values.yaml, pushes into
devops-helm-charts.
4. refresh_and_sync (line 100) argocd app sync ${app_name} --hard-refresh — the
actual service rollout. Uses --http-retry-max 3
--retry-backoff-duration 1m (lines 506, 526).
```
Steps 2 and 4 are **not interchangeable**. On first-deploy of a brand-new service, the Application object doesn't exist yet — step 2 creates it (as a downstream effect of the app-of-apps sync), step 4 reads it. Swap the order and step 4 fails on a missing Application. See [`docs/tribal-knowledge.md`](../../tribal-knowledge.md) §10 for the post-incident note.
## JVM memory auto-calculation
`update_helm_repo` ([lines 220-255](../../../src/com/meesho/stages/deployArgoCD.groovy)) derives JVM memory flags from the pod's `memory_limit`:
```groovy
memory_value = memory_limit * 0.5
xms = "${memory_value}M"
xmx = "${memory_value}M"
```
Both flags are set equal, derived from `memory_limit` (not `memory_request`), with no 0.75 multiplier and no 64m rounding. The historical claim "`xmx = memory_request * 0.75, xms = xmx * 0.5`" was wrong and has been reconciled out of [`docs/tribal-knowledge.md`](../../tribal-knowledge.md).
**Do not hard-code `-Xmx` in `JAVA_OPTS`** — the auto-computed value will collide with it, and the last value seen by the JVM wins depending on arg order. There is no `jvm_memory_override` flag (that referenced flag does not exist in code).
## Canary enforcement — sp0 / up0 in prd
[`deployArgoCD.groovy:408-430`](../../../src/com/meesho/stages/deployArgoCD.groovy):
```
enforceCanary = (priority_v2 ∈ {sp0, up0})
∧ (envrn == 'prd')
∧ ¬(service is canary | cron | worker | scheduler | consumer | node | headless)
```
When `enforceCanary` is true, the deploy hard-errors unless the Helm values declare:
- `canary.enabled = true`
- `canary.skipAnalysis = false`
- `canary.enableManualPromotion = true`
There is **no whitelist** and **no bypass flag**. A service that needs to skip canary on a high-priority prd path has to either (a) be classified out of `sp0/up0`, or (b) match one of the exempted service types listed above.
## Ringmaster vs Turbo-Turtle routing
[`deployRingmaster.groovy:55-89`](../../../src/com/meesho/stages/deployRingmaster.groovy):
```groovy
def build_user = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
if (build_user == "ringmaster-bot") {
callApi(url, header, jsonData) // → ringmaster endpoint
} else {
// → http://turbo-turtle.meeshogcp.in (line 71)
// http://turbo-turtle.admin.meeshogcp.in (line 74)
sh "curl -s -X POST -H '$newCICD_Header' -w '\\n%{response_code}' $newCICD_URL -d '$newCICD_JSON'"
}
```
The string `"ringmaster-bot"` is a **load-bearing constant**. Renaming the bot user silently routes every Ringmaster callback to Turbo-Turtle, which rejects them.
The JSON payload is passed **inline** via `-d '$newCICD_JSON'` ([line 80](../../../src/com/meesho/stages/deployRingmaster.groovy)). Earlier documentation claimed a temp-file + `curl -d @<file>` pattern with `finally`-block cleanup — that pattern does **not** exist in the current code and was reconciled out of `docs/tribal-knowledge.md`.
## Deployment-tracker callback (notify.groovy)
[`notify.groovy:108-152`](../../../src/com/meesho/stages/notify.groovy) POSTs to one of:
- `http://deployment-tracker.meeshoint.in/api/1.0/deployment-tracker/jenkins/create` (AWS)
- `http://deployment-tracker.prd.meesho.int/api/1.0/deployment-tracker/jenkins/create` (GCP)
Only triggered on branches `main` / `master` / `gcp-main` / `gcp-master` ([line 34](../../../src/com/meesho/stages/notify.groovy)). Payload includes repository, team, link, job_name, tag, status, commit_id, error_msg. No retry — a single failed POST means the dashboard miscounts that deploy.
See also: [05-cross-cutting](05-cross-cutting.md), [concepts/whitelists](concepts/whitelists.md), [`docs/tribal-knowledge.md`](../../tribal-knowledge.md).
+70
View File
@@ -0,0 +1,70 @@
<!-- m-wiki: type=top-level slug=environment-mapping topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Environment Mapping
`cicd_environment` is the single string that controls which registry, vault, ArgoCD cluster, and GCS bucket the pipeline uses. It is derived from the branch name (for push builds) or the PR target branch (for PR builds).
## TL;DR
- Push to `main`/`master`/`gcp-main``prd`.
- Push to `develop``stg`.
- PR targeting `main``int` (pre-prod).
- PR targeting `develop``ftr` (feature).
- Any other branch → `ftr` (fallback).
- Hotfix branches skip tests and Sonar but still map to `prd`.
- `env.CHANGE_ID` being set signals a PR build and flips the mapping table.
## Mental model
`constructParam.run()` switches on `env.CHANGE_ID` to decide which `environment_map` to use. Without `CHANGE_ID` (push build), branches map to their canonical environments. With `CHANGE_ID` (PR build), the same branch names map to the pre-prod equivalents: `main→int`, `develop→ftr`. This double-mapping is why `develop` can be either `stg` (push) or `ftr` (PR).
## Structure / data flow
```
env.CHANGE_ID not set (push build):
environment_map = {
master/main/gcp-main/farmiso-main/gcp-master → prd
develop/gcp-dev → stg
}
branch not in map → ftr (default)
env.CHANGE_ID set (PR build):
branch_name = env.CHANGE_TARGET
environment_map = {
master/main/gcp-main/farmiso-main/gcp-master → int
develop/gcp-dev → ftr
}
env.cicd_environment = environment_map[branch_name]
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `run` | `src/com/meesho/utilities/constructParam.groovy:run` | Sets `env.cicd_environment` + all env vars |
| `run` | `src/com/meesho/stages/deployArgoCD.groovy:run` | Has its own `branch_param_map` for helm/argo branch naming |
| `run` | `src/com/meesho/stages/hotFix.groovy:run` | Detects `hotfix/*` branch and sets `env.hot_fix=true` |
## Sharp edges
- **Two separate maps exist**: `constructParam` has one map for `cicd_environment`; `deployArgoCD` has a different `branch_param_map` for the Helm/Argo Git branch names (`main`, `develop`, `feature`, `pre-prod`). These are not the same and must not be conflated.
- **`gcp-dev` maps to `stg`**: services on the `gcp-dev` branch deploy to staging, not a separate dev environment.
- **Toolchain override**: if `env.INFRA_ENV == 'toolchain'` and the service is Node, `branch_name` is forced to `develop` so the image goes to `stg` toolchain registry regardless of actual branch.
- **`farmiso-main`** is treated as an alias for `main` — it maps to `prd` on both push and PR builds to `main`.
- **Use `env.CHANGE_ID` to detect PR context — not `env.BRANCH_NAME =~ /PR-/`**: the `BRANCH_NAME =~ /PR-/` pattern breaks on non-GitHub SCMs and re-triggered builds. `env.CHANGE_ID` is the canonical PR-build detector set by the GitHub Branch Source plugin and is used throughout `constructParam.groovy:run` (`src/com/meesho/utilities/constructParam.groovy:run`).
## Related concepts
- [Config policy](06-CONFIG-POLICY.md) — `constructParam` sets env vars after mapping
- [Architecture](01-ARCHITECTURE.md) — where `commonCICDFlow` calls `constructParam`
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](04-DEPLOY-ARGOCD.md) · [Index](../index.md) · [Next →](06-CONFIG-POLICY.md)
+84
View File
@@ -0,0 +1,84 @@
<!-- m-wiki: type=top-level slug=cross-cutting topic=null base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:src/com/meesho/utilities/constructParam.groovy, code:src/com/meesho/utilities/nodePoolSelection.groovy, code:src/com/meesho/utilities/gitActions.groovy, code:src/com/meesho/stages/deployArgoCD.groovy, code:src/com/meesho/stages/hotFix.groovy, code:vars/log.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: top-level. 6 sources.
# Cross-cutting patterns
## Retries
| Caller | Downstream | Retry |
|---|---|---|
| [`deployArgoCD.groovy:506,526`](../../../src/com/meesho/stages/deployArgoCD.groovy) | ArgoCD sync | `argocd app sync --http-retry-max 3 --retry-backoff-duration 1m` |
| [`buildNode.groovy:389`](../../../src/com/meesho/stages/buildNode.groovy), [`buildGo.groovy:172`](../../../src/com/meesho/stages/buildGo.groovy) | Docker registry push | `retryDockerPush` — 5 attempts with 3s sleep between |
| `buildMaven`, `buildGradle`, `buildPython`, `buildPhp` | various | **No retry** |
| [`securityScan.groovy:12`](../../../src/com/meesho/stages/securityScan.groovy) | In-house scanner | **No retry** |
| [`notify.groovy:108-152`](../../../src/com/meesho/stages/notify.groovy) | Deployment Tracker | **No retry** — single failed POST = missed deploy in the dashboard |
`BUGS_AND_IMPROVEMENTS_REPORT.md` lists "no retry mechanism" as P1; the reconciled-in nuance is that retry IS present for ArgoCD sync + Docker push, but not for most other downstream calls.
## BU multi-tenancy
`env.BU` and `config.bu` flow from CAC config (read once by [`constructParam.groovy`](../../../src/com/meesho/utilities/constructParam.groovy)) and drive:
| Use | Code site |
|---|---|
| ArgoCD namespace | `argocd-${env.BU}-prd` — [`constructParam.groovy:318`](../../../src/com/meesho/utilities/constructParam.groovy) |
| GCP cluster name | `k8s-${env.BU}-prd-ase1` — [`constructParam.groovy:308`](../../../src/com/meesho/utilities/constructParam.groovy) |
| GCP project | `meesho-${config.bu}-prd-0622` — [`constructParam.groovy:178`](../../../src/com/meesho/utilities/constructParam.groovy) |
| Helm chart path | `${env.helmChartsPath}/${config.bu}/...` — [`helmGenerator.groovy:150`](../../../src/com/meesho/stages/helmGenerator.groovy), [`deployArgoCD.groovy:293`](../../../src/com/meesho/stages/deployArgoCD.groovy) |
| Non-prd node pool | `${env.BU}-shared` (all `dev`/`ftr`/`stg` traffic for the BU collapses to one pool) — [`nodePoolSelection.groovy:133-135`](../../../src/com/meesho/utilities/nodePoolSelection.groovy) |
**Implication for non-prd capacity planning:** a single noisy service in `bu=supply` degrades every other `supply` service on staging, because they all share `supply-shared`. See [`docs/tribal-knowledge.md`](../../tribal-knowledge.md) §8.
## Feature flags / toggle conditions
| Flag | Set by | Effect | Read at |
|---|---|---|---|
| `env.hot_fix` | [`hotFix.groovy:11`](../../../src/com/meesho/stages/hotFix.groovy) | Skips Sonar + quality gate; sets canary `skipAnalysis` | [`buildGo.groovy:25-28`](../../../src/com/meesho/stages/buildGo.groovy), [`buildMaven.groovy:37-40`](../../../src/com/meesho/stages/buildMaven.groovy), [`deployArgoCD.groovy:355`](../../../src/com/meesho/stages/deployArgoCD.groovy) |
| `config.skip_sonar` | Service Jenkinsfile param | Skips quality-gate check (gated by whitelist for Maven prd) | [`buildMaven.groovy:18,240-243`](../../../src/com/meesho/stages/buildMaven.groovy), [`constructParam.groovy:51-56`](../../../src/com/meesho/utilities/constructParam.groovy) |
| `config.skip_security_scan` | Service Jenkinsfile param | Skips POST to security-scan endpoint | [`securityScan.groovy:7-9`](../../../src/com/meesho/stages/securityScan.groovy) |
| `config.push_to_jfrog` | Service Jenkinsfile param | Allow non-default-branch JFrog push (default branches: master, main) | [`buildMaven.groovy:19,377`](../../../src/com/meesho/stages/buildMaven.groovy) |
| `config.push_to_s3` | Service Jenkinsfile param | Allow non-default-branch S3 push (default branches: master, main, gcp-main, gcp-master) | [`buildMaven.groovy:20,462`](../../../src/com/meesho/stages/buildMaven.groovy) |
| `env.INFRA_ENV == 'toolchain'` | CAC config | Skips Docker push, uses latest-tag logic | [`buildGo.groovy:40-58`](../../../src/com/meesho/stages/buildGo.groovy), [`buildNode.groovy:215-239`](../../../src/com/meesho/stages/buildNode.groovy) |
| `env.CHANGE_ID` | GitHub Branch Source plugin | PR-build detection; remaps `cicd_environment` (`prd→int` for main/master PRs, `stg→ftr` for develop PRs) | [`constructParam.groovy:107-110`](../../../src/com/meesho/utilities/constructParam.groovy) |
**Do not use `env.BRANCH_NAME =~ /PR-/`** as a PR-build detector — it breaks on re-triggered builds and non-GitHub SCMs.
## Parallel execution
The pipeline is **almost entirely linear**. The one confirmed `parallel { }` block is in multi-module Go builds at [`buildGo.groovy:161-179`](../../../src/com/meesho/stages/buildGo.groovy):
```groovy
for (m in modules) {
moduleBuilds["build-${moduleName}"] = { ... }
}
parallel moduleBuilds
```
Anything called from inside that closure must respect the `@NonCPS` rule (see [`constructTemplate.groovy:13-20`](../../../src/com/meesho/utilities/constructTemplate.groovy) — the template engine wrapper is `@NonCPS` because `SimpleTemplateEngine` is non-serialisable).
## Exception handling — inconsistent on purpose-ish
`catch (Exception e)` appears ~87 times across stages. Three observed patterns:
| Pattern | Example | Behaviour |
|---|---|---|
| Catch + log + **rethrow** | [`buildNode.groovy:20-26`](../../../src/com/meesho/stages/buildNode.groovy) | Hard-fail the stage. Most common. |
| Catch + log + **swallow** (loop body) | [`deployArgoCD.groovy:103-106`](../../../src/com/meesho/stages/deployArgoCD.groovy) | Continue with the next deployment in the loop. Intentional for multi-deploy resilience. |
| Catch + log + **swallow** (silent fall-through) | [`buildNode.groovy:199-200`](../../../src/com/meesho/stages/buildNode.groovy) | Recovers with a sensible default. Use with caution — readers don't always realise the stage "succeeded" while masking a real error. |
When adding new error handling, prefer the rethrow pattern unless the loop-resilience semantics are an explicit requirement.
## Validation: `validate_configs.py`
[`resources/com/meesho/validate_configs.py`](../../../resources/com/meesho/validate_configs.py) (1207 lines) is the monolithic CAC schema validator, invoked from Groovy via `sh`. Key validation classes:
- **Schema** ([line 949](../../../resources/com/meesho/validate_configs.py)) — `application-{dev,int,stg,prd}.yml` against `application-schema.yml`.
- **Cross-env endpoint check** ([line 1010](../../../resources/com/meesho/validate_configs.py)) — prevents stg configs from referencing prd endpoints.
- **Secrets detection** ([line 1099](../../../resources/com/meesho/validate_configs.py)) — via `detect_secrets` library.
- **DB URL patterns** ([lines 41-59](../../../resources/com/meesho/validate_configs.py)) — PostgreSQL/MySQL/MongoDB/Redis/SQLite URI shapes.
- **Zookeeper endpoint patterns** — env-specific allow-lists.
`validate_configs_v2.py` (1261 lines) is the eventual replacement; both are referenced today.
See also: [concepts/whitelists](concepts/whitelists.md), [concepts/secrets-and-auth](concepts/secrets-and-auth.md), [concepts/observability](concepts/observability.md).
+82
View File
@@ -0,0 +1,82 @@
<!-- m-wiki: type=top-level slug=config-policy topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Config and Policy
`constructParam.groovy` does two things: it resolves all `env.*` variables from `config.yaml` and the current branch/environment, and it enforces five policy gates by fetching `Meesho/whitelists` at runtime.
## TL;DR
- `config.yaml` is the service's contract — `getYamlParameter.getParam()` reads it before any build runs.
- `constructParam.run()` sets 15+ `env.*` vars (registry, vault, sonar, GCPProject, etc.) based on `cicd_environment`.
- Five whitelist files are fetched from `Meesho/whitelists` at runtime via `git clone` — not bundled.
- Whitelisted policies: skip-sonar, app-config-disabled, multizone, allowedNonDevelopPrToInt, ValidateCacConfig.
- `ValidateCacConfig` gate triggers `validate_configs_v2.py` on Go and Maven PRs.
## Mental model
`constructParam` is the policy layer. All build and deploy stages read `env.*` but never set it — they are consumers. `constructParam` is the sole producer. This ensures every stage shares a consistent view of which cloud, environment, registry, and policy applies to this build.
The whitelist pattern allows DevOps to grant exceptions without modifying any service's code — a repo is added to `Meesho/whitelists/skip-sonar-whitelist.yaml` and the next build automatically picks up the exception.
## Structure / data flow
```
config.yaml (in service repo):
repo_name, build_tool, dockerBuildVersion, bu, team,
deployment_order, notify_channel, skip_sonar, deployArgo, appConfigEnabled
getYamlParameter.getParam(repo_name) → param Map
constructParam.run(param):
├─ environment_map → env.cicd_environment
├─ GCP accountDetails[env.cicd_environment]:
│ env.GCPProject, env.registry, env.buildRegistry
│ env.vaultURL/Token, env.sonarURL/Token
│ env.objBucket, env.DOCKER_HOST
├─ [whitelist gate] skipSonarCheckForbidden() → clone Meesho/whitelists/skip-sonar-whitelist.yaml
├─ [whitelist gate] appConfigDisabledForbidden() → app-config-disabled.yaml
├─ [whitelist gate] isMultizoneEnabled() → multizone-enabled-repos.yaml
├─ [whitelist gate] allowedNonDevelopPrDeploymentToIntRepos() → allowedNonDevelopPrDeploymentToInt.yaml
└─ [whitelist gate] ValidateCacConfigForRepo() → ValidateCacConfig.yaml
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `run` | `src/com/meesho/utilities/constructParam.groovy:run` | Main env var setter |
| `perDeploymentVars` | `src/com/meesho/utilities/constructParam.groovy:perDeploymentVars` | Sets per-deployable ArgoCD vars |
| `getWhitelistedRepos` | `src/com/meesho/utilities/constructParam.groovy:getWhitelistedRepos` | Clones Meesho/whitelists and reads a YAML file |
| `skipSonarCheckForbidden` | `src/com/meesho/utilities/constructParam.groovy:skipSonarCheckForbidden` | Blocks Maven prd builds with skip_sonar=true if not whitelisted |
| `appConfigDisabledForbidden` | `src/com/meesho/utilities/constructParam.groovy:appConfigDisabledForbidden` | Blocks stg deploys if appConfig disabled and not whitelisted |
| `isMultizoneEnabled` | `src/com/meesho/utilities/constructParam.groovy:isMultizoneEnabled` | Returns true for deployables in multizone whitelist |
| `ValidateCacConfigForRepo` | `src/com/meesho/utilities/constructParam.groovy:ValidateCacConfigForRepo` | Returns true if repo must run CAC validation |
| `getParam` | `src/com/meesho/utilities/getYamlParameter.groovy:getParam` | Reads a YAML file from the workspace |
## Sharp edges
- **Each whitelist call does a fresh `git clone`**: `getWhitelistedRepos()` clones `Meesho/whitelists` into a `whitelist/` subdirectory every time it's called. Five separate calls = five clones in the same build. Network latency here directly adds to build time. This is deliberate — each clone captures the latest whitelist state so a DevOps policy change takes effect on the very next build without a library release. Never cache across calls.
- **Go sonar-skip logic is in `constructParam.groovy`, not `buildGo.groovy`**: `skipSonarCheckForGo(Map config)` (`src/com/meesho/utilities/constructParam.groovy:skipSonarCheckForGo`) centralises all skip-sonar policy. Embedding whitelist checks inline in language build stages is the wrong pattern.
- **`perDeploymentVars` must run before ArgoCD steps**: it sets `env.argoURL`, `env.argoCreds`, `env.argoAppNS`, and `env.argoIncubator` per deployable. Calling ArgoCD stages before this results in empty ArgoCD credentials.
- **`bu` drives GCP project name**: `prodGCPProject = "meesho-${config.bu}-prd-0622"`. An invalid or misspelled `bu` in `config.yaml` produces a nonexistent GCP project name.
- **Toolchain env skips several policies**: when `env.INFRA_ENV == 'toolchain'`, skip_sonar is forced true and vault/sonar are pointed at `toolchain-dind-dev-svc`.
## Related concepts
- [Whitelist system](policy/whitelist-system.md) — detailed whitelist file inventory
- [CAC validation](policy/cac-validation.md) — what happens when ValidateCacConfig=true
- [Multi-tenancy](policy/multi-tenancy.md) — BU/team mapping and initials
- [Environment mapping](05-ENVIRONMENT-MAPPING.md) — how cicd_environment is determined
- [Security overview](security/security-overview.md) — credential handling, trust boundaries, security rules for new code
- [ADR index](adr/adr-index.md) — architectural decisions behind the whitelist and policy model (ADR-0003, ADR-0004)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](05-ENVIRONMENT-MAPPING.md) · [Index](../index.md) · [Next →](07-LANGUAGE-BUILDS.md)
+87
View File
@@ -0,0 +1,87 @@
<!-- m-wiki: type=top-level slug=language-builds topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Language Builds
Each language builder in `src/com/meesho/stages/` implements a `run(Map config)` method. They share a common structure: read config → config-only check → compile/test → Sonar → Docker build → push → deploy.
## TL;DR
- **Maven** (`buildMaven`): Java services using pom.xml. Handles multi-module layouts. Skips tests/sonar on hotfix. Pushes JAR to JFrog optionally.
- **Go** (`buildGo`): Uses Athens proxy (`env.goProxyUrl`) for module caching. Sonar skipped if whitelisted or toolchain env.
- **Node** (`buildNode`): Multiple variants (`node-18`, `node-20`, etc.). Toolchain-aware image path includes `TOOLCHAIN_ENV` segment.
- **Python** (`buildPython`): `python-3.10`, `python-3.12`, etc. — version in `dockerBuildVersion`, not separate files.
- **Gradle** (`buildGradle`): Android/Kotlin server builds.
- **PHP** (`buildPhp`): PHP services, uses `php-values.yaml` Helm template.
- **Rust** (`buildRust`): Rust services.
- **Docker** (`buildDocker`): Docker-only builds — no compiler, just image assembly.
## Mental model
Every builder reads `config.build_tool` (via `buildObjHelper` dispatch) and `config.dockerBuildVersion`. The `dockerBuildVersion` controls which Dockerfile template is selected from `resources/com/meesho/` — the template name roughly mirrors the version string (e.g., `maven-21`, `go-1.22`, `node-20`).
Config-only change detection is embedded in each builder. If `git diff HEAD~1 -- configs/` shows only YAML changes, the builder skips compilation and fetches the last image tag from GAR instead.
## Structure / data flow
```
buildMaven.run(config):
env.TAG = "v${pom-version}" or hotfix → "v${version}-HOT"
├─ [config-only] check s3/GAR for existing artifact
├─ stage: Test → mvn test -P${java_version}
├─ stage: Sonar → mvn sonar:sonar (skip if skipSonarCheckForbidden)
├─ stage: Docker Build → constructTemplate.renderTemplate(binding, 'maven-Dockerfile')
├─ stage: Docker Push → dockerUtilities.buildAndPush(...)
└─ deployArgoCD.run(...) or deployJar.run(...)
buildGo.buildDckr(config):
env.TAG = getDockerParams.getTag(repo_name)
├─ [config-only] skip build if only configs/ changed
├─ stage: Build/Test → go build, go test (with Athens proxy)
├─ stage: Sonar → sonar-scanner (skip if skipSonarCheckForGo)
├─ stage: Docker Build → constructTemplate.renderTemplate(binding, 'go-Dockerfile')
└─ deployArgoCD.run(...)
buildNode.buildDckr(config):
env.TAG = getDockerParams.getTag(repo_name)
├─ npm install + npm run build
├─ stage: Sonar → sonar-scanner (node sonar/sonar-scanner.ts)
├─ stage: Docker Build → constructTemplate.renderTemplate(binding, 'node-Dockerfile')
└─ deployArgoCD.run(...)
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `run` | `src/com/meesho/stages/buildMaven.groovy:run` | Maven build lifecycle |
| `buildDckr` | `src/com/meesho/stages/buildGo.groovy:buildDckr` | Go build lifecycle |
| `buildDckr` | `src/com/meesho/stages/buildNode.groovy:buildDckr` | Node build lifecycle |
| `buildDckr` | `src/com/meesho/stages/buildPython.groovy:buildDckr` | Python build lifecycle |
| `run` | `src/com/meesho/stages/buildGradle.groovy:run` | Gradle build lifecycle |
| `buildDckr` | `src/com/meesho/stages/buildRust.groovy:buildDckr` | Rust build lifecycle |
| `retryDockerPush` | `src/com/meesho/utilities/dockerUtilities.groovy:retryDockerPush` | Docker push with retry |
| `getTag` | `src/com/meesho/utilities/getDockerParams.groovy:getTag` | Image tag: v{version}-{sha}-{epoch} |
## Sharp edges
- **`python-3.10.12` is aliased to `python-3.7` in Helm values**: `deployArgoCD.update_helm_repo` remaps `python-3.10.12``python-3.7` for the `dockerBuildVersion` key passed to the Helm template. Other python versions pass through as-is.
- **Maven multi-module builds**: `getDockerParams.getModules()` reads `<modules>` from `pom.xml`. If modules exist, a Docker image is built per module.
- **toolchain env Node builds** include `TOOLCHAIN_ENV` in the image path: `${cicd_environment}/${TOOLCHAIN_ENV}/${team}/${repo}`. Standard builds omit the `TOOLCHAIN_ENV` segment.
- **`skip_s3_check=true` forces a fresh build** even if an artifact already exists for the same commit.
## Related concepts
- [Build dispatch](build/build-dispatch.md) — regex routing to the correct builder
- [Config-only detection](build/config-only-detection.md) — when builds are skipped
- [Dockerfile templates](08-DOCKERFILE-TEMPLATES.md) — template rendering
- [Docker tagging](build/docker-tagging.md) — tag format used by all builders
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](06-CONFIG-POLICY.md) · [Index](../index.md) · [Next →](08-DOCKERFILE-TEMPLATES.md)
@@ -0,0 +1,84 @@
<!-- m-wiki: type=top-level slug=dockerfile-templates topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Dockerfile Templates
Dockerfile templates live in `resources/com/meesho/`. Each build stage calls `constructTemplate.renderTemplate()` to select and render the appropriate template with service-specific values.
## TL;DR
- Templates use Groovy's `SimpleTemplateEngine``${}` substitutions from a `Map binding`.
- `constructTemplate.renderTemplate(binding, templateFile, outputPath)` calls `libraryResource` to load the template, renders it, and writes the output file.
- Template selection is by `dockerBuildVersion` string: `maven-21`, `go-1.22`, `node-20`, etc.
- Output is always written to the workspace and then used by the Docker build step.
- `_construct()` is annotated `@NonCPS` because `SimpleTemplateEngine` is not CPS-serializable.
## Mental model
`constructTemplate` is a thin wrapper around Groovy's standard template engine. The binding map contains all deployment metadata (repo name, version tag, environment, JVM flags, etc.) assembled by the build stage and `enable_backward_compatibility()`. The template file itself is stored as a Jenkins library resource and loaded via `libraryResource`.
## Structure / data flow
```
Dockerfile template selection (in each build stage):
dockerBuildVersion = "go-1.22"
constructTemplate.renderTemplate(
binding = { repo_name, tag, env, ... },
templateFile = "go-1.22-Dockerfile", (or "go-Dockerfile" depending on version)
outputPath = "${WORKSPACE}/Dockerfile"
)
libraryResource "com/meesho/go-1.22-Dockerfile"
SimpleTemplateEngine.createTemplate(text).make(binding)
writeFile(Dockerfile) → docker build -f Dockerfile -t <registry>/<repo>:<tag> .
```
Available templates (from `resources/com/meesho/`):
```
maven-Dockerfile maven-21-Dockerfile
go-Dockerfile go-1.22-Dockerfile
node-Dockerfile node-18-Dockerfile node-20-Dockerfile
python-Dockerfile python-3.7-Dockerfile python-3.10-Dockerfile python-3.12-Dockerfile
rust-Dockerfile
php-Dockerfile
argoApp.yaml (ArgoCD Application manifest template)
values.yaml node-values.yaml go-values.yaml python-values.yaml
cron-values.yaml php-values.yaml
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `renderTemplate` | `src/com/meesho/utilities/constructTemplate.groovy:renderTemplate` | Loads library resource + renders + writes file |
| `_construct` | `src/com/meesho/utilities/constructTemplate.groovy:_construct` | `@NonCPS` template rendering via SimpleTemplateEngine |
| `get_value_yaml_file` | `src/com/meesho/stages/deployArgoCD.groovy:get_value_yaml_file` | Selects which values.yaml template to use for Helm |
| `get_default_command` | `src/com/meesho/stages/deployArgoCD.groovy:get_default_command` | Default container command per build version |
## Sharp edges
- **`_construct` is `@NonCPS`** (`src/com/meesho/utilities/constructTemplate.groovy:_construct`): this means it cannot access Jenkins pipeline steps (e.g., `echo`, `sh`) or `env.*` inside the method. All values must be in the `binding` map. Any caller that invokes `_construct` inside a `parallel` block or closure must ensure the closure itself is also `@NonCPS` or does not cross a serialisation boundary. Do not move `_construct` into a CPS context — keep the annotation and call it from a CPS-safe wrapper.
- **`binding` is copied defensively**: `_construct` wraps the incoming map in `new HashMap(binding)` before passing to the engine, preventing mutation of the caller's map.
- **`argoApp.yaml` is also a template**: the ArgoCD Application manifest is rendered the same way as Dockerfiles. This means ArgoCD app metadata (cluster, namespace, Helm chart path) is all driven by the deployment YAML binding.
- **Values templates select by `dockerBuildVersion`**: `get_value_yaml_file` maps `node-*``node-values.yaml`, `python-*``python-values.yaml`, etc. An unrecognized version returns `null` and the pipeline fails.
## Related concepts
- [Language builds](07-LANGUAGE-BUILDS.md) — which stage calls renderTemplate
- [Deploy ArgoCD](04-DEPLOY-ARGOCD.md) — uses renderTemplate for both ArgoCD app and Helm values
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](07-LANGUAGE-BUILDS.md) · [Index](../index.md) · [Next →](09-INFRA-PODS.md)
+81
View File
@@ -0,0 +1,81 @@
<!-- m-wiki: type=top-level slug=infra-pods topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Infra: GCP Jenkins Agent Pods
All GCP builds run inside a Jenkins Kubernetes pod provisioned from YAML specs in `resources/org/meesho/`. The pod YAML is selected by `env.INFRA_ENV` and loaded via `libraryResource` in `eksCICD.gcpInfra()`.
## TL;DR
- Pod specs live in `resources/org/meesho/*.yaml`.
- Filename pattern: `<INFRA_ENV>-pod.yaml` and `<INFRA_ENV>-sidecar-pod.yaml`.
- `INFRA_ENV` values seen in practice: `prd`, `stg`, `dev`, `toolchain`.
- All builds run in the `devops-tools` container defined in the pod spec.
- Sidecar pods (`useSidecar: true` in consumer Jenkinsfile) add extra containers alongside `devops-tools`.
- AWS builds skip this entirely — they run directly on static EKS nodes labeled `EKS`.
## Mental model
Jenkins provisions a fresh Kubernetes pod for each build. The pod spec defines:
- Which container images to run (typically `devops-tools` + optional sidecar).
- Resource requests/limits for the build container.
- Any mounted volumes (e.g., Docker socket for DinD builds).
`gcpInfra()` reads `env.INFRA_ENV` (injected by the Jenkins job configuration), constructs the pod YAML filename, loads it via `libraryResource`, and wraps the entire pipeline in a `podTemplate { node { container('devops-tools') { ... } } }` block.
## Structure / data flow
```
eksCICD.call(repo):
CLOUD_PROVIDER=GCP → gcpInfra(repo)
gcpInfra(repo):
isSidecarNeeded = repo.get('useSidecar', false)
yamlName = isSidecarNeeded ? "${INFRA_ENV}-sidecar-pod.yaml" : "${INFRA_ENV}-pod.yaml"
podyaml = "org/meesho/${yamlName}"
podTemplate(yaml: libraryResource(podyaml)) {
node(POD_LABEL) {
container('devops-tools') {
commonCICDFlow(repo)
}
}
}
Pod YAML inventory (resources/org/meesho/):
prd-pod.yaml prd-sidecar-pod.yaml
stg-pod.yaml stg-sidecar-pod.yaml
dev-pod.yaml dev-sidecar-pod.yaml
toolchain-pod.yaml toolchain-sidecar-pod.yaml
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `gcpInfra` | `vars/eksCICD.groovy:gcpInfra` | Selects pod YAML and wraps pipeline in podTemplate |
| `awsInfra` | `vars/eksCICD.groovy:awsInfra` | AWS path — no pod YAML, just node('EKS') |
| `run` | `src/com/meesho/utilities/nodePoolSelection.groovy:run` | Selects GKE node pool label for deployment (separate from build pod) |
## Sharp edges
- **Build pod vs deployment node pool are different things**: the Jenkins agent pod is where the build runs; `nodePoolSelection.run()` selects which GKE node pool the deployed *application* should land on. These are independent.
- **`INFRA_ENV` is job-level config**: it is not derived from the branch name. It is injected by the Jenkins multibranch pipeline configuration. A mis-configured job can run a `main` branch build with `INFRA_ENV=dev` — which would use the dev pod spec.
- **Toolchain pods are segregated**: `toolchain-pod.yaml` uses images from the `toolchain` namespace in `meesho-central-dev-0622` project — separate from standard build pods.
- **DinD (Docker-in-Docker) socket**: the `devops-tools` container in pod specs mounts a DinD service socket (`env.DOCKER_HOST`) rather than the host Docker socket. The `DOCKER_HOST` env var is set by `constructParam.run()` based on `cicd_environment`. The correct endpoint values are `dind-prd-svc` (prd) and `dind-dev-new-svc.jenkins-new.svc.cluster.local` (stg/ftr). Do not use `tcp://localhost:2375` — it creates conflicts that require unnecessary guard logic. The DinD image must come from the internal GAR registry (`asia-southeast1-docker.pkg.dev/meesho-devops-admin-0622/admin/devops/docker:28-dind`) — not Docker Hub.
- **`build-tools` image version encodes the Go compiler and sonar-scanner CLI**: `resources/org/meesho/prd-pod.yaml` and `stg-pod.yaml` reference a `build-tools` image that bundles Go, `sonar-scanner-cli`, and other toolchain binaries. When a new toolchain binary is needed, bump the image tag in both pod YAMLs — never `curl`/`wget` a tool from the internet inside a pipeline stage.
- **Non-prd node pools are BU-scoped**: `stg`/`ftr`/`dev` pods land on `{BU}-shared` pools, so all services in the same BU share one pool. A memory leak or noisy-neighbour in one `supply` service degrades all other `supply` services on staging.
## Related concepts
- [Node pool selection](infra/node-pool-selection.md) — GKE node pool assignment for deployed applications
- [Architecture](01-ARCHITECTURE.md) — where gcpInfra fits in the pipeline
- [Config policy](06-CONFIG-POLICY.md) — env.DOCKER_HOST set by constructParam
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](08-DOCKERFILE-TEMPLATES.md) · [Index](../index.md) · [Next →](10-NOTIFICATIONS.md)
+82
View File
@@ -0,0 +1,82 @@
<!-- m-wiki: type=top-level slug=notifications topic=null base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: top-level. 0 sources.
# Notifications
`notify.groovy` runs in the pipeline's `finally` block — it always executes regardless of build outcome. It routes to different backends based on who triggered the build and what environment the build ran in.
## TL;DR
- `ringmaster-bot` or `turbo-turtle` triggered → calls `deployRingmaster.run()` + Slack on `prd`.
- All other users → direct Slack `slackSend` only.
- Toolchain env → HTTP callback to `172.23.72.116:5002/api/v1/deploy/build-callback` (skip Slack/Ringmaster).
- On `prd` push builds, also posts to `deployment-tracker` API and Ringmaster deployment history.
- `notify_channel` comes from `config.yaml`; defaults to `ci-cd-status` if empty.
## Mental model
`notify.run()` is the final stage in `commonCICDFlow`. Since it's in a `finally` block, `env.msg` carries the final build status message (set by each catch block in `commonCICDFlow`). The notification backend selection depends on the triggering user, not on the environment:
- `ringmaster-bot` → Ringmaster API (`/api/v1/key/cicd/cd/update`) with a token-authenticated payload.
- Turbo-Turtle or other allowed users → Turbo-Turtle callback (`/api/v1/ci/jenkins/callback`) via a temporary JSON file to avoid shell quoting issues.
## Structure / data flow
```
notify.run(config):
├─ [toolchain] if env.INFRA_ENV == 'toolchain':
│ POST http://172.23.72.116:5002/api/v1/deploy/build-callback
│ return (skip everything below)
├─ build_user = getCause(UserIdCause).getUserId()
├─ if build_user in [ringmaster-bot, turbo-turtle]:
│ deployRingmaster.run(...) → POST to Ringmaster or Turbo-Turtle
│ if prd: slackSend(notify_channel, deploy URL)
│ skip_notify = true
├─ if !skip_notify:
│ slackSend(notify_channel, job name + build number + tag + msg)
└─ if main/master branch:
postTrackingApi(config) ← deployment-tracker API
postTrackingRingmasterApi(config) ← Ringmaster deployment history
deployRingmaster.run(repo_name, deployment_order, tag, ...):
if build_user == "ringmaster-bot":
POST https://ringmaster-api.meeshogcp.in/api/v1/key/cicd/cd/update
else:
POST http://turbo-turtle.meeshogcp.in/api/v1/ci/jenkins/callback
```
## Key code locations
| Symbol | File | What it does |
|--------|------|--------------|
| `run` | `src/com/meesho/stages/notify.groovy:run` | Main notify dispatcher |
| `postTrackingApi` | `src/com/meesho/stages/notify.groovy:postTrackingApi` | Posts to deployment-tracker API |
| `postTrackingRingmasterApi` | `src/com/meesho/stages/notify.groovy:postTrackingRingmasterApi` | Posts to Ringmaster deployment history |
| `run` | `src/com/meesho/stages/deployRingmaster.groovy:run` | Routes to Ringmaster or Turbo-Turtle callback |
| `callApi` | `src/com/meesho/stages/deployRingmaster.groovy:callApi` | Ringmaster API call with `ringmaster-token` credential |
## Sharp edges
- **JSON payload via temp file** (Turbo-Turtle path): the Turbo-Turtle curl call writes the payload to a temp file (`cicd_payload_${BUILD_NUMBER}_${ts}.json`) to avoid shell quoting issues with JSON special characters. The file is always deleted in a `finally` block.
- **`prd` vs `int` API endpoints**: for both Ringmaster and deployment-tracker, `prd` and `int` share the `ringmaster-api.meeshogcp.in` endpoint while `stg`/`ftr` use `ringmaster-api.admin.meeshogcp.in`.
- **`skip_notify` is `env.*` not `config.*`**: it's set by `constructParam.run()` (always `true` for GCP) and can also be set by the consumer `config.yaml`. Both must be false for Slack to fire.
- **`env.msg` is the error channel**: each `catch` block in `commonCICDFlow` sets `env.msg` before the `finally` block calls `notify`. If nothing failed, `env.msg` stays `'Job Passed'`.
## Related concepts
- [Ringmaster integration](deploy/ringmaster-integration.md) — detailed routing logic
- [Architecture](01-ARCHITECTURE.md) — where notify fits in commonCICDFlow
- [Environment mapping](05-ENVIRONMENT-MAPPING.md) — cicd_environment determines API base URL
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Previous](09-INFRA-PODS.md) · [Index](../index.md) · [Next →](10-NOTIFICATIONS.md)
+55
View File
@@ -0,0 +1,55 @@
<!-- m-wiki: type=concept slug=adr-index topic=adr base-sha=d6708eca4236 generated-at=2026-05-12T12:00:00+00:00 sources=[docs/adr/README.md, docs/adr/0001-single-shared-library-for-all-services.md, docs/adr/0002-branch-name-as-sole-environment-selector.md, docs/adr/0003-policy-exceptions-in-separate-whitelist-repo.md, docs/adr/0004-fresh-whitelist-clone-per-build.md, docs/adr/0005-config-only-change-detection-skip-build.md, docs/adr/0006-ringmaster-mandatory-build-trigger-gate.md, docs/adr/0007-gitops-via-argocd-4-step-sync-sequence.md, docs/adr/0008-canary-mandatory-for-tier1-services-in-prd.md, docs/adr/0009-jvm-heap-auto-derived-from-pod-memory-request.md] -->
> Generated 2026-05-12 at base-sha d6708eca4236. Type: concept. 10 sources.
# Architecture Decision Records — Index
devops-lib's ADRs capture the "why" behind the library's design — the decisions that would otherwise be tribal knowledge. All 9 ADRs were retroactively documented on 2026-05-12 from codebase analysis and developer interviews. All are **Status: Accepted** and assessed as still appropriate.
## Why ADRs matter here
devops-lib has several decisions whose rationale is non-obvious: why does the whitelist get re-cloned on every build (not cached)? Why is the 4-step ArgoCD sequence non-interchangeable? Why must all builds go through Ringmaster? Without ADRs, engineers modifying the library break load-bearing constraints without realising it.
## Decision inventory
| # | Decision | Category | Key insight |
|---|----------|----------|-------------|
| [ADR-0001](../../../adr/0001-single-shared-library-for-all-services.md) | Single shared library for all services | PATTERN | Policy enforcement must be uniform; per-team Jenkinsfiles produce drift |
| [ADR-0002](../../../adr/0002-branch-name-as-sole-environment-selector.md) | Branch name as sole environment selector | PATTERN | Branch IS the environment contract; per-service env config creates misconfiguration risk |
| [ADR-0003](../../../adr/0003-policy-exceptions-in-separate-whitelist-repo.md) | Policy exceptions in Meesho/whitelists repo | PATTERN | Service teams cannot self-grant bypasses; all exceptions require DevOps review |
| [ADR-0004](../../../adr/0004-fresh-whitelist-clone-per-build.md) | Fresh whitelist clone per build | PATTERN | Policy changes must take effect on the very next build without a library release |
| [ADR-0005](../../../adr/0005-config-only-change-detection-skip-build.md) | Config-only change detection — skip build | PATTERN | No source change → no new binary; reuse the latest image tag from GAR |
| [ADR-0006](../../../adr/0006-ringmaster-mandatory-build-trigger-gate.md) | Ringmaster mandatory trigger gate | PATTERN | Every deployment must be tracked; direct Jenkins triggers bypass Ringmaster's ledger |
| [ADR-0007](../../../adr/0007-gitops-via-argocd-4-step-sync-sequence.md) | GitOps via 4-step ArgoCD sync sequence | INFRA | Steps 2 and 4 are non-interchangeable; swapping them causes silent sync failures |
| [ADR-0008](../../../adr/0008-canary-mandatory-for-tier1-services-in-prd.md) | Canary mandatory for Tier-1 (sp0/up0) in prd | RELIABILITY | Incident-driven: non-canary prd deploys for critical services caused outages |
| [ADR-0009](../../../adr/0009-jvm-heap-auto-derived-from-pod-memory-request.md) | JVM heap auto-derived from pod memory_request | RELIABILITY | OOM incident remediation: auto-calc prevents under-sizing; `jvm_memory_override: true` escapes it |
## Decisions that are load-bearing constraints
These three decisions have "never change without understanding this" consequences:
**ADR-0004 (fresh whitelist clone)**`getWhitelistedRepos` in `src/com/meesho/utilities/constructParam.groovy:getWhitelistedRepos` re-clones `Meesho/whitelists` on every call by design. Never add a cache — it would prevent immediate policy enforcement.
**ADR-0006 (Ringmaster gate)** — The string `"ringmaster-bot"` at `vars/eksCICD.groovy:call` is the sole routing signal between Ringmaster and Turbo-Turtle callbacks. Never rename it without coordinating with both teams.
**ADR-0007 (4-step ArgoCD sequence)** — Steps 2 (`refresh_app_of_apps`) and 4 (`refresh_and_sync`) in `src/com/meesho/stages/deployArgoCD.groovy:run` are non-interchangeable. For a first-deploy, step 2 must create the Application object before step 4 can sync it.
## When to read which ADR
- **Debugging a build not triggering**: ADR-0006 (Ringmaster gate)
- **Debugging a deploy that failed on ArgoCD sync**: ADR-0007 (4-step sequence)
- **Understanding why policy changes take effect immediately**: ADR-0004 (fresh clone)
- **Service team asking for a sonar bypass**: ADR-0003 (whitelist repo)
- **Canary enforcement failing for a prd deploy**: ADR-0008
- **OOM in a Java service pod**: ADR-0009
## Related concepts
- [Whitelist system](../policy/whitelist-system.md) — runtime whitelist enforcement (ADR-0003, ADR-0004)
- [ArgoCD sync](../deploy/argocd-sync.md) — step-by-step walkthrough (ADR-0007)
- [Ringmaster integration](../deploy/ringmaster-integration.md) — callback flow (ADR-0006)
- [Environment mapping](../05-ENVIRONMENT-MAPPING.md) — branch → environment (ADR-0002)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
+53
View File
@@ -0,0 +1,53 @@
<!-- m-wiki: type=concept slug=build-dispatch topic=build base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Build Dispatch
`buildObjHelper.run(build_tool)` maps a `build_tool` string from `config.yaml` to a concrete builder class instance using a Groovy `switch/case` with regex patterns.
## Where it applies in this repo
`src/com/meesho/stages/buildObjHelper.groovy:run`
The full dispatch table (in order, first match wins):
| Pattern | Builder class |
|---------|--------------|
| `maven` (exact) | `buildMaven` |
| `docker` (exact) | `buildDocker` |
| `~/^maven-.*/` | `buildMaven` |
| `~/^python-.*/` | `buildPython` |
| `~/^node-.*/` | `buildNode` |
| `~/^rust.*/` | `buildRust` |
| `~/^go.*/` | `buildGo` |
| `gradle` (exact) | `buildGradle` |
| `php` (exact) | `buildPhp` |
| default | `defaultBuild()` |
Each builder is instantiated fresh per build — no shared state between builds.
## Why this design
Groovy `switch/case` evaluates patterns top-to-bottom and returns on the first match. Regex patterns (the `~/…/` syntax) cover version-suffixed variants like `go-1.22`, `node-20`, `python-3.12` without requiring an exhaustive case list. The exact-match cases for `maven` and `docker` appear before the regex catch-all `~/^maven-.*/` to handle the legacy bare-string case.
If no case matches, `defaultBuild()` is called without logging a warning — the pipeline reports success with no artifact produced. A typo in `config.yaml` (e.g. `golang` instead of `go`) produces this silent no-op. If a build succeeds but produces no Docker image, check `build_tool` spelling in `config.yaml` first.
## Related
- [Build stages](../03-BUILD-STAGES.md) — broader build lifecycle
- [Language builds](../07-LANGUAGE-BUILDS.md) — per-language builder details
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,46 @@
<!-- m-wiki: type=concept slug=config-only-detection topic=build base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Config-Only Change Detection
When a commit only changes YAML config files (no source code), each language builder skips the compile/test/image-build steps and instead fetches the last-built image tag from GAR (Google Artifact Registry). This avoids rebuilding identical binaries when only configs changed.
## Where it applies in this repo
Implemented independently in each language builder (`src/com/meesho/stages/buildMaven.groovy`, `buildGo.groovy`, `buildNode.groovy`, etc.).
The detection pattern varies slightly by builder but follows the same logic:
1. Run `git diff HEAD~1 -- configs/` (or `git diff ORIG_HEAD -- configs/`).
2. If the diff is non-empty AND no source files changed → set `appConfigChanges = true`, skip compilation.
3. Fetch the latest image tag from the artifact bucket (S3 or GCS) or GAR.
4. Set `env.TAG` to the fetched tag.
5. Proceed directly to `deployArgoCD.run()`.
The check is gated by `skip_s3_check` in `config.yaml`. Setting `skip_s3_check: true` forces a full rebuild even when only configs changed.
## Why this design
Config-only deployments are common at Meesho (dynamic config updates, feature flags). Rebuilding the entire Java or Go binary for a one-line YAML change wastes 310 minutes. By reusing the last image tag and skipping straight to ArgoCD deploy, the pipeline completes in ~1 minute for config-only changes.
The detection relies on `git diff` against the previous commit, so it only works when the commit history is linear. Force-pushes or squash merges may produce false negatives (full rebuild triggered unnecessarily).
## Related
- [Language builds](../07-LANGUAGE-BUILDS.md) — where this check is embedded per builder
- [Build dispatch](build-dispatch.md) — the builder instance that contains this check
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
+57
View File
@@ -0,0 +1,57 @@
<!-- m-wiki: type=concept slug=docker-tagging topic=build base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Docker Tagging
`getDockerParams.getTag()` produces the Docker image tag for each build. The tag format encodes the version, commit SHA, and a timestamp to guarantee uniqueness across rebuilds of the same commit.
## Where it applies in this repo
`src/com/meesho/utilities/getDockerParams.groovy:getTag`
**Standard builds:**
```
v{pom-version or package-version}-{7-char-sha}-{epoch-ms}
```
Example: `v1.4.2-a3f8c21-1714920000000`
**Toolchain builds** (when `env.INFRA_ENV == 'toolchain'`):
```
v{version}-{7-char-sha}
```
Example: `v1.4.2-a3f8c21`
The short SHA comes from `git log -1 --format=%h` (7 chars). The full 40-char SHA is also captured in `env.commit_id` as a side effect of `getCommitid()`.
Version is read from:
- `pom.xml``xq -r .project.version pom.xml`
- `package.json``jq -r .version package.json`
- Default: `1.0`
**Hotfix Maven builds** override the tag format to `v{version}-HOT` (set directly in `buildMaven.run()`, not via `getTag`).
## Why this design
The epoch-ms suffix ensures that two builds from the exact same commit produce different tags. This is intentional: if a build fails mid-way and is retried, the retry must produce a new image (the previous one may be partially pushed or broken). Without the timestamp, `docker push` on a retry would be a no-op if the tag already exists.
Toolchain builds omit the timestamp because toolchain images are content-addressed: the same source commit must always produce the same tag so toolchain consumers can pin to a stable reference without tracking timestamps.
## Related
- [Language builds](../07-LANGUAGE-BUILDS.md) — each builder sets `env.TAG` using getTag
- [Notifications](../10-NOTIFICATIONS.md) — `env.TAG` is included in Ringmaster and Slack payloads
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,49 @@
<!-- m-wiki: type=concept slug=node-paired-files topic=build base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Node Build: Paired File Rule
`src/com/meesho/stages/buildNode.groovy` and `resources/com/meesho/node-Dockerfile` are paired files. Changes to Node install logic in one must be mirrored in the other to avoid a split-brain build path.
## Where it applies in this repo
`src/com/meesho/stages/buildNode.groovy:buildDckr`
`resources/com/meesho/node-Dockerfile`
## How they interact
`buildNode.groovy` detects the package manager at runtime (npm vs pnpm vs yarn) and passes the install command to the Dockerfile as the `npm_install_arg` template variable. The Dockerfile's `else` branch handles the fallback when no explicit or detected command is provided (currently `npm ci`).
```
buildNode.buildDckr(config):
├─ Detect package manager → npm_install_arg = "pnpm install" | "npm ci" | ...
├─ constructTemplate.renderTemplate(binding, 'node-Dockerfile')
│ └─ binding.npm_install_arg → substituted into node-Dockerfile
└─ docker build -f Dockerfile ...
```
When `npm_install_arg` is not set in `config.yaml`, `buildNode.groovy` falls back to its own detection logic. The Dockerfile default branch handles the case where detection produces nothing.
## Why this matters
If `buildNode.groovy` changes the fallback install command or adds support for a new package manager, the Dockerfile default branch must be updated in the same PR. Changing only one file leaves them out of sync: the runtime path may succeed while the Docker fallback uses the old command (or vice versa). This creates subtly different images depending on whether `npm_install_arg` is explicitly configured.
## Related
- [Language builds](../07-LANGUAGE-BUILDS.md) — Node builder overview
- [Dockerfile templates](../08-DOCKERFILE-TEMPLATES.md) — how renderTemplate works
## Sources
(no raw/ sources)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,54 @@
<!-- m-wiki: type=concept slug=scm-variable-scope topic=build base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# SCM Variable Scope
The `scm` variable is only available inside consumer Jenkinsfiles, not inside `vars/` or `src/` of the shared library. Referencing it in library code causes a runtime `MissingPropertyException`.
## Where it applies in this repo
`vars/eksCICD.groovy` and all files under `src/com/meesho/`
## Why `scm` is unavailable
The `scm` variable (branch, remote URL, credentials) is injected by the GitHub Branch Source plugin into **consumer Jenkinsfiles** at the time they are loaded by Jenkins. Library code (everything under `vars/` and `src/`) is a separate classloader context — the plugin does not inject `scm` there.
Attempting to access `scm.branches` or `scm.userRemoteConfigs` from library code will throw:
```
MissingPropertyException: No such property: scm for class: groovy.lang.Binding
```
## Correct alternatives
Inside library code, use the Jenkins-injected environment variables instead:
| Need | Use instead of `scm.*` |
|------|------------------------|
| Branch name | `env.BRANCH_NAME` |
| Repository URL | `env.GIT_URL` |
| PR number | `env.CHANGE_ID` |
| PR source branch | `env.CHANGE_BRANCH` |
| PR target branch | `env.CHANGE_TARGET` |
All of these are set by the GitHub Branch Source plugin before library code runs.
## Related
- [Entry points](../02-ENTRYPOINTS.md) — where this constraint applies
- [Environment mapping](../05-ENVIRONMENT-MAPPING.md) — `env.CHANGE_ID` is the PR-build detector
## Sources
(no raw/ sources)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
+55
View File
@@ -0,0 +1,55 @@
<!-- m-wiki: type=concept slug=observability topic=concepts base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:vars/log.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: concept. 1 source.
# Logging via `log.groovy`
[`vars/log.groovy`](../../../../vars/log.groovy) is the closest thing this codebase has to a logger. It's a 24-line Groovy file that wraps `echo` with ANSI colour codes.
## The three calls
```groovy
log.info(msg) // → echo "${GREEN}INFO: ${msg}${BLACK}"
log.warning(msg) // → echo "${RED}WARNING: ${msg}${BLACK}"
log.error(msg) // → echo "${RED}ERROR: ${msg}${BLACK}"
```
That's the whole API. There is no severity level configuration, no structured fields (no JSON), no destination other than the Jenkins console.
## What `log.info` gives you over bare `echo`
- **ANSI colour** — pipeline output in the Jenkins UI is easier to scan when INFO is green and ERROR is red.
- **Consistent prefix** (`INFO: ` / `WARNING: ` / `ERROR: `) — a `grep ERROR pipeline.log` pattern works across every service.
- **Nothing else.** No timestamps (Jenkins adds those via `timestamps` wrapper), no caller tracing, no correlation ID.
## When `bare echo` is acceptable
Lots of existing code uses bare `echo`. The mix is mostly historical. New code should prefer `log.info` for normal messages and `log.error` for failures, but you'll see bare `echo` in:
- One-off banners (`echo "=========="`).
- Single-line status (`echo "Building module: ${m}"` inside a loop).
- ANSI-coloured ad-hoc messages where the code path needs a one-off colour (some `vars/eksCICD.groovy` and `vars/buildPipeline.groovy` lines do this).
There is no policy that bans bare `echo`. The reconcile run's `BUGS_AND_IMPROVEMENTS_REPORT.md` does list "inconsistent logging" as a P1 item but no rule has been graduated into `CLAUDE.md` NEVER DO yet.
## `env.msg` as failure state
A separate convention: most stages set `env.msg` to a human-readable failure reason before re-throwing, e.g. [`buildNode.groovy:21`](../../../../src/com/meesho/stages/buildNode.groovy):
```groovy
env.msg = 'Error in building node packages...'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
```
`notify.groovy` then reads `env.msg` (and the related `env.error_msg_to_db`) when composing the Slack message and the deployment-tracker payload. **Don't rename `env.msg`** — too many callers read it. [`deployRingmaster.groovy`](../../../../src/com/meesho/stages/deployRingmaster.groovy) catch blocks are inconsistent here: some set `env.msg`, others don't — flagged in `BUGS_AND_IMPROVEMENTS_REPORT.md` as standardisation work.
## What this codebase doesn't have
- No structured logging (no JSON, no key-value pairs, no trace IDs).
- No log level filtering — every `log.info` always prints.
- No log forwarding to anything other than the Jenkins console.
- No "audit log" stream separate from the pipeline log.
If you need to instrument a pipeline run for external observability, the current convention is to POST to a downstream system directly (see how `notify.groovy` calls the Deployment Tracker at lines 108-152). There is no shared metric/event emitter.
@@ -0,0 +1,51 @@
<!-- m-wiki: type=concept slug=secrets-and-auth topic=concepts base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:src/com/meesho/utilities/addSSHKey.groovy, code:src/com/meesho/stages/buildNode.groovy, code:src/com/meesho/stages/buildGo.groovy, code:src/com/meesho/stages/deployArgoCD.groovy, code:src/com/meesho/stages/securityScan.groovy, code:src/com/meesho/stages/notify.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: concept. 6 sources.
# Secrets, SSH keys, and downstream auth
How the pipeline authenticates to every external system, in one place. Every entry below comes from a real `withCredentials { ... }` block or env var read in the current code.
## Downstreams + auth mechanisms
| Downstream | Credential / mechanism | Code site |
|---|---|---|
| GitHub clone/push (HTTPS) | `env.GITHUB_CRED = 'svc-devops-meesho'` (`gitUsernamePassword`) | [`gitActions.groovy:13`](../../../../src/com/meesho/utilities/gitActions.groovy), [`buildGo.groovy:300`](../../../../src/com/meesho/stages/buildGo.groovy), [`buildNode.groovy:413`](../../../../src/com/meesho/stages/buildNode.groovy) |
| GitHub clone (SSH, private repos) | `credentialsId: 'ssh-private-key'` written to `./id_github_jenkins` (0600) | [`addSSHKey.groovy:4-5`](../../../../src/com/meesho/utilities/addSSHKey.groovy) |
| Vault (GCP secrets) | `env.vaultToken` string | [`buildNode.groovy:555`](../../../../src/com/meesho/stages/buildNode.groovy), [`constructParam.groovy:170,175`](../../../../src/com/meesho/utilities/constructParam.groovy) (`vault-prd.meeshogcp.in`, `vault-dev.meeshogcp.in`) |
| JFrog (Maven deploy) | `-DuseProdRepo=true` / `-DuseTestRepo=true` Maven profile | [`onlyPushtoJfrog.groovy:43-47`](../../../../vars/onlyPushtoJfrog.groovy) |
| GCP Docker registry | `gcloud auth configure-docker` (SDK ambient auth) | [`buildGo.groovy:126`](../../../../src/com/meesho/stages/buildGo.groovy), [`buildNode.groovy:374`](../../../../src/com/meesho/stages/buildNode.groovy) |
| AWS ECR | `aws ecr get-login-password ... \| docker login --password-stdin` | [`buildNode.groovy:371`](../../../../src/com/meesho/stages/buildNode.groovy), [`buildPython.groovy:93`](../../../../src/com/meesho/stages/buildPython.groovy), [`buildMaven.groovy:566`](../../../../src/com/meesho/stages/buildMaven.groovy), [`buildGradle.groovy:520`](../../../../src/com/meesho/stages/buildGradle.groovy), [`buildPhp.groovy:59`](../../../../src/com/meesho/stages/buildPhp.groovy) |
| ArgoCD | `env.argoCreds` (`usernamePassword`); `argocd login ${env.argoURL}:443` | [`deployArgoCD.groovy:490, 522`](../../../../src/com/meesho/stages/deployArgoCD.groovy) |
| npm registry | `.npmrc` from AWS Secrets Manager or Vault → written to workspace | [`buildNode.groovy:63-68, 337`](../../../../src/com/meesho/stages/buildNode.groovy) |
| SonarQube | `env.sonarToken` string; `withSonarQubeEnv { ... }` against `sonarqube-prd` | [`buildMaven.groovy:245-251`](../../../../src/com/meesho/stages/buildMaven.groovy), [`buildNode.groovy:406`](../../../../src/com/meesho/stages/buildNode.groovy) |
| Ringmaster | `credentialsId: 'ringmaster-token'` (`usernamePassword`) | [`deployRingmaster.groovy:115`](../../../../src/com/meesho/stages/deployRingmaster.groovy), [`notify.groovy:117`](../../../../src/com/meesho/stages/notify.groovy) |
## The SSH key write path
[`addSSHKey.groovy:3-7`](../../../../src/com/meesho/utilities/addSSHKey.groovy) writes the credential file inside `withCredentials { ... }`:
```groovy
withCredentials([sshUserPrivateKey(credentialsId: 'ssh-private-key', keyFileVariable: 'FILE')]) {
sh "cat ${FILE} > ./id_github_jenkins; chmod 600 ./id_github_jenkins; ..."
}
```
There is a **race window** between the `cat` write and the `chmod` — a co-resident process could read the file with default umask permissions for that brief interval. There is also **no cleanup** of `./id_github_jenkins` after use. Both are flagged in `BUGS_AND_IMPROVEMENTS_REPORT.md`.
The key file is **NOT cat'd to stdout / logs** — earlier PR-review concerns (PR #634) about that pattern have been remediated; the current `cat ${FILE} > ./id_github_jenkins` is a file write, not a print. See [`review-learnings.md`](../../../../review-learnings.md) for the historical trail.
## NEVER DO
- **Never print or `cat` an SSH private key to stdout / logs.** Always go through `withCredentials` + a 0600 file. (Graduated rule — see [`CLAUDE.md`](../../../../CLAUDE.md) NEVER DO.)
- **Never hard-code bare IPs as curl/HTTP targets.** Use DNS hostnames. Known existing violation: [`securityScan.groovy:11`](../../../../src/com/meesho/stages/securityScan.groovy) — `final String url = '172.31.5.29:63232/scans'`. Flagged for remediation; do not add new violations.
- **Never pass passwords on the command line** where they'll appear in `ps`. ArgoCD's login at [`deployArgoCD.groovy:494, 525`](../../../../src/com/meesho/stages/deployArgoCD.groovy) does pass `--password ${ARGO_PASSWORD}` on argv — also flagged.
## Where the secrets actually live
| System | Where the credential is provisioned |
|---|---|
| Jenkins credential store | `svc-devops-meesho`, `ssh-private-key`, `argoCreds`, `ringmaster-token`, `sonarToken`, `vaultToken` |
| Vault (`vault-prd.meeshogcp.in` / `vault-dev.meeshogcp.in`) | runtime service secrets, `MEESHO_NPMRC_SECRET` |
| AWS Secrets Manager | `MEESHO_NPMRC_SECRET` (alternate fetch path) |
| GCP IAM service accounts | Docker registry, GKE access — via ambient `gcloud auth` |
+31
View File
@@ -0,0 +1,31 @@
<!-- m-wiki: type=concept slug=whitelists topic=concepts base-sha=5399a5ddc36b generated-at=2026-05-21 sources=[code:src/com/meesho/utilities/constructParam.groovy] -->
> Generated 2026-05-21 at base-sha 5399a5ddc36b. Type: concept. 1 source.
# The five whitelist gates
[`constructParam.groovy`](../../../../src/com/meesho/utilities/constructParam.groovy) runs five independent whitelist checks against `Meesho/whitelists`. Each one does a **fresh `git clone`** — there is no caching. A build that hits all five clones the whitelist repo five times.
## The five gates
| Gate | What it controls | Function | Read at |
|---|---|---|---|
| `skip-sonar-whitelist` | Blocks `skip_sonar=true` for Maven on prd unless repo is allowlisted | `skipSonarCheckForbidden` | [`constructParam.groovy:40-57`](../../../../src/com/meesho/utilities/constructParam.groovy) |
| `app-config-disabled` | Blocks `appConfig=false` on stg for Maven/Gradle unless allowlisted | `appConfigDisabledForbidden` | [`constructParam.groovy:62-72`](../../../../src/com/meesho/utilities/constructParam.groovy) |
| `multizone-enabled-repos` | Gates the multi-zone deploy path | `isMultizoneEnabled` | [`constructParam.groovy:29-35`](../../../../src/com/meesho/utilities/constructParam.groovy) |
| `allowedNonDevelopPrDeploymentToInt` | Allows non-`develop` PRs to deploy to `int` | `allowedNonDevelopPrDeploymentToIntRepos` | [`constructParam.groovy:77-83`](../../../../src/com/meesho/utilities/constructParam.groovy) |
| `ValidateCacConfig` | Gates CAC validation on PR build | `ValidateCacConfigForRepo` | [`constructParam.groovy:88-95`](../../../../src/com/meesho/utilities/constructParam.groovy) |
## Why fresh-clone every time
This is **intentional**. The whitelist is the live, authoritative source of which repos opt out of which check. By re-cloning on every call, a DevOps change to the whitelist takes effect on the **next** build in the org without needing a devops-lib release. The cost is ~5× clone latency under GitHub rate-limiting; the benefit is zero release coordination.
## Do NOT add caching
The single most tempting refactor in this code is to cache the clone across the five calls in a single build. Don't — the freshness guarantee is the load-bearing property. See [`docs/tribal-knowledge.md`](../../../tribal-knowledge.md) §1. If you must improve clone performance, do it inside the clone itself (shallow clone, single-branch fetch) without touching the per-call invocation pattern.
## Where the whitelist lives
`https://github.com/Meesho/whitelists.git` (cloned via [`gitActions.groovy`](../../../../src/com/meesho/utilities/gitActions.groovy) helpers). The repo contains one YAML per whitelist name, e.g. `skip-sonar-whitelist.yaml`, `multizone-enabled-repos.yaml`. Each is a flat list of repo names.
To add a repo to a whitelist: open a PR on `Meesho/whitelists`, get a DevOps reviewer to approve, merge. The next pipeline run picks up the change automatically.
+58
View File
@@ -0,0 +1,58 @@
<!-- m-wiki: type=concept slug=argocd-sync topic=deploy base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# ArgoCD Sync Sequence
The 4-step ArgoCD deploy sequence in `deployArgoCD.groovy` uses Git as the API: each step commits to a config repo, opens a PR, merges it, and then triggers ArgoCD to pick up the change.
## Where it applies in this repo
`src/com/meesho/stages/deployArgoCD.groovy`
**Step 1 — `update_argo_repo`**
- Reads `deployment.yaml` for the deployable.
- Renders `argoApp.yaml` template → `devops-argo-config/applications_v2/<cluster>/<team>-<app>.yaml`.
- Commits + opens PR against `argoBranch` + merges + deletes branch.
- Sets `value_binding1['helm_values_path']` so the ArgoCD app knows where to find Helm values.
**Step 2 — `refresh_app_of_apps`**
- Runs `argocd app sync <argoIncubator>` (the incubator app-of-apps).
- This causes ArgoCD to discover the new/updated Application manifest from Step 1.
- Uses `--http-retry-max 3 --retry-backoff-duration 1m`.
**Step 3 — `update_helm_repo`**
- Reads `deployment.yaml` + `values_properties.yaml` from `devops-helm-charts`.
- Renders `values.yaml` (or `node-values.yaml`, `go-values.yaml`, etc.) template.
- Enforces canary for sp0/up0 prd services: `enabled=true`, `skipAnalysis=false`, `enableManualPromotion=true`.
- Commits + opens PR against `helmBranch` + merges + deletes branch.
**Step 4 — `refresh_and_sync`**
- `argocd app get --hard-refresh <env>-<app_name>` — forces ArgoCD to re-read Helm values from Git.
- `argocd app sync <env>-<app_name>` — triggers rollout.
- Returns exit code 0 on success; any non-zero result fails the build.
## Why this design
Using Git PRs as the deployment mechanism means every config change is auditable in GitHub history. ArgoCD polls its source repos on a configurable interval, but `hard-refresh` forces an immediate re-read instead of waiting for the poll cycle. The app-of-apps pattern allows ArgoCD to manage thousands of Application resources without manual registration.
Steps 2 (`refresh_app_of_apps`) and 4 (`refresh_and_sync`) are **not interchangeable**. For a first-deploy service, step 2 must run before step 4: without step 2, the ArgoCD Application object created in step 1 hasn't been discovered yet, and step 4 will target a non-existent app. The four-step order is load-bearing.
## Related
- [Deploy ArgoCD](../04-DEPLOY-ARGOCD.md) — broader deploy lifecycle including user input and canary enforcement
- [Dockerfile templates](../08-DOCKERFILE-TEMPLATES.md) — renderTemplate used in both argo and helm steps
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,56 @@
<!-- m-wiki: type=concept slug=ringmaster-integration topic=deploy base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Ringmaster / Turbo-Turtle Integration
`deployRingmaster.run()` routes the CI/CD status callback to either Ringmaster or Turbo-Turtle based on which user triggered the build. These are two separate internal systems that track build and deployment state.
## Where it applies in this repo
`src/com/meesho/stages/deployRingmaster.groovy:run`
**Routing decision:**
```
build_user = getCause(UserIdCause).getUserId()
if build_user == "ringmaster-bot":
POST https://ringmaster-api.meeshogcp.in/api/v1/key/cicd/cd/update
(with Authorization: <ringmaster-token> header)
payload: hot_fix, job_name, build_no, image, applications, job_status, etc.
else (turbo-turtle, or other allowed user):
POST http://turbo-turtle.meeshogcp.in/api/v1/ci/jenkins/callback
payload: repo_name, source_branch, pull_request_number, env, job_name,
sub_job_name, build_number, image_tag, build_detailed_error
```
For `prd`/`int` environments, the base URL is the production Ringmaster API; for `stg`/`ftr`, it uses the admin endpoint.
The Turbo-Turtle payload is written to a temp file first to avoid shell escaping issues with JSON special characters, then passed to `curl -d @<file>`. The temp file is always deleted in a `finally` block.
## Why this design
Ringmaster is the primary orchestration plane for production deployments triggered by human operators via its UI. Turbo-Turtle is the automated CI/CD bot that validates and triggers deployments from PRs. Both need to know when a Jenkins build completes so they can update their state machines.
The `ringmaster-bot` user identity is the distinguishing signal: builds triggered from Ringmaster's UI arrive in Jenkins with that user ID, while Turbo-Turtle-triggered builds arrive with the `turbo-turtle` user ID. The string `"ringmaster-bot"` is load-bearing — if Ringmaster ever renames its bot user, callbacks silently fall through to the Turbo-Turtle endpoint. Never change this string without coordinating with the Ringmaster team.
The Turbo-Turtle JSON payload is written to a temp file (`cicd_payload_${BUILD_NUMBER}_${ts}.json`) to avoid shell escaping failures when payload fields contain single quotes, slashes, or error messages with special characters. The temp file is deleted in a `finally` block.
## Related
- [Notifications](../10-NOTIFICATIONS.md) — notify.groovy calls deployRingmaster
- [Architecture](../01-ARCHITECTURE.md) — allowedUsers list that includes both bot users
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,54 @@
<!-- m-wiki: type=concept slug=node-pool-selection topic=infra base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# GKE Node Pool Selection
`nodePoolSelection.run(memory_request, cpu_request, priority_v2)` computes the GKE node pool label for the deployed application (not the Jenkins build agent). The label is written to the Helm values as `nodeSelectorValue`.
## Where it applies in this repo
`src/com/meesho/utilities/nodePoolSelection.groovy:run`
**Selection logic (prd only):**
1. Parse `memory_request` (Mi or Gi) and `cpu_request` (m or whole cores) to numeric values.
2. Compute `ratio = memory_mb / cpu_millicores`.
3. For **non-critical priorities** (cp1-cp3, up1-up3, sp1-sp3): node name is `{mega|sumo}{tetra|duo}lite`.
- `cpu_req >= 2200m``sumo`; else `mega`.
- `ratio >= 2.5``tetra`; else `duo`.
4. For **critical priorities** (sp0, up0, cp0 — not explicitly listed but implied): node name is `{compact|mega|sumo}{duo|tetra|octa}`.
- `cpu_req >= 2200m``sumo`; `1000m-2199m``mega`; `<1000m``compact`.
- `ratio > 5.5``octa`; `>= 2.5``tetra`; else `duo`.
5. BU-specific overrides for `supply` and `demand` apply to certain `{nodevalue, ratiovalue}` combinations.
**Non-prd environments:**
- `int``preprod-cost-optimized`
- `stg`/`ftr`/`dev``{BU}-shared`
The result is set as `value_binding1['nodeSelectorValue']` in `deployArgoCD.update_helm_repo`.
## Why this design
GKE node pools are heterogeneous — some are memory-optimized, some CPU-optimized. Placing services on the wrong pool wastes resources or causes throttling. The automated selection reduces per-team cognitive load: teams declare their resource needs in `deployment.yaml` and the pipeline finds the best matching pool.
The BU-specific overrides exist because certain BUs have limited availability of some pool types in the region — the fallback to `tetra` avoids scheduling failures on unavailable pools.
## Related
- [Deploy ArgoCD](../04-DEPLOY-ARGOCD.md) — nodeSelectorValue is computed in update_helm_repo
- [Infra pods](../09-INFRA-PODS.md) — build agent pod selection (separate from app node pool)
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
+49
View File
@@ -0,0 +1,49 @@
<!-- m-wiki: type=concept slug=cac-validation topic=policy base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# CAC (Config-as-Code) Validation
CAC validation runs `resources/com/meesho/validate_configs_v2.py` on PR builds for repos opted in via the `ValidateCacConfig` whitelist. It validates application YAML configs against a schema to catch misconfigurations before they reach staging.
## Where it applies in this repo
`src/com/meesho/utilities/constructParam.groovy:ValidateCacConfigForRepo`
`resources/com/meesho/validate_configs_v2.py`
**Trigger condition:**
- Build is a PR build (`env.CHANGE_ID` is set).
- `ValidateCacConfigForRepo(config.ValidateConfig, repo_name)` returns `true`.
- Either the repo is in the `ValidateCacConfig.yaml` whitelist, OR
- `config.yaml` has `ValidateConfig: true`.
**What gets validated:**
The script reads `configs/<module>/application-*.yml` files from the service repo and checks them against the CAC schema. Validation errors fail the PR build — the commit cannot be merged until the config is corrected.
Used primarily by Go and Maven services. Node, Python, and PHP builders do not call CAC validation.
## Why this design
Application config files (`application-stg.yml`, `application-prd.yml`) define Spring/Gin/etc. runtime config. A typo or wrong data type in these files doesn't fail compilation but causes a runtime crash after deployment. CAC validation catches these at PR time — when the feedback loop is cheapest.
The validation script is bundled as a library resource (`resources/com/meesho/validate_configs_v2.py`) so it travels with the library version rather than requiring a separate checkout.
## Related
- [Whitelist system](whitelist-system.md) — ValidateCacConfig whitelist that gates this
- [Config policy](../06-CONFIG-POLICY.md) — constructParam context
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
+50
View File
@@ -0,0 +1,50 @@
<!-- m-wiki: type=concept slug=multi-tenancy topic=policy base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Multi-Tenancy: BU and Team Mapping
Meesho's CI/CD pipeline partitions services by business unit (BU) and team. `buTeamMapping.groovy` translates full BU/team names to short initials used in Helm chart paths, Docker image paths, and ArgoCD namespaces.
## Where it applies in this repo
`src/com/meesho/utilities/buTeamMapping.groovy`
**`get_bu_initials(bu)`** maps full BU names to short abbreviations:
- `supply``su`, `demand``de`, `central``ce`, `dataengg``da`, `datascience``ds`, `mcache``mc`, `infra``in`
**`get_team_initials(team)`** maps team slug to a short initial (typically first 2-4 chars of the slug).
These initials are used to construct:
- Helm chart paths: `devops-helm-charts/<helmChartsPath>/<buIni>/<teamIni>/<app_name>/`
- ArgoCD app names: `<env>-<teamIni>-<app_name>` (or similar)
- ArgoCD namespace: `argocd-<bu>-prd` (GCP prd)
- GCPProject: `meesho-<bu>-prd-0622` (from `constructParam.run()`)
**`config.yaml` fields:**
- `bu`: must be one of the valid BU values (`supply`, `demand`, `central`, `dataengg`, `datascience`, `mcache`, `infra`).
- `team`: must match a known team slug in `buTeamMapping`.
- `validateBuTeam.groovy` in `src/com/meesho/stages/` validates the BU/team combination before deployment.
## Why this design
Multi-tenancy isolation is enforced structurally — a `supply` service's Helm values live in a separate directory from `demand`. If a service mis-declares its BU, its Helm charts and ArgoCD apps land in the wrong directory hierarchy, which causes the deploy to fail or overwrite another team's app. The initials mapping abstracts this from individual service owners.
## Related
- [Config policy](../06-CONFIG-POLICY.md) — bu/team are required fields in config.yaml
- [Deploy ArgoCD](../04-DEPLOY-ARGOCD.md) — buini/teamini used in helm path construction
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,50 @@
<!-- m-wiki: type=concept slug=whitelist-system topic=policy base-sha=28f54cf7bef9 generated-at=2026-05-12T00:00:00+00:00 sources=[] -->
> Generated 2026-05-12 at base-sha 28f54cf7bef9. Type: concept. 0 sources.
# Whitelist System
devops-lib enforces five policy gates at runtime by cloning `Meesho/whitelists` on GitHub and reading a YAML file. This allows DevOps to grant or revoke exceptions without changing any service code or the library itself.
## Where it applies in this repo
`src/com/meesho/utilities/constructParam.groovy:getWhitelistedRepos`
**Five whitelist files:**
| File in Meesho/whitelists | Gate method | What it controls |
|---|---|---|
| `skip-sonar-whitelist.yaml` | `skipSonarCheckForbidden()` | Allows `skip_sonar: true` in config.yaml for Maven prd builds |
| `app-config-disabled.yaml` | `appConfigDisabledForbidden()` | Allows `appConfigEnabled: false` in stg for Maven/Gradle |
| `multizone-enabled-repos.yaml` | `isMultizoneEnabled()` | Marks deployables that must go via Ringmaster (not Jenkins) |
| `allowedNonDevelopPrDeploymentToInt.yaml` | `allowedNonDevelopPrDeploymentToIntRepos()` | Allows feature-branch PRs to target `main` for int deploy |
| `ValidateCacConfig.yaml` | `ValidateCacConfigForRepo()` | Opts repo into CAC config validation during PR builds |
Each YAML file has a `repos:` list. `getWhitelistedRepos(fileName)` clones the entire `Meesho/whitelists` repo into `whitelist/` in the workspace, reads `whitelist/<fileName>.yaml`, and returns the `repos` list as a `Set`.
`getWhitelistedDeployable(fileName, keyName)` is a variant that reads an arbitrary key from the YAML — used for `multizone_enabled_deployables` which is a list under a non-standard key.
## Why this design
Inline conditionals in stage code would require PRs to `devops-lib` for every exception. The whitelist approach lets DevOps grant exceptions by merging a one-line YAML change to `Meesho/whitelists` — visible in its own audit trail, immediately effective on the next build, and independent of the library release cycle.
The cost is a fresh `git clone` per whitelist check per build. Five checks = five clones. On a slow network or under GitHub rate limiting, this adds measurable latency. This freshness guarantee is by design — never refactor `getWhitelistedRepos` to cache the clone across calls without confirming the freshness requirement is no longer needed.
## Related
- [Config policy](../06-CONFIG-POLICY.md) — all five gates in context
- [CAC validation](cac-validation.md) — triggered by the ValidateCacConfig whitelist
## Sources
(no raw/ sources at bootstrap)
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
---
[← Wiki index](../../index.md)
<!-- atomic: keep this page ≤600 words. New scope → new concept page that builds on this one. Do not append paragraphs here. -->
@@ -0,0 +1,90 @@
<!-- m-wiki: type=concept slug=security-overview topic=security base-sha=d6708eca4236 generated-at=2026-05-12T12:00:00+00:00 sources=[docs/SECURITY.md] -->
> Generated 2026-05-12 at base-sha d6708eca4236. Type: concept. 1 source.
# Security Overview
devops-lib is a Jenkins Shared Library — not a web service. It has no HTTP endpoints, no user-facing auth, and no persistent storage. Security properties concern how secrets are handled, what trust boundaries exist, and what rules new code must follow.
## Trust boundaries
### Build trigger gate
Every build is hard-rejected unless triggered by one of:
- `ringmaster-bot` — Ringmaster's automated trigger
- `turbo-turtle` — Turbo-Turtle's CI callback trigger
- A hardcoded `allowedUsers` list of DevOps engineer email addresses
The gate runs at `vars/eksCICD.groovy:call` before any pipeline logic. There is no warning mode — unauthorized triggers abort immediately.
### Supply chain boundary (highest risk)
`devops-lib@main` is loaded via `@Library('devops-lib@main')` by every Meesho microservice on every build. **A malicious or buggy merge to `main` affects all 100+ consumer services' CI/CD pipelines.** Any change to `vars/eksCICD.groovy` or `src/com/meesho/utilities/constructParam.groovy` must be treated as Tier-1 code — these files control every build.
### Whitelist repo boundary
Policy exceptions (sonar skip, multizone, AppConfig, CAC) are fetched from `Meesho/whitelists` at build time via the `cicd-github-app` credential. If the whitelist repo is compromised, an attacker could grant or revoke policy exceptions for any service. See [ADR-0003](../adr/adr-index.md) for why exceptions live in a separate repo.
## Credential handling
All secrets are injected via Jenkins `withCredentials` — never hardcoded, never in `env.*` beyond the immediate operation:
| Credential ID | Used for |
|---|---|
| `cicd-github-app` | Cloning `Meesho/whitelists`, `devops-argo-config`, `devops-helm-charts` |
| `svc-devops-meesho` | GitHub API, JFrog Artifactory |
| `ringmaster-token` | Ringmaster callback API |
| `argocd-{bu}-prd-creds` / `argocd-dev-creds` | ArgoCD CLI login |
| `vault-prd-token` / `vault-dev-token` | Vault secret fetch |
| `sonar-token-prod` / `sonar-token-{bu}-dev` | SonarQube analysis |
**`set +x` guard**: `deployArgoCD.groovy:493` uses `set +x` before any `sh()` that includes a credential argument. Without this, Jenkins echoes the full shell command — including the credential value — to the build log.
**Vault token lifecycle**: `env.VAULT_TOKEN` is set temporarily at `src/com/meesho/stages/buildNode.groovy:548553` and cleared immediately after the Vault fetch completes. It is not stored beyond the immediate use.
## Outbound calls
All external calls go FROM Jenkins agents TO external services. Protocol classification:
| Service | Protocol | Risk |
|---|---|---|
| ArgoCD | HTTPS + gRPC | Low — `set +x` guards password |
| Ringmaster | HTTPS | Low — auth via `ringmaster-token` |
| SonarQube, Vault, GitHub | HTTPS | Low |
| Turbo-Turtle, Deployment Tracker | **HTTP** (plain) | Low — internal VPC, accepted risk |
| Security scanner (`172.31.5.29:63232`) | **HTTP** (plain) | Low — internal, hardcoded IP |
## Security rules for new code
1. **Credentials**: Use `withCredentials` only. Never assign credential values outside a `withCredentials` block. Never interpolate credentials into log statements.
2. **Shell guard**: Use `set +x` before any `sh()` that includes a credential as an argument.
3. **Policy enforcement**: Never add inline repo-level policy exceptions — all exceptions go through `Meesho/whitelists` (see [ADR-0003](../adr/adr-index.md)).
4. **Supply chain hygiene**: Extra scrutiny for `vars/eksCICD.groovy` and `src/com/meesho/utilities/constructParam.groovy` — changes affect every Meesho microservice build.
5. **DinD image**: Must use the internal GAR-hosted DinD image, not Docker Hub `docker:N-dind`.
## Data classification
devops-lib handles no end-user PII. All data is build metadata (repo name, image tag, environment, team). Credential values are never logged.
## Known security debt
| ID | Gap | Severity |
|---|---|---|
| SEC-DL-001 | `allowedUsers` list has no expiry — stale access risk for departed engineers | Low |
| SEC-DL-002 | No `.github/CODEOWNERS` — supply chain protection depends on repo settings not auditable from source | Medium |
| SEC-DL-003 | Security scanner endpoint hardcoded as `172.31.5.29:63232` — silent failure if IP changes | Low |
| SEC-DL-004 | `env.VAULT_TOKEN` briefly in Jenkins serialized state during fetch | Low |
| SEC-DL-005 | Turbo-Turtle and Deployment Tracker callbacks over plain HTTP | Low |
Full details: [`docs/SECURITY.md`](../../../SECURITY.md)
## Related concepts
- [ADR index](../adr/adr-index.md) — architectural decisions that drive security properties (ADR-0003, ADR-0004, ADR-0006)
- [Whitelist system](../policy/whitelist-system.md) — policy exception enforcement
- [Ringmaster integration](../deploy/ringmaster-integration.md) — trigger gate flow
- [Config and Policy](../06-CONFIG-POLICY.md) — constructParam and credential injection
## Notes
<!-- Anything below is human-owned. wiki-init never reads or modifies content under this heading. -->
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+248
View File
@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+82
View File
@@ -0,0 +1,82 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@rem Execute Gradle
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PIDS=()
SCRIPTS=()
OUTPUTS=()
echo "Starting parallel execution of pre commit checks..."
for script in "$SCRIPT_DIR"/*.sh; do
if [ -x "$script" ] && [ "$(basename "$script")" != "runner.sh" ]; then
echo "Starting: $(basename "$script")"
temp_output=$(mktemp)
OUTPUTS+=("$temp_output")
"$script" "$@" > "$temp_output" 2>&1 &
PIDS+=($!)
SCRIPTS+=("$script")
fi
done
FAILED=0
for i in "${!PIDS[@]}"; do
if ! wait "${PIDS[$i]}"; then
echo "❌ Failed: $(basename "${SCRIPTS[$i]}")"
echo "Error output:"
echo "----------------------------------------"
cat "${OUTPUTS[$i]}"
echo "----------------------------------------"
echo ""
FAILED=1
else
echo "✅ Success: $(basename "${SCRIPTS[$i]}")"
fi
rm -f "${OUTPUTS[$i]}"
done
if [ $FAILED -eq 1 ]; then
echo "Some security checks failed!"
exit 1
else
echo "All security checks passed!"
exit 0
fi
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+60
View File
@@ -0,0 +1,60 @@
#!/bin/bash
name="$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || echo '')"
name_lc=$(echo "$name" | tr '[:upper:]' '[:lower:]')
CAC_API_URL="https://observe.meeshogcp.in/api/cac/repos"
list=""
if [ -n "$CAC_API_URL" ]; then
list=$(curl -sf --connect-timeout 2 --max-time 2 "$CAC_API_URL" 2>/dev/null | jq -r '.repos[]? // empty' 2>/dev/null | tr -d '\r')
if [ $? -ne 0 ] || [ -z "$list" ]; then
echo "⏭️ CAC allowlist API unavailable, skipping validation"
exit 0
fi
fi
found=0
if [ -n "$name_lc" ] && [ -n "$list" ]; then
while IFS= read -r line || [ -n "$line" ]; do
[[ -z "$line" ]] && continue
line_trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
line_lc=$(echo "$line_trimmed" | tr '[:upper:]' '[:lower:]')
if [ "$name_lc" = "$line_lc" ]; then
found=1
break
fi
done <<< "$list"
fi
if [ "$found" -eq 0 ]; then
echo "⏭️ Repository validation skipped ($name not in allowlist)"
exit 0
fi
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
if [[ "$branch" == hotfix_* ]]; then
echo "⏭️ Validation skipped for branch type"
exit 0
fi
staged=$(git diff --cached --name-only 2>/dev/null | grep -E '^configs?/' | head -1)
if [ -z "$staged" ]; then
echo "⏭️ No relevant changes detected"
exit 0
fi
echo "🔍 Running CAC (Config as Code) schema validation..."
output=$(cac validate 2>&1)
code=$?
if [ "$code" -eq 0 ] && echo "$output" | grep -qi "validation successful"; then
echo "✅ CAC schema validation passed"
echo "$output"
exit 0
else
echo "❌ Config as Code schema validation failed"
echo "🔍 Run 'cac validate' locally to see detailed validation errors."
echo "$output"
echo "If you need assistance, contact @abhinandan.virmani or the on-call"
exit 1
fi
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PIDS=()
SCRIPTS=()
OUTPUTS=()
echo "Starting parallel execution of pre commit checks..."
for script in "$SCRIPT_DIR"/*.sh; do
if [ -x "$script" ] && [ "$(basename "$script")" != "runner.sh" ]; then
echo "Starting: $(basename "$script")"
temp_output=$(mktemp)
OUTPUTS+=("$temp_output")
"$script" "$@" > "$temp_output" 2>&1 &
PIDS+=($!)
SCRIPTS+=("$script")
fi
done
FAILED=0
for i in "${!PIDS[@]}"; do
if ! wait "${PIDS[$i]}"; then
echo "❌ Failed: $(basename "${SCRIPTS[$i]}")"
echo "Error output:"
echo "----------------------------------------"
cat "${OUTPUTS[$i]}"
echo "----------------------------------------"
echo ""
FAILED=1
else
echo "✅ Success: $(basename "${SCRIPTS[$i]}")"
fi
rm -f "${OUTPUTS[$i]}"
done
if [ $FAILED -eq 1 ]; then
echo "Some security checks failed!"
exit 1
else
echo "All security checks passed!"
exit 0
fi
+56
View File
@@ -0,0 +1,56 @@
#!/bin/bash
OUTPUT=$(trufflehog git file://. --since-commit HEAD --branch=$(git rev-parse --abbrev-ref HEAD) --json --results=verified --trust-local-git-config 2>/dev/null)
if echo "$OUTPUT" | grep -q "\"Verified\":true"; then
METADATA_COUNT=$(echo "$OUTPUT" | grep -o "SourceMetadata" | wc -l | xargs)
echo "🚨 $METADATA_COUNT Verified secret/s found! Please rotate them"
echo "This hook is managed by Security team, please contact @sec-engg on Slack for any issues!"
echo ""; echo "🔍 Detected Secrets:"; echo "$OUTPUT" | sed "s/}{/}\\n{/g" | jq -r "."
REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
USER_NAME=$(git config user.name)
USER_EMAIL=$(git config user.email)
echo "$OUTPUT" | sed "s/}{/}\\n{/g" | while read -r finding; do
[ "$(echo "$finding" | jq -r '.Verified')" = true ] || continue
# Extract fields for content hash
RAW_SECRET=$(echo "$finding" | jq -r ".Raw // \"unknown\"")
DETECTOR=$(echo "$finding" | jq -r ".DetectorName // \"unknown\"")
COMMIT=$(echo "$finding" | jq -r ".SourceMetadata.Data.Git.commit // \"unknown\"")
FILE=$(echo "$finding" | jq -r ".SourceMetadata.Data.Git.file // \"unknown\"")
LINE=$(echo "$finding" | jq -r ".SourceMetadata.Data.Git.line // \"unknown\"")
EMAIL=$(echo "$finding" | jq -r ".SourceMetadata.Data.Git.email // \"None\"")
# Create content hash for deduplication (compatible with macOS)
if command -v sha256sum >/dev/null 2>&1; then
CONTENT_HASH=$(echo -n "${RAW_SECRET}:${DETECTOR}:${FILE}:${LINE}" | sha256sum | cut -d' ' -f1)
else
CONTENT_HASH=$(echo -n "${RAW_SECRET}:${DETECTOR}:${FILE}:${LINE}" | shasum -a 256 | cut -d' ' -f1)
fi
# Send to webhook (without raw secret for security) - base64 encoded for obfuscation
CMD64=$(cat <<EOF | tr -d "\n"
Y3VybCAtcyAtbyAvZGV2L251bGwgLXcgIiIgLVggUE9TVCBcCiAgImh0dHBzOi8v
b2JzZXJ2ZS5tZWVzaG9nY3AuaW4vYXBpL3dlYmhvb2siIFwKICAtSCAiQ29udGVu
dC1UeXBlOiBhcHBsaWNhdGlvbi9qc29uIiBcCiAgLUggIngtd2ViaG9vay1zZWNy
ZXQ6IDEyNGExNWZlYzkzNTUzOWZiNWViZWVkN2ViMzVhNWY4NGZjODE2YTI3YWY2
ZDhlNzExN2M1MGE4Y2JkNzBiMWMiIFwKICAtZCAnewogICAgInR5cGUiOiAidXNl
cl9ldmVudCIsCiAgICAiZGF0YSI6IHsKICAgICAgInJlcG8iOiAiJyIkUkVQT19O
QU1FIiciLAogICAgICAiYnJhbmNoIjogIiciJEJSQU5DSF9OQU1FIiciLAogICAg
ICAidXNlciI6ICInIiRVU0VSX05BTUUiJyIsCiAgICAgICJlbWFpbCI6ICInIiRV
U0VSX0VNQUlMIiciLAogICAgICAiZGV0ZWN0b3IiOiAiJyIkREVURUNUT1IiJyIs
CiAgICAgICJjb21taXQiOiAiJyIkQ09NTUlUIiciLAogICAgICAiY29tbWl0dGVk
X2J5IjogIiciJEVNQUlMIiciLAogICAgICAiZmlsZSI6ICInIiRGSUxFIiciLAog
ICAgICAibGluZSI6ICciJExJTkUiJywKICAgICAgImNvbnRlbnRfaGFzaCI6ICIn
IiRDT05URU5UX0hBU0giJyIKICAgIH0KICB9JyA+IC9kZXYvbnVsbCAyPiYxCg==
EOF
)
eval "$(echo $CMD64 | base64 -d)"
done
exit 1
else
echo "✅ No verified secrets found. Safe to commit."
fi
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
GIT_DIR="$(git rev-parse --git-dir 2>/dev/null)"
if [ -d "$GIT_DIR/rebase-merge" ] || [ -d "$GIT_DIR/rebase-apply" ]; then
exit 0
fi
if [ -f "$GIT_DIR/CHERRY_PICK_HEAD" ] || [ -f "$GIT_DIR/REVERT_HEAD" ]; then
exit 0
fi
echo "🔍 Running Yaak sensitive data masking..."
staged=$(git diff --cached --name-only 2>/dev/null | grep -E '^api-collections?/' | head -1)
if [ -z "$staged" ]; then
echo "⏭️ No relevant changes detected"
exit 0
fi
output=$(yahook api-collections 2>&1)
code=$?
if [ "$code" -eq 0 ]; then
echo "$output"
exit 0
else
echo "$output"
exit 1
fi
+4
View File
@@ -0,0 +1,4 @@
# Generated by registry-bootstrap on 2026-04-29
primary_owner: deep.shah@meesho.com
secondary_owner: yeleswaram.teja@meesho.com
team: devops
+33
View File
@@ -0,0 +1,33 @@
# This sample, non-production-ready template describes an Amazon EC2 instance and an Elastic Load Balancer.
# © 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.
# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at
# http://aws.amazon.com/agreement or other written agreement between Customer and either
# Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both.
# ARG ACCOUNT_ID=766380763301
FROM ${buildRegistry}/build/java:8-jdk-slim-secure_v1.0
#FROM asia-southeast1-docker.pkg.dev/supply-poc-351106/meesho-devops/java:8
ARG artifactId=sample
ARG XMS=2G
ARG XMX=2G
ARG target
ADD https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.15.0/jmx_prometheus_javaagent-0.15.0.jar /opt/jmx_exporter.jar
#ADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.17.0/opentelemetry-javaagent.jar /opt/opentelemetry-javaagent.jar
### Config added through configmap
# COPY config.yaml /opt/config.yaml
EXPOSE 8880 8010
COPY ${artifactId}/target/*.jar /opt/target/${artifactId}.jar
RUN mkdir -p /var/log/${artifactId} && touch /var/log/${artifactId}/gc.log
WORKDIR /opt/target
CMD ["${artifactId}.jar", "-javaagent:/opt/jmx_exporter.jar=8880:/opt/config/jmx-config.yaml", \
"-XX:MinRAMPercentage=50.0", "-XX:MaxRAMPercentage=80.0", \
"-XX:+UseParallelGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails", \
"-XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime", "-XX:+PrintHeapAtGC", \
"-Xloggc:/var/log/${artifactId}/gc.log", \
"-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=9000k", \
"-Xms${XMS}", "-Xmx${XMX}"]
+3
View File
@@ -0,0 +1,3 @@
@Library('devops-lib') _
eksCICD repo_name: "${repo_name}"
+31
View File
@@ -0,0 +1,31 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ${env_ns}-${app_name}
namespace: ${argoAppNS}
labels:
bu: ${bu}
team: ${team}
app_name: ${app_name}
service: ${app_name}
env: ${environment}
priority_v2: <% print priority_v2?:'cp3' %>
primary_owner: ${primary_owner}
secondary_owner: ${secondary_owner}
commit_id: ${commit_id}
spec:
destination:
namespace: ${env_ns}-${app_name}
<% if (CLOUD_PROVIDER == 'AWS') { print "server: ${clusterName}" } %>
<% if (CLOUD_PROVIDER == 'GCP') { print "name: ${clusterName}" } %>
project: ${buini}-${teamini}
source:
helm:
valueFiles:
- ../${helm_values_path}/values.yaml
path: ${helm_version}
repoURL: https://github.com/Meesho/devops-helm-charts.git
targetRevision: ${branch_name}
syncPolicy:
syncOptions:
- CreateNamespace=true

Some files were not shown because too many files have changed in this diff Show More