615 lines
33 KiB
Markdown
615 lines
33 KiB
Markdown
# PR Review Guide
|
||
|
||
> Derived from human review discussions across 26 PRs merged into `main` (PRs #306–#727).
|
||
> Focus: P0 (production-impacting) and P1 (bugs, build reliability, architecture, error handling, observability) signals.
|
||
> Excluded: Style, formatting, refactors, lint-level feedback.
|
||
|
||
---
|
||
|
||
## P0 — Must Not Miss (Production Impact)
|
||
|
||
```yaml
|
||
- id: P0_DEBUG_URL_IN_PRODUCTION_CONFIG
|
||
severity: P0
|
||
title: Debug/tunnel URLs must not reach production pipeline code
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/*.groovy"
|
||
- "src/com/meesho/utilities/*.groovy"
|
||
required_evidence:
|
||
- "No lhr.life, ngrok, localtunnel, or similar tunnel hostnames present in any switch/case or URL assignment"
|
||
- "All cicdBaseUrl values point to stable internal DNS names (e.g. turbo-turtle.meeshogcp.in)"
|
||
suggested_fix:
|
||
- "Revert any lhr.life or debug tunnel URL to the canonical internal DNS before merging"
|
||
- "Add a pre-commit or CI grep that fails on *.lhr.life, *.ngrok.io, *.tunnel.* patterns in .groovy files"
|
||
why: >
|
||
PRs #596 and #664 both accidentally merged lhr.life tunnel URLs into deployRingmaster.groovy,
|
||
replacing the production Turbo-Turtle endpoint. This would break all production and int-environment
|
||
deployments silently — pipelines would attempt callbacks to an ephemeral tunnel that no longer exists.
|
||
references:
|
||
- "PR #596"
|
||
- "PR #664"
|
||
|
||
- id: P0_SENSITIVE_DEBUG_PRINT_IN_PIPELINE
|
||
severity: P0
|
||
title: Pipeline code must not print SSH private keys or credentials to console
|
||
trigger_paths:
|
||
- "src/com/meesho/utilities/addSSHKey.groovy"
|
||
- "src/com/meesho/**/*.groovy"
|
||
- "resources/com/meesho/*Dockerfile"
|
||
required_evidence:
|
||
- "No `cat`, `echo`, or `print` of private key file contents (id_github_jenkins, id_rsa, etc.)"
|
||
- "No `ls -al` of credential file paths in shipped pipeline code"
|
||
suggested_fix:
|
||
- "Remove any `cat ./id_github_jenkins` or `ls -al <key-path>` lines before merging"
|
||
- "Debug prints of credentials are acceptable only in local branches; must be stripped before PR"
|
||
why: >
|
||
PR #634 shipped `cat ./id_github_jenkins` inside addSSHKey.groovy. Jenkins build logs are accessible
|
||
to all users with job read access — printing private key content exposes the key to anyone who can
|
||
view the console output, which is a critical credential leak.
|
||
references:
|
||
- "PR #634"
|
||
|
||
- id: P0_HARDCODED_IP_IN_PIPELINE
|
||
severity: P0
|
||
title: Internal service endpoints must use DNS names, not hardcoded IPs
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/*.groovy"
|
||
- "src/com/meesho/utilities/*.groovy"
|
||
required_evidence:
|
||
- "No bare IP addresses (e.g. 172.x.x.x, 10.x.x.x) used as curl/HTTP targets in pipeline code (known exception: securityScan.groovy:11 uses 172.31.5.29:63232 pending remediation)"
|
||
- "All internal service calls use DNS-resolvable hostnames"
|
||
suggested_fix:
|
||
- "Replace IP literals with DNS names before merging"
|
||
- "If no DNS exists yet, create one before the PR lands — do not ship with the IP as a placeholder"
|
||
why: >
|
||
PR #681 hardcoded `http://172.23.72.116:5002` as the build-callback endpoint in notify.groovy.
|
||
IP addresses break silently when infra is rebalanced or services are migrated, with no indication
|
||
in the pipeline code that the endpoint has moved.
|
||
references:
|
||
- "PR #681"
|
||
|
||
- id: P0_CONFIG_TEMPLATE_INJECTION_SAFETY
|
||
severity: P0
|
||
title: User-supplied config data embedded in YAML templates must be sanitized
|
||
trigger_paths:
|
||
- "resources/com/meesho/values.yaml"
|
||
- "src/com/meesho/stages/deployArgoCD.groovy"
|
||
required_evidence:
|
||
- "Any `.trim().replaceAll(...)` applied to externally-sourced config data is reviewed for correctness"
|
||
- "Indentation injection via `replaceAll(\"(?m)^\", \" \")` is validated against multiline edge cases"
|
||
- "User-controlled strings containing YAML special characters (`:`, `|`, `>`, `#`) are escaped or block-quoted"
|
||
suggested_fix:
|
||
- "Apply strict input validation or schema-based parsing before interpolating user config into YAML templates"
|
||
- "Prefer a YAML library for construction over string interpolation"
|
||
why: >
|
||
PR #343 reviewer flagged that 'people can put strange stuff in their configs' when raw config data
|
||
is string-interpolated into the values.yaml template. Malformed or adversarial input can corrupt
|
||
the YAML structure silently, causing ArgoCD deployments to use wrong or empty config.
|
||
references:
|
||
- "PR #343"
|
||
```
|
||
|
||
---
|
||
|
||
## P1 — Important Improvements (Reliability, Build Health, Architecture)
|
||
|
||
```yaml
|
||
- id: P1_EXTERNAL_BINARY_DOWNLOAD_IN_BUILD
|
||
severity: P1
|
||
title: External binaries must not be downloaded on every pipeline run
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "resources/com/meesho/*Dockerfile"
|
||
required_evidence:
|
||
- "No `curl -LO https://binaries.sonarsource.com/...` or `curl -LO https://go.dev/dl/...` inside the stage body"
|
||
- "Sonar-scanner binary is baked into the relevant Docker image or fetched from internal Artifactory"
|
||
- "Go toolchain is fetched from Artifactory (not go.dev) when a non-image version is needed"
|
||
suggested_fix:
|
||
- "Bake sonar-scanner into the builder base image; reference it as a path, not a curl download"
|
||
- "For Go version management, pull tarballs from internal Artifactory instead of go.dev to avoid external dependency and improve speed"
|
||
- "If dynamic version selection is needed, cache the download to a Jenkins agent workspace layer"
|
||
why: >
|
||
PR #643 added `curl -LO https://binaries.sonarsource.com/.../sonar-scanner-cli-5.0.1.3006-linux.zip`
|
||
and `curl -LO https://go.dev/dl/go${goVersion}.linux-amd64.tar.gz` inside the build stage body.
|
||
This increases build time on every run, creates a hard dependency on external internet connectivity,
|
||
and makes pipelines brittle when upstream URLs change or are rate-limited.
|
||
references:
|
||
- "PR #643"
|
||
|
||
- id: P1_MISSING_PREREQUISITE_FILE_CHECK
|
||
severity: P1
|
||
title: Required config files must be verified to exist before stages that depend on them
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
required_evidence:
|
||
- "If a stage uses `sonar-project.properties`, `pom.xml`, or equivalent config, existence is checked before the stage runs"
|
||
- "Missing file results in a clear failure message, not a cryptic scanner error mid-stage"
|
||
suggested_fix:
|
||
- "Add `if (!fileExists('sonar-project.properties')) { error('sonar-project.properties not found — cannot run Sonar scan') }` before the scan stage"
|
||
why: >
|
||
PR #643 reviewer flagged that if sonar-project.properties does not exist, the scan stage fails with
|
||
a cryptic error mid-execution. An upfront existence check surfaces the real problem immediately and
|
||
avoids partial stage execution that leaves the pipeline in an ambiguous state.
|
||
references:
|
||
- "PR #643"
|
||
|
||
- id: P1_TEST_FAILURE_MUST_FAIL_PIPELINE
|
||
severity: P1
|
||
title: Test failures must cause the build to fail, not silently continue
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
required_evidence:
|
||
- "Non-zero test exit codes propagate as build failures or are explicitly documented as intentional with a tracking ticket"
|
||
- "Any `|| true` or equivalent suppression on a test command has a comment explaining why"
|
||
suggested_fix:
|
||
- "Remove `if (testExitCode != 0) { sh('echo tests failed, pipeline continues') }` patterns"
|
||
- "If tests are intentionally non-blocking, capture the result as a warning in the build report rather than silently absorbing the failure"
|
||
why: >
|
||
PR #643 had a pattern where Go test failures were caught, logged as 'pipeline will continue', and
|
||
execution proceeded to Sonar and Docker stages. This allows broken code to be packaged and deployed
|
||
while appearing to pass CI, defeating the purpose of test gating.
|
||
references:
|
||
- "PR #643"
|
||
|
||
- id: P1_CENTRALIZED_CLEANUP_IN_FINALLY
|
||
severity: P1
|
||
title: Build cleanup logic must be in a centralized finally block, not scattered try-catches
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildRust.groovy"
|
||
required_evidence:
|
||
- "New failure/cleanup paths are handled via a shared cleanup function called from the build's finally block"
|
||
- "Cleanup is not added only to the specific branch/case that was fixed — it covers all known failure scenarios"
|
||
suggested_fix:
|
||
- "Extract a `cleanupOnFailure(config)` function in each build stage file"
|
||
- "Call it from the `finally` block of the top-level try/catch in buildMaven, buildNode, buildGo, buildRust"
|
||
why: >
|
||
PR #533 reviewer requested that cleanup on failed Docker tag operations be moved to the overall
|
||
`finally` block rather than an ad-hoc inner try-catch. Scattered cleanup means new failure modes
|
||
(aborts, timeouts, new stages) bypass cleanup silently — a centralized `finally` ensures cleanup
|
||
is language-agnostic and future-proof.
|
||
references:
|
||
- "PR #533"
|
||
|
||
- id: P1_SEMANTIC_FLAG_REUSE
|
||
severity: P1
|
||
title: Pipeline control flags must have precise semantics; do not reuse existing flags for new meanings
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "src/com/meesho/stages/hotFix.groovy"
|
||
required_evidence:
|
||
- "New pipeline behaviors controlled by a new, clearly named env flag (not piggybacking on `hot_fix`, `skip_sonar`, etc.)"
|
||
- "The flag name matches the intent: `revert_pr`, `skip_quality_gate`, `is_config_only_change`"
|
||
suggested_fix:
|
||
- "Introduce `env.revert_pr` rather than overloading `env.hot_fix` for revert PR detection"
|
||
- "Document new flags in the devops-lib README or pipeline configuration guide"
|
||
why: >
|
||
PR #578 used `env.hot_fix` to skip quality gates for revert PRs, which a reviewer flagged as a
|
||
semantic hack. PR #495 added a redundant `env.hot_fix || skip_sonar` check when `skip_sonar` was
|
||
already sufficient. Overloading flags creates confusion about which paths are active and makes
|
||
future changes to either flag risky.
|
||
references:
|
||
- "PR #578"
|
||
- "PR #495"
|
||
|
||
- id: P1_DOCKER_LAYER_CACHING_DEPENDENCY_FIRST
|
||
severity: P1
|
||
title: Dockerfiles must copy dependency manifests before source to enable layer caching
|
||
trigger_paths:
|
||
- "resources/com/meesho/*Dockerfile"
|
||
- "resources/com/meesho/rust-Dockerfile"
|
||
required_evidence:
|
||
- "Dependency manifest files (Cargo.toml/Cargo.lock, package.json/package-lock.yaml, go.mod/go.sum, pom.xml) are COPY'd and dependencies fetched before COPY . ."
|
||
- "Source-only changes do not invalidate the dependency install layer"
|
||
suggested_fix:
|
||
- "For Rust: `COPY Cargo.toml Cargo.lock ./` → `RUN cargo fetch` → `COPY . .` → `RUN cargo build --release`"
|
||
- "For Node: `COPY package.json package-lock.yaml ./` → `RUN npm ci` → `COPY . .` → `RUN npm run build`"
|
||
why: >
|
||
PR #650 reviewer flagged that the Rust Dockerfile did `COPY . .` before fetching dependencies,
|
||
meaning every source change triggers a full `cargo build` including all dependency compilation.
|
||
Splitting the copy invalidates the dependency layer only on manifest changes, dramatically reducing
|
||
incremental build times.
|
||
references:
|
||
- "PR #650"
|
||
|
||
- id: P1_HARDCODED_TOOL_VERSION_IN_DOCKERFILE
|
||
severity: P1
|
||
title: Base image tool versions must be developer-configurable, not hardcoded in Dockerfiles
|
||
trigger_paths:
|
||
- "resources/com/meesho/*Dockerfile"
|
||
- "resources/com/meesho/rust-Dockerfile"
|
||
required_evidence:
|
||
- "Runtime/language version (Rust, Go, Java, Node) is parameterized as a build ARG or resolved from the service config, not hardcoded"
|
||
- "Pattern is consistent with how Go, Java, and Python versions are handled in existing Dockerfiles"
|
||
suggested_fix:
|
||
- "Use `ARG RUST_VERSION=1.86.0` and `FROM .../rust:${RUST_VERSION} AS builder` — pass the version from the pipeline config"
|
||
why: >
|
||
PR #650 hardcoded `FROM .../rust:1.86.0` in the Rust Dockerfile. Reviewer noted this is inconsistent
|
||
with Go, Java, and Python where the version is developer-configurable via the service config. Services
|
||
needing a different Rust version would require a devops-lib PR rather than a self-service config change.
|
||
references:
|
||
- "PR #650"
|
||
|
||
- id: P1_DYNAMIC_DOCKERFILE_MUTATION_VIA_SHELL
|
||
severity: P1
|
||
title: Dockerfiles must not be constructed by appending lines via shell echo at build time
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
required_evidence:
|
||
- "No `sh \"echo '...' >> Dockerfile-${artifactId}\"` patterns in build stage code"
|
||
- "Conditional Dockerfile content is handled via ARG/build-arg, multi-stage targets, or separate Dockerfile templates"
|
||
suggested_fix:
|
||
- "Use `ARG USE_CAC_PATH` in the Dockerfile template and conditional `RUN` blocks based on that arg"
|
||
- "If the feature is truly mode-specific, maintain a separate Dockerfile template (e.g., `node-cac-Dockerfile`) rather than mutating a shared one at runtime"
|
||
why: >
|
||
PR #707 appended `RUN truncate -s 0 .env` to the Dockerfile via `sh echo >>` when CAC was enabled.
|
||
Runtime Dockerfile mutation makes the actual image instructions invisible in code review, untestable
|
||
in isolation, and fragile to ordering assumptions.
|
||
references:
|
||
- "PR #707"
|
||
|
||
- id: P1_FEATURE_BRANCH_IN_ENVIRONMENT_MAP
|
||
severity: P1
|
||
title: Feature branch names must not be hardcoded in the environment/branch mapping tables
|
||
trigger_paths:
|
||
- "src/com/meesho/utilities/constructParam.groovy"
|
||
- "src/com/meesho/stages/deployArgoCD.groovy"
|
||
required_evidence:
|
||
- "The `environment_map` and `branch_param_map` in constructParam.groovy / deployArgoCD.groovy contain only permanent branch names (master, main, develop, gcp-main, etc.)"
|
||
- "No `feature/...` branch names are present in these maps"
|
||
suggested_fix:
|
||
- "Remove test-only feature branch entries before merging"
|
||
- "If special branch routing is needed for integration testing, use a mechanism that is not committed to the shared library (e.g., a per-repo override config)"
|
||
why: >
|
||
PR #707 added `feature/config_as_code_main` to both environment_map and branch_param_map as a
|
||
testing convenience. Reviewer questioned the purpose and the author confirmed it was test-only.
|
||
Leaving it in the shared library would mean all repos inheriting devops-lib would attempt to route
|
||
this branch to staging, which is incorrect.
|
||
references:
|
||
- "PR #707"
|
||
|
||
- id: P1_SCRIPT_REUSE_OVER_DUPLICATION
|
||
severity: P1
|
||
title: Config validation scripts must be shared, not duplicated per language
|
||
trigger_paths:
|
||
- "resources/com/meesho/validate_configs*.py"
|
||
required_evidence:
|
||
- "New language-specific validation needs extend `validate_configs_v2.py` (or the current canonical script) rather than creating a new file"
|
||
- "Duplication is justified only when the new script has fundamentally different logic that cannot be parameterized"
|
||
suggested_fix:
|
||
- "Extend `validate_configs_v2.py` with language-specific config schemas rather than copying it to `validate_configs_node.py`"
|
||
- "Use a `--lang` argument or config-driven schema selection to handle per-language differences"
|
||
why: >
|
||
PR #707 introduced `validate_configs_node.py` (470 lines) when `validate_configs_v2.py` already
|
||
existed. Duplicating the validation script means bug fixes and new validation rules must be applied
|
||
in multiple places, and the scripts will drift over time.
|
||
references:
|
||
- "PR #707"
|
||
|
||
- id: P1_TOOLS_IN_BASE_IMAGE_NOT_BUILD_STEP
|
||
severity: P1
|
||
title: Globally-used tools must be baked into the base image, not installed per build
|
||
trigger_paths:
|
||
- "resources/com/meesho/*Dockerfile"
|
||
- "src/com/meesho/stages/*.groovy"
|
||
required_evidence:
|
||
- "No `npm install -g <tool>` or `apt-get install <tool>` inside the Docker build RUN steps that are also used globally"
|
||
- "Tools like sonar-scanner, pnpm, and librdkafka are in the base image where they apply to all builds"
|
||
suggested_fix:
|
||
- "Move `npm install -g sonar-scanner` and similar global installs to the base Node image build process"
|
||
- "Cut a new base image version that includes the tool rather than installing it on each application build"
|
||
why: >
|
||
PR #306 flagged `npm install -g sonar-scanner` in the application Dockerfile — this runs on every
|
||
build for every service. Moving it to the base image eliminates the per-build install cost and
|
||
ensures all services use the same tool version.
|
||
references:
|
||
- "PR #306"
|
||
|
||
- id: P1_DOWNLOADED_BINARY_HASH_VERIFICATION
|
||
severity: P1
|
||
title: Externally downloaded binaries must have their integrity verified
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "resources/com/meesho/*Dockerfile"
|
||
required_evidence:
|
||
- "Any `curl -LO` downloading a binary is followed by a hash/checksum verification step"
|
||
- "Or the download is replaced by an Artifactory-hosted artifact with controlled provenance"
|
||
suggested_fix:
|
||
- "After downloading, run `sha256sum -c <expected-hash>` or use the official checksum file provided by the vendor"
|
||
- "Prefer Artifactory-hosted binaries (Meesho-controlled) to eliminate the need for runtime checksum verification"
|
||
why: >
|
||
PR #643 reviewer requested: 'Could we also verify that the downloaded artifact matches its computed hash?'
|
||
Pipelines downloading binaries from the internet without verification are vulnerable to supply-chain
|
||
attacks if the upstream URL is compromised or served a modified artifact.
|
||
references:
|
||
- "PR #643"
|
||
|
||
- id: P1_WRONG_ENV_VAR_IN_CONTAINER
|
||
severity: P1
|
||
title: Container environment variables must be appropriate for the runtime language
|
||
trigger_paths:
|
||
- "resources/com/meesho/rust-values.yaml"
|
||
- "resources/com/meesho/rust-Dockerfile"
|
||
- "resources/com/meesho/*-values.yaml"
|
||
required_evidence:
|
||
- "GOMAXPROCS is not set in non-Go containers (Rust, Java, Node, Python)"
|
||
- "Any env vars carried over from a template are validated as relevant to the target runtime"
|
||
suggested_fix:
|
||
- "Remove `GOMAXPROCS` from rust-values.yaml — it is a Go runtime tuning parameter and has no effect in a Rust binary"
|
||
- "When creating a new language template by copying an existing one, audit all env vars for relevance"
|
||
why: >
|
||
PR #650 set `GOMAXPROCS` in the Rust service values.yaml template, which a reviewer questioned.
|
||
This is a Go-specific knob that controls goroutine scheduling and is meaningless in a Rust process.
|
||
Beyond being confusing, copying env vars blindly from templates creates operational noise and
|
||
incorrect assumptions for future operators.
|
||
references:
|
||
- "PR #650"
|
||
|
||
- id: P1_SHARED_CONSTANTS_FOR_REGISTRY_PATHS
|
||
severity: P1
|
||
title: Repeated infrastructure constants (registry URLs, base paths) must be defined as shared variables
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/*.groovy"
|
||
- "src/com/meesho/utilities/*.groovy"
|
||
required_evidence:
|
||
- "Registry base URLs like `asia-southeast1-docker.pkg.dev/meesho-central-dev-0622/toolchain` appear in a single shared constant, not repeated inline across stage files"
|
||
- "New stage files reference the shared constant rather than re-declaring the string"
|
||
suggested_fix:
|
||
- "Define `TOOLCHAIN_REGISTRY_BASE` (or equivalent) in a shared constants file/class in `src/com/meesho/utilities/`"
|
||
- "Reference it in notify.groovy and any other stage that constructs toolchain image paths"
|
||
why: >
|
||
PR #681 reviewer flagged `def base_registry = \"asia-southeast1-docker.pkg.dev/meesho-central-dev-0622/toolchain\"`
|
||
as an inline constant that should be a shared variable. Duplicating registry paths means a project
|
||
or region migration requires finding and updating every occurrence individually.
|
||
references:
|
||
- "PR #681"
|
||
```
|
||
|
||
---
|
||
|
||
## Common Failure Patterns Observed
|
||
|
||
```yaml
|
||
- id: PATTERN_DEBUG_ARTIFACT_LEFT_IN_PRODUCTION_CODE
|
||
title: Debug artifacts and test configurations merged into main pipeline code
|
||
description: >
|
||
Multiple PRs merged temporary debug configurations (tunnel URLs, hardcoded IPs, feature branch
|
||
mappings, SSH key prints, debug echo statements) that were introduced during local testing and
|
||
not cleaned up before merge. The pattern appears in deployRingmaster.groovy (lhr.life URLs),
|
||
notify.groovy (IP address), constructParam.groovy (feature branch mapping), and addSSHKey.groovy
|
||
(credential printing). The shared library nature of devops-lib means these reach all services
|
||
immediately on merge.
|
||
related_rules:
|
||
- "P0_DEBUG_URL_IN_PRODUCTION_CONFIG"
|
||
- "P0_SENSITIVE_DEBUG_PRINT_IN_PIPELINE"
|
||
- "P0_HARDCODED_IP_IN_PIPELINE"
|
||
- "P1_FEATURE_BRANCH_IN_ENVIRONMENT_MAP"
|
||
references:
|
||
- "PR #596"
|
||
- "PR #634"
|
||
- "PR #664"
|
||
- "PR #681"
|
||
- "PR #707"
|
||
|
||
- id: PATTERN_PER_BUILD_TOOL_INSTALLATION
|
||
title: Tools that should be in base images are installed on every build run
|
||
description: >
|
||
Several PRs introduced per-build installation of tools that belong in the builder base image:
|
||
sonar-scanner downloaded via curl on every Go and Node build, Go toolchain downloaded from go.dev
|
||
on every Sonar scan, librdkafka and pnpm installed in application Dockerfiles. This pattern slows
|
||
all builds, adds external network dependencies to the critical path, and creates version drift
|
||
when different builds download different patch versions.
|
||
related_rules:
|
||
- "P1_EXTERNAL_BINARY_DOWNLOAD_IN_BUILD"
|
||
- "P1_TOOLS_IN_BASE_IMAGE_NOT_BUILD_STEP"
|
||
- "P1_DOWNLOADED_BINARY_HASH_VERIFICATION"
|
||
references:
|
||
- "PR #306"
|
||
- "PR #643"
|
||
- "PR #650"
|
||
|
||
- id: PATTERN_COPY_PASTE_WITHOUT_AUDIT
|
||
title: New language support created by copying existing templates without auditing all fields
|
||
description: >
|
||
When adding Rust CICD support (PR #650) and Node CAC support (PR #707), reviewers flagged that
|
||
values.yaml templates, Dockerfiles, and validation scripts were copied from an existing language
|
||
without removing inapplicable settings (GOMAXPROCS in Rust) or reusing shared logic
|
||
(validate_configs_node.py vs validate_configs_v2.py). Copy-paste propagates bugs and misconfigurations
|
||
from the source template to the new language.
|
||
related_rules:
|
||
- "P1_WRONG_ENV_VAR_IN_CONTAINER"
|
||
- "P1_SCRIPT_REUSE_OVER_DUPLICATION"
|
||
- "P1_HARDCODED_TOOL_VERSION_IN_DOCKERFILE"
|
||
- "P1_DOCKER_LAYER_CACHING_DEPENDENCY_FIRST"
|
||
references:
|
||
- "PR #650"
|
||
- "PR #707"
|
||
```
|
||
|
||
---
|
||
|
||
## Review Checklist
|
||
|
||
Before approving, verify:
|
||
|
||
**Security & Credentials**
|
||
- [ ] **No debug/tunnel URLs** (P0_DEBUG_URL_IN_PRODUCTION_CONFIG): No lhr.life, ngrok, or localtunnel hostnames in any `.groovy` or config file
|
||
- [ ] **No credential printing** (P0_SENSITIVE_DEBUG_PRINT_IN_PIPELINE): No `cat`, `echo`, or `print` of SSH key or token file contents
|
||
- [ ] **No hardcoded IPs** (P0_HARDCODED_IP_IN_PIPELINE): All service endpoints use DNS names, not bare IP literals
|
||
|
||
**Config & Template Safety**
|
||
- [ ] **Config injection safety** (P0_CONFIG_TEMPLATE_INJECTION_SAFETY): User-supplied data embedded in YAML templates is sanitized against multiline and special-character edge cases
|
||
- [ ] **No feature branches in env maps** (P1_FEATURE_BRANCH_IN_ENVIRONMENT_MAP): `environment_map` and `branch_param_map` contain only permanent branch names
|
||
|
||
**Build Reliability**
|
||
- [ ] **No per-build tool downloads** (P1_EXTERNAL_BINARY_DOWNLOAD_IN_BUILD): sonar-scanner, Go tarball, or other tools are not curl-downloaded inside the stage body
|
||
- [ ] **Binary integrity** (P1_DOWNLOADED_BINARY_HASH_VERIFICATION): Any downloaded binary has a hash/checksum verification step, or comes from Artifactory
|
||
- [ ] **Prerequisite file checks** (P1_MISSING_PREREQUISITE_FILE_CHECK): Required config files (sonar-project.properties, etc.) are checked for existence before stages that depend on them
|
||
- [ ] **Test failures fail the build** (P1_TEST_FAILURE_MUST_FAIL_PIPELINE): Non-zero test exit codes propagate as pipeline failures; no silent suppression
|
||
- [ ] **Tools in base image** (P1_TOOLS_IN_BASE_IMAGE_NOT_BUILD_STEP): Global tools (sonar-scanner, pnpm, librdkafka) are in the base image, not installed per-build
|
||
|
||
**Docker & Image Quality**
|
||
- [ ] **Dependency-first Dockerfile layering** (P1_DOCKER_LAYER_CACHING_DEPENDENCY_FIRST): Manifests (Cargo.toml, package.json) are copied and deps fetched before `COPY . .`
|
||
- [ ] **Parameterized tool versions** (P1_HARDCODED_TOOL_VERSION_IN_DOCKERFILE): Language/runtime version is a build ARG, not hardcoded in FROM
|
||
- [ ] **No runtime Dockerfile mutation** (P1_DYNAMIC_DOCKERFILE_MUTATION_VIA_SHELL): No `echo '...' >> Dockerfile-*` patterns in pipeline code
|
||
- [ ] **Correct env vars for runtime** (P1_WRONG_ENV_VAR_IN_CONTAINER): No Go-specific vars (GOMAXPROCS) in non-Go containers; template env vars audited for relevance
|
||
|
||
**Code Architecture**
|
||
- [ ] **Centralized cleanup** (P1_CENTRALIZED_CLEANUP_IN_FINALLY): New failure paths route through the shared `finally` cleanup function, not ad-hoc inner try-catches
|
||
- [ ] **Precise flag semantics** (P1_SEMANTIC_FLAG_REUSE): New pipeline behaviors use new, clearly named flags; not piggybacking on `hot_fix` or `skip_sonar`
|
||
- [ ] **Shared constants** (P1_SHARED_CONSTANTS_FOR_REGISTRY_PATHS): Registry URLs and infra paths are defined as shared constants, not repeated inline
|
||
- [ ] **Script reuse** (P1_SCRIPT_REUSE_OVER_DUPLICATION): Config validation logic extends the existing canonical script; no new duplicate validation scripts
|
||
|
||
---
|
||
|
||
## CodeRabbit-Sourced Learnings
|
||
|
||
> Derived from CodeRabbit automated review comments across PRs #664, #727, and related.
|
||
> Merged into this file per AI Blitz Task 10.
|
||
|
||
```yaml
|
||
- id: CR_UNDEFINED_VARIABLE_IN_NEW_STAGE
|
||
severity: P0
|
||
title: New config fields must be defined in every build stage that references them
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildRust.groovy"
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "src/com/meesho/stages/buildGradle.groovy"
|
||
required_evidence:
|
||
- "Every variable used in conditional logic (e.g. `repoType`, `skip_npmrc`) is extracted from `config` with a default value before first use"
|
||
- "Compare with existing build stages to ensure parity: if buildGo defines `def repoType = config.repoType ?: 'unknown'`, buildRust must too"
|
||
suggested_fix:
|
||
- "Add `def repoType = config.repoType ?: 'unknown'` near the other config extractions in the function"
|
||
why: >
|
||
CodeRabbit flagged in PR #664 that `repoType` was used in buildRust.groovy
|
||
without being defined, causing a MissingPropertyException at runtime. The variable
|
||
was properly defined in buildGo, buildGradle, and buildMaven but missed in the
|
||
new Rust stage.
|
||
references:
|
||
- "PR #664 (CodeRabbit)"
|
||
|
||
- id: CR_INVERTED_CONDITIONAL_ON_REPOTYPE
|
||
severity: P0
|
||
title: Docker build conditionals on repoType must use == not != for the intended type
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "src/com/meesho/stages/buildGradle.groovy"
|
||
- "src/com/meesho/stages/buildRust.groovy"
|
||
required_evidence:
|
||
- "Conditionals guarding Docker build (`stageDocker`) use `repoType == 'microservice'` (not `!=`)"
|
||
- "Both the GCP and fallback/AWS branches have the same guard polarity"
|
||
suggested_fix:
|
||
- "Replace `repoType != 'microservice'` with `repoType == 'microservice'` in the stageDocker conditional"
|
||
why: >
|
||
CodeRabbit caught in PR #664 that `repoType != 'microservice'` caused Docker
|
||
builds to run for library/client repos and skip for microservices — the exact
|
||
opposite of the PR's intent. The same inversion appeared in both the GCP and
|
||
fallback code paths.
|
||
references:
|
||
- "PR #664 (CodeRabbit)"
|
||
|
||
- id: CR_GROOVY_SINGLE_QUOTE_NO_INTERPOLATION
|
||
severity: P1
|
||
title: Log messages with variable references must use double quotes for GString interpolation
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/*.groovy"
|
||
- "src/com/meesho/utilities/*.groovy"
|
||
required_evidence:
|
||
- "No log.info/log.warn/log.error calls use single-quoted strings containing `${...}` placeholders"
|
||
- "All strings with variable interpolation use double quotes (`\"...\"`)"
|
||
suggested_fix:
|
||
- "Change `log.info('Skipping for ${repoType}')` to `log.info(\"Skipping for ${repoType}\")`"
|
||
why: >
|
||
CodeRabbit flagged in PR #664 that single-quoted Groovy strings do NOT support
|
||
interpolation — `'${repoType}'` prints the literal text `${repoType}`, not its value.
|
||
Double-quoted strings become GStrings and evaluate `${}` expressions.
|
||
references:
|
||
- "PR #664 (CodeRabbit)"
|
||
|
||
- id: CR_MISLEADING_SKIP_LOG_MESSAGE
|
||
severity: P1
|
||
title: Skip/bypass log messages must state the actual reason for skipping
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
required_evidence:
|
||
- "Log messages in else/skip branches mention the actual condition that triggered the skip (e.g. `shouldValidateConfig=false` or `CHANGE_ID not set`)"
|
||
- "Messages do not blame `repoType` when the skip is caused by a different condition"
|
||
suggested_fix:
|
||
- "Include the actual condition values: `log.info(\"Skipping CAC validation (repoType=${repoType}, changeId=${env.CHANGE_ID ?: 'N/A'})\")`"
|
||
why: >
|
||
CodeRabbit flagged in PR #664 that the else branch logged "Skipping for repoType"
|
||
when the actual reason was that shouldValidateConfig returned false or CHANGE_ID
|
||
was not set. Misleading skip messages make debugging pipeline behavior harder.
|
||
references:
|
||
- "PR #664 (CodeRabbit)"
|
||
|
||
- id: CR_DEFAULT_FLAG_BREAKS_EXISTING_BEHAVIOR
|
||
severity: P1
|
||
title: New boolean config flags must default to preserving existing behavior
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "src/com/meesho/stages/buildMaven.groovy"
|
||
- "src/com/meesho/stages/buildGo.groovy"
|
||
required_evidence:
|
||
- "New flags (e.g. `skip_npmrc`, `keep_package_lock`) default to the pre-existing behavior when not explicitly set"
|
||
- "If existing behavior is to include `.npmrc`, the default must be `skip_npmrc: false`, not `true`"
|
||
suggested_fix:
|
||
- "Change `def skip_npmrc = config.skip_npmrc != null ? config.skip_npmrc : true` to default `false` to preserve existing behavior"
|
||
why: >
|
||
CodeRabbit flagged in PR #727 that defaulting skip_npmrc to true would break
|
||
repos relying on private registries that need the secret-managed .npmrc. The
|
||
shared library serves all services — a new opt-in flag must not change default
|
||
behavior for existing consumers.
|
||
references:
|
||
- "PR #727 (CodeRabbit)"
|
||
|
||
- id: CR_PACKAGE_LOCK_REMOVAL_VS_NPM_CI
|
||
severity: P1
|
||
title: Removing package-lock.json is incompatible with npm ci as the default install command
|
||
trigger_paths:
|
||
- "src/com/meesho/stages/buildNode.groovy"
|
||
- "resources/com/meesho/node-Dockerfile"
|
||
required_evidence:
|
||
- "If `keep_package_lock=false` removes the lock file, `npm_install_arg` must not be empty (which defaults to `npm ci` in the Dockerfile)"
|
||
- "The Dockerfile fallback install command and the Groovy-side lock file removal logic are in sync"
|
||
suggested_fix:
|
||
- "When `keep_package_lock=false` and `npm_install_arg` is empty, set `npm_install_arg = 'npm install'` to avoid `npm ci` failing on missing lockfile"
|
||
why: >
|
||
CodeRabbit flagged in PR #727 that removing package-lock.json while leaving
|
||
npm_install_arg empty causes the Dockerfile to fall back to `npm ci`, which
|
||
requires a lockfile and will fail. The Groovy stage and Dockerfile must agree
|
||
on the install strategy.
|
||
references:
|
||
- "PR #727 (CodeRabbit)"
|
||
```
|
||
|
||
### CodeRabbit Review Checklist Additions
|
||
|
||
Before approving, also verify:
|
||
|
||
**Variable & Conditional Correctness (CodeRabbit-sourced)**
|
||
- [ ] **No undefined config variables** (CR_UNDEFINED_VARIABLE_IN_NEW_STAGE): Every variable used in a build stage is extracted from `config` with a default before first use
|
||
- [ ] **Correct conditional polarity** (CR_INVERTED_CONDITIONAL_ON_REPOTYPE): Docker build guards use `==` for the intended type, not `!=`
|
||
- [ ] **Double quotes for interpolation** (CR_GROOVY_SINGLE_QUOTE_NO_INTERPOLATION): No `log.info('${var}')` — must be `log.info("${var}")`
|
||
- [ ] **Accurate skip messages** (CR_MISLEADING_SKIP_LOG_MESSAGE): Skip/bypass logs state the actual condition, not a misleading one
|
||
- [ ] **Safe flag defaults** (CR_DEFAULT_FLAG_BREAKS_EXISTING_BEHAVIOR): New boolean config flags default to preserving pre-existing behavior
|
||
- [ ] **Lock file vs install command sync** (CR_PACKAGE_LOCK_REMOVAL_VS_NPM_CI): Removing lock files is paired with a compatible install command
|