// Best-effort fire of the standalone ai-blitz-jobs (coverage-only) Jenkins // job after a successful per-repo CI build. ai-blitz-jobs runs the // coverage-only.Jenkinsfile from devops-lib@coverage-only-pipeline against // the repo+branch we just built, so coverage and test-quality metrics flow // to novaviz without anyone having to trigger the job manually. // // Gates — skip when triggering would waste compute or pollute the data: // - PR builds (CHANGE_ID set; coverage on PRs is noise) // - hot-fix builds (env.hot_fix == true) // - toolchain image rebuilds (env.INFRA_ENV == 'toolchain') // - explicit opt-out (skip_coverage_trigger: true in config.yaml) // - non-develop branches (current policy: ONLY 'develop' fires. main / // master / gcp-main / gcp-master are // deploy-only branches at Meesho — tests // already ran on the develop merge that // produced their content, so re-running // coverage on them would be duplicate // spend. Expand this allowlist if a repo // ships from a non-develop branch.) // // Failure is swallowed — observability must never block prod CI. The // ai-blitz-jobs Jenkins job being renamed/disabled/missing logs a warning // and continues; the parent build stays green. def call(Map param) { if (env.CHANGE_ID) { log.info("triggerCoverageOnly: skipping PR build (CHANGE_ID=${env.CHANGE_ID}).") return } if (env.hot_fix == 'true' || env.hot_fix == true) { log.info("triggerCoverageOnly: skipping hot-fix build.") return } if ((env.INFRA_ENV ?: '') == 'toolchain') { log.info("triggerCoverageOnly: skipping toolchain (image-rebuild) build.") return } if (param?.skip_coverage_trigger?.toString() == 'true') { log.info("triggerCoverageOnly: skipping — repo opted out via config.yaml skip_coverage_trigger=true.") return } String branch = (env.BRANCH_NAME ?: '').trim() Set allowed = ['develop'] if (!allowed.contains(branch)) { log.info("triggerCoverageOnly: skipping branch '${branch}' (only ${allowed} triggers coverage today).") return } String repoName = param?.repo_name if (!repoName) { log.warning("triggerCoverageOnly: param.repo_name missing — cannot construct REPO_URL. Skipping.") return } // Meesho convention: every backend service lives at github.com/Meesho/. // ai-blitz-jobs' coverage-only.Jenkinsfile takes the full URL as REPO_URL. String repoUrl = "https://github.com/Meesho/${repoName}" // Absolute path. Per-repo CI jobs may live inside Jenkins folders // (e.g. /Meesho/order-service); a relative 'ai-blitz-jobs' would try // the parent folder first and 404 there. The leading slash anchors at // the Jenkins root, where ai-blitz-jobs lives (confirmed by the URL // shape http://jenkins-dev.../job/ai-blitz-jobs//). String targetJob = '/ai-blitz-jobs' try { log.info("triggerCoverageOnly: launching ${targetJob} for ${repoName} @ ${branch} (fire-and-forget).") // The `build` step is in-process Jenkins RPC — no API tokens or // credentials needed. It runs with the parent build's identity // (UpstreamCause), and ai-blitz-jobs' coverage-only.Jenkinsfile // calls coverageOnly() directly (NOT eksCICD), so the // allowedUsers gate in eksCICD doesn't apply to this path. Bot // and upstream-triggered builds both work. // wait:false → parent build queues the downstream and moves on // immediately. It never reads the downstream result, so `propagate` // is a no-op here and intentionally omitted. The try/catch below is // what shields the parent from step-level errors (job not found, // bad params, queue full). build( job: targetJob, parameters: [ string(name: 'REPO_URL', value: repoUrl), string(name: 'BRANCH', value: branch) ], wait: false, quietPeriod: 0 ) } catch (Exception e) { // ai-blitz-jobs renamed/disabled/queue-blocked/whatever — log and // continue. We never block the parent build on observability. log.warning("triggerCoverageOnly: build(job: '${targetJob}', …) threw: ${e}. Continuing — parent CI stays green.") } }