Renames src/com/meesho -> src/com/homelab, resources/com/meesho -> resources/com/homelab, resources/org/meesho -> resources/org/homelab (via git mv, preserving history), and sweeps every remaining occurrence of "meesho" (any casing) out of package declarations, imports, libraryResource() paths, and comments across the whole repo. Also drops the per-user allowlist in vars/eksCICD.groovy, which hardcoded real former-colleagues' emails and doesn't apply to a single-person homelab — that branch is now permanently skipped rather than deleted outright, to avoid hand-editing the escape-sequence-heavy echo blocks it guards (eksCICD.groovy itself is unused legacy code, not called by homelabPipeline.groovy). Does not touch the ~114 files that were already missing from the working tree but still tracked in the prior commit — that's unrelated pre-existing state, left as-is.
196 lines
8.9 KiB
Groovy
196 lines
8.9 KiB
Groovy
// cdHookRunner — standalone Jenkins job that runs turbo-turtle deploy-phase
|
|
// CI/CD hooks (pre_deploy / post_deploy) in an isolated agent pod.
|
|
//
|
|
// turbo-turtle triggers this parameterized job during the CD workflow (see
|
|
// TriggerCdHookPipelineActivity). It checks out the target repo, reads the
|
|
// deploy-phase hooks declared under `environment.<TT_ENV>.hooks.<HOOK_PHASE>` in
|
|
// the per-app deployment file (deployments/<APP_NAME>.yaml), executes them via
|
|
// the shared runHooks execution core, and POSTs an aggregate completion callback
|
|
// to /api/v1/cd/hook/callback which signals the waiting workflow. A blocking hook
|
|
// failure makes success=false so the workflow can fail the deploy; advisory
|
|
// (blocking:false) failures keep success=true.
|
|
//
|
|
// Required parameters (Jenkins job params, read from params/env):
|
|
// REPO_URL, TT_REPO, APP_NAME, BRANCH, COMMIT, TT_ENV, HOOK_PHASE,
|
|
// IMAGE_TAG, TT_IS_HOTFIX, TT_PR_NUMBER, TT_WORKFLOW_ID, TT_RUN_ID
|
|
def call(Map jobParams = [:]) {
|
|
String repoUrl = param('REPO_URL', jobParams)
|
|
String repoName = param('TT_REPO', jobParams)
|
|
String appName = param('APP_NAME', jobParams)
|
|
String branch = param('BRANCH', jobParams)
|
|
String commit = param('COMMIT', jobParams)
|
|
String ttEnv = param('TT_ENV', jobParams)
|
|
String phase = param('HOOK_PHASE', jobParams)
|
|
String workflowID = param('TT_WORKFLOW_ID', jobParams)
|
|
String runID = param('TT_RUN_ID', jobParams)
|
|
|
|
if (!repoUrl && repoName) {
|
|
repoUrl = "https://github.com/Homelab/${repoName}"
|
|
}
|
|
// TT_WORKFLOW_ID / TT_RUN_ID are required: the completion callback correlates
|
|
// to the waiting Temporal run by them. If either is missing (e.g. incomplete
|
|
// job-param registration), fail before checkout/hook execution rather than run
|
|
// hooks whose callback can never be matched (workflow would wait to timeout).
|
|
if (!repoName || !appName || !ttEnv || !phase || !workflowID || !runID) {
|
|
error("cdHookRunner: TT_REPO, APP_NAME, TT_ENV, HOOK_PHASE, TT_WORKFLOW_ID and TT_RUN_ID are required parameters.")
|
|
}
|
|
// Make env available to stageName / the hook context.
|
|
env.cicd_environment = ttEnv
|
|
|
|
String podyaml = "org/homelab/${env.INFRA_ENV ?: 'prd'}-pod.yaml"
|
|
podTemplate(yaml: libraryResource(podyaml)) {
|
|
node(POD_LABEL) {
|
|
container('devops-tools') {
|
|
boolean success = true
|
|
String errMsg = ''
|
|
try {
|
|
stage(stageName("cd-hook checkout: ${repoName}")) {
|
|
deleteDir()
|
|
dir(repoName) {
|
|
checkout([
|
|
$class: 'GitSCM',
|
|
branches: [[name: commit ?: "*/${branch}"]],
|
|
userRemoteConfigs: [[url: repoUrl, credentialsId: 'svc-devops-homelab']],
|
|
extensions: [[$class: 'CloneOption', shallow: false, noTags: false]],
|
|
])
|
|
}
|
|
}
|
|
|
|
def hooks = resolveHooks(repoName, appName, ttEnv, phase)
|
|
if (!hooks) {
|
|
log.info("cdHookRunner: no ${phase} hooks declared for env ${ttEnv} in ${repoName}/deployments/${appName}.yaml — nothing to run.")
|
|
} else {
|
|
List<String> ctxEnv = deployPhaseContextEnv(repoName, ttEnv, jobParams)
|
|
runHooks.executeHooks(repoName, hooks, phase, ctxEnv)
|
|
}
|
|
} catch (Exception e) {
|
|
success = false
|
|
errMsg = e.toString()
|
|
log.error("cdHookRunner: ${phase} hook run failed for ${repoName} (env ${ttEnv}): ${errMsg}")
|
|
} finally {
|
|
postCdHookCallback(repoName, ttEnv, phase, success, errMsg, workflowID, runID)
|
|
}
|
|
if (!success) {
|
|
// Surface as a build failure too (the workflow reads the callback,
|
|
// but a red build aids debugging).
|
|
currentBuild.result = 'FAILURE'
|
|
error("cdHookRunner: ${phase} hooks failed for ${repoName}: ${errMsg}")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// resolveHooks reads the per-app deployment file (deployments/<appName>.yaml) and
|
|
// returns the deploy-phase hook list for env+phase (or null). Deploy-phase hooks
|
|
// live in deployment.yaml, not config.yaml.
|
|
def resolveHooks(String repoName, String appName, String ttEnv, String phase) {
|
|
dir(repoName) {
|
|
String deployFile = "deployments/${appName}.yaml"
|
|
if (!fileExists(deployFile)) {
|
|
error("cdHookRunner: ${deployFile} not found in ${repoName}.")
|
|
}
|
|
def deployment = readYaml file: deployFile
|
|
def envBlock = deployment?.environment?.get(ttEnv)
|
|
def hooksBlock = envBlock?.hooks
|
|
if (!(hooksBlock instanceof Map)) {
|
|
return null
|
|
}
|
|
def hooks = hooksBlock[phase]
|
|
return (hooks instanceof List && !hooks.isEmpty()) ? hooks : null
|
|
}
|
|
}
|
|
|
|
// deployPhaseContextEnv builds the TT_* context contract from the job params.
|
|
def deployPhaseContextEnv(String repoName, String ttEnv, Map jobParams) {
|
|
return [
|
|
"TT_REPO_NAME=${repoName}",
|
|
"TT_ENV=${ttEnv}",
|
|
"TT_EVENT=push",
|
|
"TT_IS_HOTFIX=${param('TT_IS_HOTFIX', jobParams) ?: 'false'}",
|
|
"TT_BRANCH=${param('BRANCH', jobParams) ?: ''}",
|
|
"TT_TARGET_BRANCH=",
|
|
"TT_PR_NUMBER=${param('TT_PR_NUMBER', jobParams) ?: ''}",
|
|
"TT_COMMIT_SHA=${param('COMMIT', jobParams) ?: ''}",
|
|
"TT_IMAGE_TAG=${param('IMAGE_TAG', jobParams) ?: ''}",
|
|
]
|
|
}
|
|
|
|
// postCdHookCallback POSTs the aggregate result to turbo-turtle, which signals
|
|
// the waiting CD workflow. Env-routed exactly like the Jenkins CI callback.
|
|
def postCdHookCallback(String repoName, String ttEnv, String phase, boolean success, String errMsg, String workflowID, String runID) {
|
|
String baseUrl
|
|
switch (ttEnv) {
|
|
case 'prd':
|
|
case 'int':
|
|
baseUrl = 'http://turbo-turtle.homelabgcp.in'
|
|
break
|
|
default:
|
|
baseUrl = 'http://turbo-turtle.admin.homelabgcp.in'
|
|
}
|
|
Map payload = [
|
|
repo_name : repoName,
|
|
env : ttEnv,
|
|
phase : phase,
|
|
success : success,
|
|
error : errMsg,
|
|
build_url : env.BUILD_URL ?: '',
|
|
workflow_id: workflowID,
|
|
run_id : runID,
|
|
]
|
|
String url = baseUrl + "/api/v1/cd/hook/callback"
|
|
String jsonFilePath = "cd_hook_callback_${env.BUILD_NUMBER}.json"
|
|
int maxAttempts = 3
|
|
// The callback is turbo-turtle's ONLY completion signal for the waiting run.
|
|
// Retry delivery (bounded, with connect/request timeouts) and, if every attempt
|
|
// fails, fail the job so the failure is visible — otherwise the job goes green
|
|
// while the workflow waits until its backstop timeout. Duplicate delivery is
|
|
// safe: turbo-turtle correlates callbacks by workflow/run/repo/env/phase.
|
|
try {
|
|
writeFile(file: jsonFilePath, text: writeJSON(returnText: true, json: payload))
|
|
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
boolean delivered = false
|
|
try {
|
|
final def(String response, String code) = sh(
|
|
returnStdout: true,
|
|
script: """
|
|
curl -s --connect-timeout 10 --max-time 30 -X POST \\
|
|
-H 'Content-Type: application/json' \\
|
|
-w '\\n%{response_code}' \\
|
|
$url \\
|
|
-d @$jsonFilePath
|
|
"""
|
|
).trim().tokenize("\n")
|
|
if (code == "200") {
|
|
log.info("cdHookRunner: callback delivered (attempt ${attempt}/${maxAttempts})")
|
|
delivered = true
|
|
} else {
|
|
log.warn("cdHookRunner: callback attempt ${attempt}/${maxAttempts} failed code=${code} response=${response}")
|
|
}
|
|
} catch (Exception e) {
|
|
log.warn("cdHookRunner: callback attempt ${attempt}/${maxAttempts} error: ${e}")
|
|
}
|
|
if (delivered) {
|
|
return
|
|
}
|
|
if (attempt < maxAttempts) {
|
|
sleep(time: attempt * 5, unit: 'SECONDS')
|
|
}
|
|
}
|
|
error("cdHookRunner: callback POST to ${url} failed after ${maxAttempts} attempts; turbo-turtle will not receive completion for repo=${repoName} env=${ttEnv} phase=${phase}")
|
|
} finally {
|
|
sh(script: "rm -f ${jsonFilePath}", returnStatus: true)
|
|
}
|
|
}
|
|
|
|
// param reads a job parameter, preferring an explicit map, then params, then env.
|
|
def param(String key, Map jobParams) {
|
|
if (jobParams?.containsKey(key)) {
|
|
return jobParams[key]?.toString()
|
|
}
|
|
if (params?.containsKey(key) && params[key] != null) {
|
|
return params[key].toString()
|
|
}
|
|
return env[key]?.toString()
|
|
}
|