Rename com.meesho/org.meesho namespace to com.homelab/org.homelab

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.
This commit is contained in:
Mukul Sharma
2026-09-02 01:24:37 +05:30
parent 3419cfba0c
commit 83cbf62ec6
94 changed files with 850 additions and 800 deletions
@@ -0,0 +1,68 @@
package com.homelab.stages
import com.homelab.stages.deployJar
def run(Map config){
// get required variables from config
def automation_repo_name = config.run_automation.repo_name
def branch = config.run_automation.branch
def deployObj = new deployJar()
//deploy service(farmiso) on specified machine
deployObj.run(config)
checkoutAutomationRepo(automation_repo_name, branch)
runAutomationSuite(automation_repo_name)
}
def checkoutAutomationRepo(String automation_repo_name, String branch){
try{
stage('Checkout automation repo'){
sh "rm -rf ${automation_repo_name}; git clone git@github.com:Homelab/${automation_repo_name}.git -b ${branch}"
echo "automation repo cloned"
}
}
catch( Exception e) {
env.msg = "Error cloning automation repo. Please check console output for more details."
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
def runAutomationSuite(String automation_repo_name){
try{
stage('Run Automation Suite'){
dir("$automation_repo_name"){
run_cmd = "mvn test" //specific to Farmiso as of now.
sh "$run_cmd"
}
}
}
catch( Exception e) {
env.msg = "Error running automation suite. Please check console output for more details."
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
finally{
//publish HTML test report
dir("$automation_repo_name"){
archiveArtifacts artifacts: "test-output/*.*"
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: 'test-output',
reportFiles: 'index.html',
reportName: "Farmiso-Test-Automation-Report"
])
}
}
}
+77
View File
@@ -0,0 +1,77 @@
package com.homelab.stages
import com.homelab.utilities.constructTemplate
// Rewritten for this homelab. The original's run() delegated to a
// language-specific build object (buildNode/buildPython/etc.) and its
// release() function drove ArgoCD sync via Ringmaster, Slack, CDN
// invalidation, GCP/AWS branching — none of that applies to one solo app
// on one cluster. Tag bumping in the target Helm chart is its own
// separate stage now (updateHelmTag.groovy) rather than bundled in here.
//
// Kept faithfully: the real buildGo.groovy's fallback rule (line ~156)
// — if the repo has no Dockerfile, render one from a language template;
// if it does, just use it. That fallback now covers go/node/python/
// java/php (matching resources/com/homelab/<lang>-Dockerfile), selected
// via config.dockerBuildVersion — e.g. 'go-1.22', 'node-20',
// 'python-3.12', 'java-21', 'php-8.3'. Simplified versions of the real
// templates: no Homelab private-registry/SSH-keyed module auth, no Sonar,
// no JFrog — see each template's own header comment.
def run(Map config) {
def tag = "${env.BUILD_NUMBER}-${env.GIT_COMMIT?.take(7) ?: 'dev'}"
env.TAG = tag
def image = "harbor-core.harbor.svc.cluster.local/${config.harbor_project}/${config.repo_name}:${tag}"
try {
stage(stageName('Build & push image')) {
container('docker-cli') {
dir("${config.repo_name}") {
if (!fileExists('Dockerfile')) {
renderDockerfile(config)
}
sh "docker build -t ${image} ."
sh "docker push ${image}"
}
}
}
}
catch (Exception e) {
env.msg = 'Error building/pushing image. Error: ' + e.toString()
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
return tag
}
// Only reached when the repo has no Dockerfile of its own. Requires
// config.dockerBuildVersion (e.g. 'go-1.22') — there's no sensible
// default to fall back to if a repo has neither a Dockerfile nor this
// set, unlike every other config key in this pipeline.
def renderDockerfile(Map config) {
if (!config.dockerBuildVersion) {
error("buildDocker: no Dockerfile in repo and config.dockerBuildVersion not set — can't pick a base image. Either add a Dockerfile to the repo, or set dockerBuildVersion (e.g. 'go-1.22') in config.yaml or the Jenkinsfile call.")
}
def parts = config.dockerBuildVersion.split('-', 2)
def lang = parts[0]
def version = parts.size() > 1 ? parts[1] : null
def templates = [
go : ['go-Dockerfile', '1.22'],
node : ['node-Dockerfile', '20'],
python: ['python-Dockerfile', '3.12'],
java : ['java-Dockerfile', '21'],
maven : ['java-Dockerfile', '21'],
php : ['php-Dockerfile', '8.3'],
]
def entry = templates[lang]
if (!entry) {
error("buildDocker: unrecognised language '${lang}' in dockerBuildVersion '${config.dockerBuildVersion}'. Supported: ${templates.keySet().join(', ')}. Or just add a Dockerfile to the repo instead — that always takes priority over this fallback.")
}
def (templateFile, defaultVersion) = entry
def resolvedVersion = version ?: defaultVersion
log.info("buildDocker: no Dockerfile in repo — rendering ${templateFile} for ${lang} ${resolvedVersion}")
def constructObj = new constructTemplate()
constructObj.renderTemplate([version: resolvedVersion], templateFile, 'Dockerfile')
sh 'cat Dockerfile'
}
+412
View File
@@ -0,0 +1,412 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.getDockerParams
import com.homelab.utilities.addSSHKey
import com.homelab.utilities.gitActions
import com.homelab.utilities.constructParam
import com.homelab.utilities.dockerUtilities
def buildDckr(Map config) {
env.GOPRIVATE = 'github.com/Homelab'
def buTeamMappingObj = new buTeamMapping()
def constructObj = new constructTemplate()
def dockerParamObj = new getDockerParams()
def addSshKeyObj = new addSSHKey()
def dockerUtilObj = new dockerUtilities()
def team = buTeamMappingObj.get_team_initials(config.team)
def modules = config.modules ?: ['module_less']
def repoName = config.repo_name
def dockerRepository = "${env.cicd_environment}/${team}/${repoName.toLowerCase()}"
def tag = dockerParamObj.getTag(repoName)
def dockerBindings = [:]
def version = config.dockerBuildVersion.split('-')[-1]
def constructParamObj = new constructParam()
def repoType = config.repo_type ?: 'microservice'
boolean skipSonarAndQualityGate = constructParamObj.skipSonarCheckForGo(config)
echo "skipSonarAndQualityGate: ${skipSonarAndQualityGate}"
String goVersion = config.goVersion ?: version
boolean shouldDeployArgo = (config.deployArgo ?: false).toBoolean()
boolean isConfigOnlyChange = false
// Ensure goVersion is in x.x.x format
echo "goVersion: ${goVersion}"
if (goVersion.split('\\.').size() == 2) {
goVersion += '.0'
}
if (env.INFRA_ENV == 'toolchain') {
tag = dockerParamObj.getTag(repoName)
boolean allImagesExist = true
for (module in modules) {
def moduleName = (module instanceof LinkedHashMap) ? module.keySet()[0] : module
def modulePath = (moduleName == 'module_less') ? dockerRepository : "${dockerRepository}/${moduleName}"
//TODO: building everything if one is missing -> create list of modules unbuild will only build those
if (!dockerUtilObj.imageExists(env.registry, modulePath, tag)) {
log.info("Toolchain: Image missing for ${moduleName} at ${modulePath}:${tag}. Proceeding with build.")
allImagesExist = false
break
}
}
if (allImagesExist) {
log.info("Toolchain: All images found in registry for tag ${tag}. Skipping build step.")
return [tag, shouldDeployArgo]
}
}
stage(stageName('Check Config Changes and Get Latest Image Tag')) {
(isConfigOnlyChange, shouldDeployArgo) = is_config_only_change_and_should_deploy_argo(repoName, shouldDeployArgo)
log.info("Is just application config change? $isConfigOnlyChange")
def param = new constructParam()
if (env.CHANGE_ID && repoType == 'microservice') {
def shouldValidateConfig = param.ValidateCacConfigForRepo(false, repoName)
if (shouldValidateConfig) {
log.info('************ Validate Config for CAC application.yml files ************')
dir("$repoName") {
writeFile file: 'validate_configs.py', text: libraryResource('com/homelab/validate_configs_v2.py')
sh 'python3 validate_configs.py'
}
} else {
log.info('************ Skipping Validation of CAC Config ************')
}
} else {
log.info("Skipping Validation of CAC Config for ${repoType} repo type")
log.info('************ Skipping Validation of CAC Config ************')
}
if (isConfigOnlyChange) {
log.info('Skipping the build, since it is just application config change')
log.info('Getting latest image tag from GAR')
def firstModule = modules[0]
def firstModuleName = (firstModule instanceof LinkedHashMap) ? firstModule.keySet()[0] : firstModule
def moduleDockerRepository = (firstModuleName == 'module_less') ? dockerRepository : "${dockerRepository}/${firstModuleName}"
try {
def latest_image = sh(
script: "gcloud container images list-tags ${env.registry}/${moduleDockerRepository} --format='value(tags)' | sed '/^\$/d' | awk -F'-' '{print \$NF}' | sort | tail -1",
returnStdout: true
).trim()
echo "latest_image: ${latest_image}"
if (latest_image == '') {
log.info("No existing images found for ${env.registry}/${moduleDockerRepository}. Using generated tag: ${tag}")
} else {
tag = sh(
script: "gcloud container images list-tags ${env.registry}/${moduleDockerRepository} --format='value(tags)' | tr ',' '\n' | grep ${latest_image}",
returnStdout: true
).trim()
}
} catch (Exception e) {
env.msg = 'Error getting latest docker image from GAR'
env.error_msg_to_db = env.msg
log.error("${env.msg}\n${e.toString()}")
currentBuild.result = env.FAILURE
throw e
}
return [tag, shouldDeployArgo]
}
log.info('Proceeding with the build')
}
stage(stageName('Scanning Sonar and Quality Gate')) {
dir(repoName) {
sonar_scan(repoName, skipSonarAndQualityGate, goVersion)
}
}
if (currentBuild.result == 'UNSTABLE')
{ return [tag, false] }
if (repoType != 'microservice') {
log.info("Skipping Docker build for ${repoType} repo type")
return [tag, shouldDeployArgo]
}
stage(stageName('Building docker images')) {
try {
sh(script: 'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
} catch (Exception e) {
env.msg = "[Failure] Can't login to gcloud docker registry."
env.error_msg_to_db = env.msg
log.error("${env.msg}. Error: ${e.toString()}")
currentBuild.result = env.FAILURE
throw e
}
dockerBindings['version'] = (version == 'go') ? '1.24.4' : version
dockerBindings['base_dir'] = config.base_dir ?: false
dockerBindings['build_registry'] = env.buildRegistry
dockerBindings['go_proxy'] = env.goProxyUrl
dockerBindings['repo_name'] = repoName
if (config.containsKey('copy_file')) {
String recursive = config.copy_file.recursive ? ' -r' : ''
dir(repoName) {
dir('copied_files') {
sh(script: "gsutil cp${recursive} ${config.copy_file.path} .")
}
}
dockerBindings['copy_file'] = true
dockerBindings['copy_target'] = config.copy_file.target ?: '/app/'
dockerBindings['base_dir'] = config.base_dir ?: false
} else {
dockerBindings['copy_file'] = false
}
try {
dir(repoName) {
addSshKeyObj.create()
if (!fileExists('Dockerfile')) {
def moduleBuilds = [:]
for (m in modules) {
def moduleRef = m
def moduleName = (moduleRef instanceof LinkedHashMap) ? moduleRef.keySet()[0] : moduleRef
moduleBuilds["build-${moduleName}"] = {
def localBindings = dockerBindings.clone()
localBindings['module_property'] = (moduleRef instanceof LinkedHashMap) ? moduleRef[moduleName] : [:]
localBindings['kafka'] = (localBindings['module_property'].kafka) ? '-kafka' : ''
localBindings['module'] = moduleName
constructObj.renderTemplate(localBindings, 'go-Dockerfile', "Dockerfile-${moduleName}")
sh "cat Dockerfile-${moduleName}"
def moduleDockerRepository = (moduleRef == 'module_less') ? dockerRepository : "${dockerRepository}/${moduleName}"
sh(script: 'tar -cf only-mods.tar $(git ls-files "go.mod" "go.sum" "**/go.mod" "**/go.sum" 2>/dev/null || find . -name go.mod -o -name go.sum)')
sh(script: "set +x && docker build --tag ${env.registry}/${moduleDockerRepository}:${tag} -f Dockerfile-${moduleName} .")
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
dockerUtilObj.retryDockerPush("docker push ${env.registry}/${moduleDockerRepository}:${tag}")
} else {
log.info("Skipping Docker Push - ${env.cicd_environment} env")
}
sh(script: "rm -rf Dockerfile-${moduleName}")
}
}
parallel moduleBuilds
} else {
sh 'cat Dockerfile'
def moduleDockerRepository = dockerRepository // default for repo root
if (modules && modules[0] != 'module_less') {
def firstModule = modules[0]
def firstModuleName = (firstModule instanceof LinkedHashMap) ? firstModule.keySet()[0] : firstModule
moduleDockerRepository = dockerRepository + '/' + firstModuleName
}
def pushOrNot = (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') ? '--push' : ''
if (!pushOrNot) {
log.info("Skipping Docker Push - ${env.cicd_environment} env")
}
sh(script: "docker build --tag ${env.registry}/${moduleDockerRepository}:${tag} ${pushOrNot} .")
}
}
} catch (Exception e) {
env.msg = 'Error in building DockerFile Or Pushing To ECR'
env.error_msg_to_db = env.msg
log.error("${env.msg}. Error: ${e.toString()}")
currentBuild.result = env.FAILURE
throw e
}
return [tag, shouldDeployArgo]
}
}
def is_config_only_change_and_should_deploy_argo(String repoName, boolean deployArgo) {
if (env.INFRA_ENV == 'toolchain') {
return [false, deployArgo]
}
def gitObj = new gitActions()
def configFiles = []
boolean shouldDeployArgoResult = deployArgo
boolean isConfigOnlyChange = false
def changedFiles = env.CHANGE_ID ?
gitObj.fetchDiffFilesForPullRequest(repoName, env.CHANGE_TARGET) :
gitObj.fetchDiffFilesForPushRequest(repoName)
if (!changedFiles) {
return [isConfigOnlyChange, shouldDeployArgoResult]
}
for (file in changedFiles.split('\n')) {
if (!file.startsWith('configs/')) {
isConfigOnlyChange = false
return [isConfigOnlyChange, shouldDeployArgoResult]
}
isConfigOnlyChange = true
configFiles << file
}
if (configFiles && deployArgo && isConfigOnlyChange) {
def hasEnvironmentConfig = configFiles.any { it.contains(env.cicd_environment) }
if (!hasEnvironmentConfig) {
shouldDeployArgoResult = false
echo "Config changes don't contain environment: ${env.cicd_environment}"
}
}
return [isConfigOnlyChange, shouldDeployArgoResult]
}
/**
* Function to execute sonar scan
* @param repoName: repository name
* @param skipSonarAndQualityGate: boolean parameter to skip sonar scan
* @param goVersion: Go version string
*/
def sonar_scan(String repoName, boolean skipSonarAndQualityGate, String goVersion) {
if (skipSonarAndQualityGate) {
log.info('Sonar scan is skipped. Marking this stage as passed.')
return
}
stage(stageName('Running sonar scan')) {
try {
withSonarQubeEnv(env.sonarEnv) {
echo "env.sonarEnv: ${env.sonarEnv}"
echo "env.BRANCH_NAME: ${env.BRANCH_NAME}"
def exclusions = ""
if (!fileExists('sonar-project.properties')) {
exclusions = " -Dsonar.exclusions=**/*_test.go,**/mock_*.go,**/mock.go,**/*.pb.go,**/*.proto,**/model.go"
}else{
exclusions = " -Dproject.settings=`pwd`/sonar-project.properties"
}
def scannerCommand = "sonar-scanner -Dsonar.projectKey=${repoName} -Dsonar.go.coverage.reportPaths=./cov.out -Dsonar.branch.name=${env.BRANCH_NAME} -Dsonar.ws.timeout=120 ${exclusions}"
echo "env.SONAR_HOST_URL: ${env.SONAR_HOST_URL}"
env.PATH = "/usr/local/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin:${env.PATH}"
def response = sh(
script: "curl -s -w '\n%{http_code}' -u ${env.SONAR_AUTH_TOKEN}: ${env.SONAR_HOST_URL}/api/navigation/component?component=${repoName}",
returnStdout: true
).trim()
def responseLines = response.split('\n')
def statusCode = responseLines[-1]
def responseBody = responseLines[0..-2].join('\n')
log.info("SonarQube API Response Code: ${statusCode}")
log.info("SonarQube API Response Body: ${responseBody}")
def projectExists = (statusCode == '200')
if (!projectExists && env.CHANGE_ID) {
log.info("Project ${repoName} does not exist. Creating it via a bootstrap scan on the target branch.")
def targetBranch = env.CHANGE_TARGET ?: 'develop'
echo "targetBranch: ${targetBranch}"
sh "sonar-scanner \
-Dsonar.projectKey=${repoName} \
-Dsonar.projectName=${repoName} \
-Dsonar.branch.name=${targetBranch} \
-Dsonar.sources=. \
-Dsonar.scm.disabled=true \
-Dsonar.ws.timeout=120"
log.info("Bootstrap complete. Project created.")
}else{
echo "Project ${repoName} already exists. Skipping bootstrap scan."
}
if (env.CHANGE_ID) {
echo "env.CHANGE_ID: ${env.CHANGE_ID}"
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
}
scannerCommand = "sonar-scanner -Dsonar.projectKey=${repoName} -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repoName} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.go.coverage.reportPaths=./cov.out -Dsonar.ws.timeout=120${exclusions}"
echo "scannerCommand: ${scannerCommand}"
}
downloadGoFromJFrog(goVersion)
sh(script: "GOPROXY=${env.goProxyUrl},direct && go mod tidy")
int testExitCode = sh(script: 'go test -short -coverprofile=./cov.out ./...', returnStatus: true)
if (testExitCode != 0) {
error('Go tests failed.')
}
echo "scannerCommand: ${scannerCommand}"
sh(script: scannerCommand)
}
} catch (Exception e) {
log.error("Error in running sonar scan: ${e}")
currentBuild.result = env.FAILURE
throw e
}
}
stage(stageName('Quality Gate')) {
try {
if (!env.CHANGE_ID || env.cicd_environment == 'int') {
log.info('Skipping quality gate check on Branches/Pre-Prod. Marking this stage as passed.')
} else {
timeout(time: 600, unit: 'SECONDS') {
def qg = waitForQualityGate()
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
if (qg.status != 'OK') {
log.warning("Quality gate failed: ${qg.status}")
error "Stopping pipeline due to quality gate failure."
}
}
}
}
}catch (Exception e) {
echo "Error in quality gate: ${e.message}"
unstable('Quality Gate Failed !')
throw e
}
}
}
/**
* Function to download Go binary from JFrog
* @param goVersion: Go version string
*/
def downloadGoFromJFrog(String goVersion) {
try {
echo "env.cicd_environment: ${env.cicd_environment}"
def jfrogUrl = (env.cicd_environment == 'prd' || env.cicd_environment == 'int') ?
'https://jfrog-prd.homelabgcp.in' :
'https://jfrog-dev.homelabgcp.in'
def repo = 'devops-tools-local'
def goTarball = "go${goVersion}.linux-amd64.tar.gz"
def jfrogPath = "${jfrogUrl}/artifactory/${repo}/go/${goTarball}"
def jfrogUser = ''
def jfrogPass = ''
def credentialId = (env.cicd_environment == 'prd' || env.cicd_environment == 'int') ?
'jfrog-prd-credentials' : 'jfrog-stg-credentials'
echo "Using credential ID: ${credentialId}"
withCredentials([usernamePassword(credentialsId: credentialId,
usernameVariable: 'JFROG_USER',
passwordVariable: 'JFROG_PASS')]) {
jfrogUser = env.JFROG_USER
jfrogPass = env.JFROG_PASS
log.info("Attempting to download Go ${goVersion} from JFrog: ${jfrogPath}")
def downloadStatus = sh(script: """
curl -u "${env.JFROG_USER}:${env.JFROG_PASS}" \
-fLO "${jfrogPath}" \
--fail --silent --show-error
""", returnStatus: true)
if (downloadStatus == 0 && fileExists(goTarball)) {
log.info("Successfully downloaded Go ${goVersion} from JFrog")
} else {
echo("WARN: Go ${goVersion} not found in JFrog. Falling back to go.dev...")
sh(script: "curl -LO https://go.dev/dl/${goTarball}")
log.info("Uploading downloaded Go ${goVersion} to JFrog for future use")
sh(script: """
curl -u "${env.JFROG_USER}:${env.JFROG_PASS}" \
-T "${goTarball}" \
"${jfrogPath}" \
--fail --silent --show-error || echo "Upload to JFrog failed, but continuing..."
""")
}
}
sh(script: "tar -xvzf ${goTarball} -C /usr/local", returnStdout: true)
sh(script: "rm -Rf ${goTarball}")
env.PATH = "/usr/local/go/bin:${env.PATH}"
// Verify installation
sh(script: "go version")
} catch (Exception e) {
echo("ERROR: Failed to download Go from JFrog: ${e.message}")
// Final fallback
sh(script: "curl -LO https://go.dev/dl/go${goVersion}.linux-amd64.tar.gz")
sh(script: "tar -xvzf go${goVersion}.linux-amd64.tar.gz -C /usr/local", returnStdout: true)
sh(script: "rm -Rf go${goVersion}.linux-amd64.tar.gz")
env.PATH = "/usr/local/go/bin:${env.PATH}"
}
}
+542
View File
@@ -0,0 +1,542 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.getDockerParams
import com.homelab.stages.checkOut
/*
Function to define the flow of entire build, this function will call different stages related to maven build
*/
def run(Map config) {
// get required variables from config
def checkoutObj = new checkOut()
def repo_name = config.repo_name
def args = config.build_args ?: ''
def skip_test = config.skip_test ?: false
def skip_sonar = config.skip_sonar ?: false
def push_to_jfrog = config.push_to_jfrog ?: false
def push_to_s3 = config.push_to_s3 ?: false
def branch_name = "${env.BRANCH_NAME}"
if (branch_name == 'gcp-main' || branch_name == 'gcp-master') {
push_to_jfrog = config.containsKey('push_to_jfrog') ? config.push_to_jfrog : false
}
def version = getVersion("${repo_name}")
if (env.hot_fix) {
skip_test = true
skip_sonar = true
push_to_jfrog = false
}
// call the stages
checkS3(repo_name, branch_name, version, push_to_s3)
build(repo_name, skip_test, args)
sonar_scan(repo_name, skip_sonar)
if (!env.CHANGE_ID) {
pushArtifactToJFrog(repo_name, push_to_jfrog)
pushArtifactToS3(repo_name, branch_name, push_to_s3)
}
else {
log.info('Artifact Push is disabled for Pull Requests')
}
}
/*
Function to check if the artifact exist in s3 or not
input argument
*/
def checkS3(String repo_name, String branch_name, String version, boolean push_to_s3) {
stage(stageName('Checking if artifact already exists')) {
if (env.CLOUD_PROVIDER == 'AWS') {
if (env.hot_fix) {
env.TAG = "v${version}-HOT"
}
else {
if (("${branch_name}" == 'master' || "${branch_name}" == 'main' || push_to_s3) && !env.CHANGE_ID) {
env.TAG = "v${version}"
log.info('########################## Checking if Artifact Already Exists. ###########################')
artifact_exists = sh(returnStdout: true, script: "aws s3 ls \"s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/\" 2>/dev/null || echo ''").trim()
log.info("${artifact_exists}")
if ( artifact_exists ) {
env.msg = "CI for this\n version - ${version}\n branch - ${branch_name}\n TAG - ${TAG}\nis already done. Please proceed with CD."
log.error(env.msg)
currentBuild.result = 'FAILURE'
throw new Exception(env.msg)
}
else {
log.info("Proceeding with building artifact for TAG - ${TAG}.")
}
}
else {
log.info('Skipping - Building artifact')
}
}
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if (env.hot_fix) {
env.TAG = "v${version}-HOT"
}
else {
if (("${branch_name}" == 'master' || "${branch_name}" == 'main' || "${branch_name}" == 'gcp-main' || "${branch_name}" == 'gcp-master' || push_to_s3) && !env.CHANGE_ID) {
env.TAG = "v${version}"
log.info('########################## Checking if Artifact Already Exists. ###########################')
artifact_exists = sh(returnStdout: true, script: "gsutil ls \"gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/\" 2>/dev/null || echo ''").trim()
log.info("${artifact_exists}")
if ( artifact_exists ) {
env.msg = "CI for this\n version - ${version}\n branch - ${branch_name}\n TAG - ${TAG}\nis already done. Please proceed with CD."
log.error(env.msg)
currentBuild.result = 'FAILURE'
throw new Exception(env.msg)
}
else {
log.info("Proceeding with building artifact for TAG - ${TAG}.")
}
}
else {
log.info('Skipping - Building artifact.')
}
}
}
}
}
/*
Function to build the maven package
input arguments:
repo_name: repository name for changing dirctory
skip_test: boolen value to skip unit tests
skip_sonar: boolen value to skip sonar quality gate
args: string parameter to provide additional arguments to build cmd, example: '-U'
*/
def build(String repo_name, boolean skip_test, String args) {
try {
if (env.CLOUD_PROVIDER == 'AWS') {
stage(stageName('Building gradle package')) {
String build_cmd = 'gradle clean build'
dir("$repo_name") {
sh(script:"cp ~/.m2/settings.xml .;${build_cmd} ${args}")
}
}
}
else if (env.CLOUD_PROVIDER == 'GCP') {
stage(stageName('Building gradle package')) {
String build_cmd = 'gradle clean build'
dir("$repo_name") {
sh(script:"gradle -v;cp ~/.m2/settings.xml .;${build_cmd} ${args}")
}
}
}
}
catch ( Exception e) {
env.msg = "Error Building the maven package. Please check console output for more details - ${e}"
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
/*
Fuction to execute sonar scan
Input Arguments:
repo_name: repository name
skip_sonar: boolena parameter to skip sonar scan
*/
def sonar_scan(String repo_name, boolean skip_sonar) {
try {
stage(stageName('Running sonar scan')) {
if (!skip_sonar) {
dir("$repo_name") {
withSonarQubeEnv(env.sonarEnv) {
if (env.CHANGE_ID) {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "gradle sonar:sonar -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script: "gradle sonar:sonar -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET}")
}
}
else {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "gradle sonar:sonar -Dsonar.branch.name=${env.BRANCH_NAME}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script: "gradle sonar:sonar -Dsonar.branch.name=${env.BRANCH_NAME}")
}
}
}
}
}
else {
log.info('Skipping - Sonar Scan')
}
}
// if (!skip_sonar) {
// stage('Quality Gate') {
// timeout(time: 300, unit: 'SECONDS') {
// def qg = waitForQualityGate()
// catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
// if (qg.status != 'OK') {
// error "stage failed due to quality gate failure: ${qg.status}"
// }
// }
// }
// }
// }
}
catch (Exception e) {
dir("$repo_name") {
withSonarQubeEnv(env.sonarEnv) {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: 'gradle sonar:sonar')
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script: 'gradle sonar:sonar')
}
}
}
}
}
/*
Function to get the version from build.gradle
input arguments:
repo_name: String parameter to change the directory where build.gradle is located
*/
def getVersion(String repo_name) {
try {
dir("$repo_name") {
return sh(returnStdout: true, script: "grep version build.gradle | head -1 | cut -d \"'\" -f2").trim()
}
}
catch ( Exception e) {
env.msg = 'Error while getting the version from build.gradle . Please check console output for more details.'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
/*
Function to get the modules from build.gradle
input arguments:
repo_name: String parameter to change the directory where build.gradle is located
*/
def getModules(String repo_name) {
try {
dir("$repo_name") {
modules = sh(returnStdout: true, script: 'xq -r .project.modules.module[] build.gradle 2>/dev/null || xq -r .project.modules.module build.gradle 2>/dev/null || echo empty').trim()
if (modules == 'empty' || modules == 'null') {
modules = 'module_less'
}
modules = modules.split('\n') as List
return modules
}
}
catch ( Exception e) {
env.msg = 'Error getting the modules from build.gradle . Please check console output for more details.'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
/*
Function to push artifacts to jfrog artifactory
input arguments:
repo_name: repository name
push_to_jfrog: boolen argument to push_to_jfrog
*/
def pushArtifactToJFrog(String repo_name, boolean push_to_jfrog) {
try {
stage(stageName('Deploying to JFrog')) {
if (env.CLOUD_PROVIDER == 'AWS') {
branch_name = 'repo'
if (branch_name == 'master' || branch_name == 'main' || push_to_jfrog) {
dir("${repo_name}") {
profiles = sh(returnStdout:true, script: 'xq -r .project.profiles build.gradle ').trim()
block_dist = sh(returnStdout:true, script: 'xq -r .project.distributionManagement build.gradle ').trim()
// Check for profiles tag in the build.gradle
if (profiles != 'null') {
// Check if profiles has distiributionManagement defined
profile_dist = sh(returnStdout:true, script: 'xq -r .project.profiles.profile[].distributionManagement build.gradle 2>/dev/null || xq -r .project.profiles.profile.distributionManagement build.gradle 2>/dev/null || echo null').trim()
if (profile_dist != 'null' ) {
publish_repo = (branch_name == 'master' || branch_name == 'main' || branch_name == 'gcp-main' || branch_name == 'gcp-master') ? 'useProdRepo' : 'useTestRepo'
log.info('########################### Pushing artifact to Jfrog. ###########################')
sh "gradle package deploy -DskipTests=true -D${publish_repo}=true"
}
else {
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
}
}
else if (block_dist != 'null') {
// Check if distributionManagement is defined without profiles
log.info('########################### Pushing artifact to Jfrog. ###########################')
sh 'gradle package deploy -DskipTests=true'
}
else {
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
}
}
}
else {
log.info('########################### Skipping - Push to Jfrog Artifactory. ###########################')
}
}
else if (env.CLOUD_PROVIDER == 'GCP') {
branch_name = 'repo'
if (branch_name == 'master' || branch_name == 'main' || push_to_jfrog) {
dir("${repo_name}") {
profiles = sh(returnStdout:true, script: 'xq -r .project.profiles build.gradle ').trim()
block_dist = sh(returnStdout:true, script: 'xq -r .project.distributionManagement build.gradle ').trim()
// Check for profiles tag in the build.gradle
if (profiles != 'null') {
// Check if profiles has distiributionManagement defined
profile_dist = sh(returnStdout:true, script: 'xq -r .project.profiles.profile[].distributionManagement build.gradle 2>/dev/null || xq -r .project.profiles.profile.distributionManagement build.gradle 2>/dev/null || echo null').trim()
if (profile_dist != 'null' ) {
publish_repo = (branch_name == 'master' || branch_name == 'main' || branch_name == 'gcp-main' || branch_name == 'gcp-master') ? 'useProdRepo' : 'useTestRepo'
log.info('########################### Pushing artifact to Jfrog. ###########################')
sh "gradle package deploy -DskipTests=true -D${publish_repo}=true"
echo 'execute gradle dependency'
sh 'gradle dependency:list > dependency_tree.txt'
echo ' uploading gradle dependency'
sh 'ls -ltr'
echo 'lets run copy command'
try {
sh "gsutil cp dependency_tree.txt 'gs://${env.objBucket}/common-dependencies/${repo_name}/'"
}
catch ( Exception e ) {
echo "Skipping - copying txt file to GCS - ${e}"
}
echo 'uploading gradle dependency'
}
else {
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
}
}
else if (block_dist != 'null') {
// Check if distributionManagement is defined without profiles
log.info('########################### Pushing artifact to Jfrog. ###########################')
sh 'gradle package deploy -DskipTests=true'
}
else {
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
}
}
}
else {
log.info('########################### Skipping - Push to Jfrog Artifactory. ###########################')
}
}
}
}
catch ( Exception e ) {
env.msg = 'Error in pushing artifacts to jfrog . Please check console output for more details.'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
/*
Function to push artifacts to s3
input arguments:
repo_name: repository name
push_to_s3: boolen argument to push to s3
*/
def pushArtifactToS3(String repo_name, String branch_name, boolean push_to_s3) {
try {
stage(stageName('Pushing artifacts to object storage')) {
if (env.CLOUD_PROVIDER == 'AWS') {
if ("${branch_name}" == 'master' || "${branch_name}" == 'main' || push_to_s3) {
def modules = getModules("${repo_name}")
log.info('}########################### Pushing artifacts to S3. ###########################')
j = 0
for (module in modules) {
j += 1
sh """
echo "${j}. ${module}"
if [ -f ${repo_name}/${module}/target/*.jar ]
then
ls -al ${repo_name}/${module}/target/*.jar
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
aws s3 cp ${repo_name}/${module}/target/*.jar "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
echo "Listing ${TAG} artifacts -"
aws s3 ls "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
elif [ -f ${repo_name}/target/*.jar ] && [ ${module} = 'module_less' ]
then
ls -al ${repo_name}/target/*.jar
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
aws s3 cp ${repo_name}/target/*.jar "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
echo "Uploading Archived Code to - ${repo_name}/${branch_name}/${TAG}"
echo "Listing ${TAG} artifacts -"
aws s3 ls "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
else
echo "No jar file found."
fi
"""
}
log.info("TAG for CD - ${TAG}")
}
else {
log.info("Skipping - Artifact push. As it is not supported for ${env.BRANCH_NAME}")
}
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if ("${branch_name}" == 'master' || "${branch_name}" == 'main' || "${branch_name}" == 'gcp-main' || branch_name == 'gcp-master' || push_to_s3) {
def modules = getModules("${repo_name}")
log.info('}########################### Pushing artifacts to S3. ###########################')
j = 0
for (module in modules) {
j += 1
sh """
echo "${j}. ${module}"
if [ -f ${repo_name}/${module}/target/*.jar ]
then
ls -al ${repo_name}/${module}/target/*.jar
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
gsutil cp ${repo_name}/${module}/target/*.jar "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
echo "Listing ${TAG} artifacts -"
gsutil ls "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
elif [ -f ${repo_name}/target/*.jar ] && [ ${module} = 'module_less' ]
then
ls -al ${repo_name}/target/*.jar
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
gsutil cp ${repo_name}/target/*.jar "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
echo "Uploading Archived Code to - ${repo_name}/${branch_name}/${TAG}"
echo "Listing ${TAG} artifacts -"
gsutil ls "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
else
echo "No jar file found."
fi
"""
}
log.info("TAG for CD - ${TAG}")
}
else {
log.info("Skipping - Artifact push. As it is not supported for ${env.BRANCH_NAME}")
}
}
}
}
catch ( Exception e ) {
env.msg = "Error in pushing artifacts to s3 bucket. Please check console output for more details - ${e}"
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
/*
Fuction for java version
*/
def getDockerBuildVersion(String java_version) {
switch (java_version) {
case 'gradle': return '8-jdk-slim-secure-multiarch_v3.0'
}
}
/*
Fuction to build maven docker repo
*/
def buildDckr(Map config) {
def btObj = new buTeamMapping()
def dparam_obj = new getDockerParams()
def constructObj = new constructTemplate()
def team = btObj.get_team_initials(config.team)
def repo_name = config.repo_name
def tag = dparam_obj.getTag(repo_name)
def skip_test = config.skip_test ?: false
def modules = getModules("${repo_name}")
def excludedModules = config.excludedModules ?: []
def deployArgo = config.deployArgo ?: false
def repoType = config.repo_type ?: 'microservice'
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
env.JAVA_HOME = '/usr/lib/jvm/java-8-openjdk-amd64/'
env.PATH = "/opt/gradle/gradle-5.6.1/bin:${env.PATH}"
def docker_bindings = [
'repo_name': repo_name,
'buildRegistry': env.buildRegistry
]
if (config.containsKey('copy_file')) {
// if (env.CLOUD_PROVIDER == "AWS"){
// def recursive = config.copy_file.recursive ? ' --recursive' : ''
// }
// else if (env.CLOUD_PROVIDER == "GCP"){
// def recursive = config.copy_file.recursive ? ' -r' : ''
// }
def recursive = config.copy_file.recursive ? ' --recursive' : ''
dir(repo_name) {
dir('copied_files') {
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
}
}
docker_bindings['copy_file'] = true
docker_bindings['copy_target'] = config.copy_file.target ?: '/opt/target'
}
else {
docker_bindings['copy_file'] = false
}
if (modules != 'module_less') {
excludedModules.each { modules.removeElement(it) }
}
else {
modules = ['module_less']
}
docker_bindings['arch'] = config.arch
def java_version = config.dockerBuildVersion
dockerBuildVersion = getDockerBuildVersion(java_version)
docker_bindings['dockerBuildVersion'] = dockerBuildVersion
run(config)
if (docker_bindings.copy_file) {
dir(repo_name) {
dir('copied_files') {
if (env.CLOUD_PROVIDER == 'AWS') {
// delete any old data
sh(script:'rm -rf *')
sh(script:"aws s3 cp${docker_bindings.recursive} ${docker_bindings.copy_file_path} .")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
// delete any old data
docker_bindings['recursive'] = '-r'
sh(script:'rm -rf *')
sh(script:"gsutil cp ${docker_bindings.recursive} ${docker_bindings.copy_file_path}* .")
}
}
}
}
stage(stageName('Building docker images')) {
// Login to docker
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
}
dir(repo_name) {
for (module in modules) {
docker_bindings['module'] = module
constructObj.renderTemplate(docker_bindings,'java-Dockerfile','Dockerfile-' + module)
def module_repo = (module == 'module_less') ? docker_repo : docker_repo + '/' + module
if ( module == 'module_less') {
sh(script: 'mkdir target;cp build/libs/*jar target')
}
else {
sh(script: "mkdir -p ${module}/target;cp build/libs/*jar ${module}/target")
}
if (env.cicd_environment != 'ftr' && repoType == 'microservice') {
sh(script: "ls target; docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module} . && docker push ${env.registry}/${module_repo}:${tag}")
}
else {
log.info("Skipping Docker Build - ${env.cicd_environment} env")
log.info("Skipping Docker Build - ${repoType} repo type")
}
//Remove dockerfile
sh(script: "rm -rf Dockerfile-${module}")
}
}
return [tag , deployArgo]
}
}
File diff suppressed because it is too large Load Diff
+568
View File
@@ -0,0 +1,568 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.getDockerParams
import com.homelab.utilities.addSSHKey
import com.homelab.utilities.dockerUtilities
import com.homelab.utilities.getYamlParameter
def removePackageLock() {
sh(script: 'rm -rf package-lock.json')
}
def buildNode(def build_cmd) {
try {
if (fileExists('package-lock.json')) {
sh(script: 'npm ci')
}
else {
sh(script: 'npm install')
}
sh(script: build_cmd)
}
catch ( Exception e ) {
env.msg = 'Error in building node packages . Please check console output for more details.'
env.error_msg_to_db = 'Error Building Node Packages'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
def getArtifactId(String repo_name) {
dir("$repo_name") {
if (fileExists('package.json')) {
return sh(returnStdout: true, script: 'jq -r .name package.json').trim()
}
}
}
def getAwsSecret(def secret_name, def destination_file, def team, def bu) {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws secretsmanager get-secret-value --secret-id ${secret_name} --query SecretString --output text > ${destination_file}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${secret_name} | jq -r .data.data > ${destination_file}")
}
}
def getNpmRc(def secret_name, def team, def bu) {
def npmrc_file = secret_name + '-npmrc-' + env.cicd_environment
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws secretsmanager get-secret-value --secret-id ${npmrc_file} --query SecretString --output text| jq -r .HOMELAB_NPMRC_SECRET > .npmrc")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${npmrc_file} | jq -r .data.data.HOMELAB_NPMRC_SECRET > .npmrc")
}
}
def getPemFile(def secret_name, def team, def bu) {
def pem_secret_name = secret_name + '-secrets-' + env.cicd_environment
if (env.BUILD_ENV == 'stage') {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_dev > 1_public_secret_dev.pem")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${pem_secret_name} | jq -r .data.data.public_secret_dev > 1_public_secret_dev.pem")
}
sh(script:"cat 1_public_secret_dev.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_dev.pem")
}
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_prod > 1_public_secret_prod.pem")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${pem_secret_name} | jq -r .data.data.public_secret_prod > 1_public_secret_prod.pem")
}
sh(script:"cat 1_public_secret_prod.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_prod.pem")
}
def getEnvFile(def secret_name, def team, def bu, boolean useCacPath = false) {
def env_file = secret_name + '-env-' + env.cicd_environment
def gcp_env_file = secret_name
def destination_file = '.env'
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws secretsmanager get-secret-value --secret-id ${env_file} | jq --raw-output '.SecretString' | jq '.' | jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' > .env")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
def vault_path = useCacPath
? "homelab/${env.cicd_environment}-cac/${bu}/${team}/${gcp_env_file}-client"
: "homelab/${env.cicd_environment}/${bu}/${team}/${gcp_env_file}"
getVaultSecret("vault kv get -format=json ${vault_path} | jq -r '.data.data | to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' > .env")
}
}
/**
* Read appConfigEnabled from deployment yaml(s) in repo (deployments/<name>.yaml).
* Uses config.deployment_order for deployment names, or repo_name if not set.
* Aligns with deploy/Helm which reads appConfigEnabled from the same files.
*/
def isAppConfigEnabledFromDeployments(String repo_name, Map config) {
def deploymentNames = (config.deployment_order instanceof List && !config.deployment_order.isEmpty())
? config.deployment_order
: [repo_name]
def yamlObj = new getYamlParameter()
for (def deployment in deploymentNames) {
try {
def depYaml = yamlObj.getParam(repo_name, "deployments/${deployment}.yaml")
def enabled = depYaml?.appConfigEnabled
if (enabled == true || enabled?.toString()?.equalsIgnoreCase('true')) {
return true
}
} catch (Exception e) {
log.debug("No appConfigEnabled in ${repo_name}/deployments/${deployment}.yaml or file missing: ${e.message}")
continue
}
}
return false
}
def getManifestJson(def secret_name, def team, def bu) {
def manifest_file = secret_name + '-manifest-' + env.cicd_environment
def destination_file = 'public/manifest.json'
getAwsSecret(manifest_file, destination_file, team, bu)
}
def generateExcludePattern(String excludeS3Files) {
def files = excludeS3Files.split(/\s*,\s*/) // Split the string by comma and trim whitespace
return '|' + files.join('|') // Join file names with '|' as an "or" operator in regex
}
def shouldSkipPm2Metrics(Map config) {
return config.skip_pm2_metrics?.toString()?.toBoolean() ?: false
}
/*
Fuction to build maven docker repo
*/
def getCommitSHA(String repo_name) {
dir(repo_name) {
return sh(returnStdout: true, script: 'git log -1 --format=%H').trim()
}
}
def buildDckr(Map config) {
// // Block all Node.js builds - throwing exception
// throw new Exception("Node.js builds are currently blocked, As a precautionary measure. Due to some packages that got hacked.")
def btObj = new buTeamMapping()
def constructObj = new constructTemplate()
def dparam_obj = new getDockerParams()
def addSSHKey = new addSSHKey()
def dockerUtilObj = new dockerUtilities()
def team = btObj.get_team_initials(config.team)
def bu = btObj.get_bu_initials(config.bu)
def repo_name = config.repo_name
// CAC: read appConfigEnabled from deployment folder (deployments/<name>.yaml), same source as deploy/Helm
def useCacPath = isAppConfigEnabledFromDeployments(repo_name, config)
def build_cmd = config.build_cmd ?: 'npm run build'
// Resolve secret_name from deployment YAML app_name.
// For multi-deployment repos, env/secret files are kept in sync so the first valid app_name is used.
def secret_name = repo_name
def keep_package_lock = config.keep_package_lock != null ? config.keep_package_lock : true
def skip_npmrc = config.skip_npmrc != null ? config.skip_npmrc : true
if (config.secret_name) {
secret_name = config.secret_name
} else {
try {
def yamlObj = new getYamlParameter()
def deploymentsPath = "${repo_name}/deployments"
def yamlFilesOutput = sh(
script: "ls ${deploymentsPath}/*.yaml 2>/dev/null | xargs -r -n1 basename",
returnStdout: true
).trim()
if (yamlFilesOutput) {
def yamlFiles = yamlFilesOutput.split('\n').collect { it.trim() }.findAll { it }
for (yamlFile in yamlFiles) {
def appConfig = yamlObj.getParam(deploymentsPath, yamlFile)
def appName = appConfig?.app_name?.toString()?.trim()
if (appName) {
echo "Resolved secret_name to ${appName} from ${deploymentsPath}/${yamlFile}"
secret_name = appName
break
}
}
}
if (secret_name == repo_name) {
echo "No app_name found in deployment yamls; using repo_name: ${repo_name}"
}
} catch (Exception e) {
echo "Error reading deployment yaml: ${e.getMessage()}; using repo_name: ${repo_name}"
}
}
def require_mainfest = config.require_mainfest ?: false
def pbac_enabled = config.pbac_enabled ?: false
def pbac_scope_name = config.pbac_scope_name ?: ''
def npm_install_arg = config.npm_install_arg ?: ''
if (!npm_install_arg) {
if (fileExists("${repo_name}/pnpm-lock.yaml") || build_cmd.contains('pnpm')) {
npm_install_arg = 'npm install -g pnpm@10.33.0 && pnpm install --frozen-lockfile'
}
else if (!keep_package_lock || !fileExists("${repo_name}/package-lock.json")) {
npm_install_arg = 'npm install'
}
// else: package-lock.json exists + keep_package_lock=true → Dockerfile uses `npm ci`
}
def require_pemfiles = config.require_pemfiles ?: false
def deployArgo = config.deployArgo ?: false
def artifactId = getArtifactId("${repo_name}")
// def region = dparam_obj.getRegion(env.cicd_environment)
// def registry = dparam_obj.getRegistry(env.cicd_environment)
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
def push_to_s3 = config.push_to_s3 ?: false
def s3_path = config.s3_path ?: "homelab-${env.BUILD_ENV}-artifacts/${repo_name}/${env.BRANCH_NAME}"
if (env.INFRA_ENV == 'toolchain' && env.TOOLCHAIN_ENV) {
docker_repo = "${env.cicd_environment}/${env.TOOLCHAIN_ENV}/${team}/${repo_name.toLowerCase()}"
log.info("Toolchain: Overriding docker_repo to ${docker_repo}")
echo "docker_repo: ${docker_repo}"
if (config.s3_path && config.push_to_s3) {
echo" {env.cicd_environment} should be stg due ot ovveride"
s3_path = s3_path.replaceFirst("/${env.cicd_environment}/", "/${env.TOOLCHAIN_ENV}/")
log.info("TOOLCHAIN OVERRIDE:")
log.info(" Original S3 Path: ${config.s3_path}")
log.info(" New Toolchain S3 Path: ${s3_path}")
log.info(" Toolchain Env ID: ${env.TOOLCHAIN_ENV}")
} else if (!config.push_to_s3) {
log.info("Toolchain: push_to_s3 is false, skipping s3_path override as it wont be pushed")
} else if (!config.s3_path && config.push_to_s3) {
log.info("Toolchain: s3_path is not set but push_to_s3 is true, skipping s3_path override as it wont be pushed")
error("CRITICAL: 's3_path' is missing in config.yaml for the ${env.cicd_environment} environment. Toolchain builds require an s3_path.")
}
}
if (env.TOOLCHAIN_ENV) {
log.info("TOOLCHAIN_ENV: ${env.TOOLCHAIN_ENV}")
}else{
log.info("TOOLCHAIN_ENV: not set")
}
def local_path = config.local_path ?: 'build/'
def acl = config.acl ? ' --acl ' + config.acl : ''
def custom_pm2_metrics = config.custom_pm2_metrics ?: false
def skip_pm2_metrics = shouldSkipPm2Metrics(config)
def include_s3_files = ''
def exclude_s3_files = ''
if (!s3_path.endsWith('/')) {
s3_path += '/'
}
if (!local_path.endsWith('/')) {
local_path += '/'
}
if (env.CLOUD_PROVIDER == 'AWS') {
if (config.include_s3_files) {
include_s3_files = ' --exclude "*"'
def include_file_map = config.include_s3_files.split(',')
for (pattern in include_file_map) {
include_s3_files += ' --include "'+pattern+'"'
}
}
exclude_s3_files = config.exclude_s3_files ? ' --exclude "'+config.exclude_s3_files+'"' : ''
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if (config.include_s3_files) {
def includeFileMap = config.include_s3_files.split(',')
def file_types = ''
def i = 0
includeFileMap.each { pattern ->
if ( i == 0 ) {
file_types = "${pattern.replaceAll('\\*.', '')}"
}
else {
file_types += "|${pattern.replaceAll('\\*.', '')}"
}
i++
}
if ( file_types == '*' ) {
include_s3_files = '^(?!.*\\.*$)'
}
else {
include_s3_files = "^(?!.*\\.(${file_types})\$)"
}
echo "include_s3_files is ${include_s3_files}"
}
else {
include_s3_files = '^(?!.*\\.*$)'
}
exclude_s3_files = config.exclude_s3_files != null && config.exclude_s3_files.trim()? generateExcludePattern(config.exclude_s3_files.trim()) : ''
}
def phantomjs = config.phantomjs ?: false
def version = config.dockerBuildVersion.split('-')[-1]
def tag = dparam_obj.getTag(repo_name)
// TODO: create value binding for dockerfile render
def skip_sonar = config.skip_sonar ?: false
if (env.hot_fix || env.INFRA_ENV == 'toolchain') {
skip_sonar = true
}
def testCMD = config.testCMD ?: 'test-report'
def scmType = 'branch'
def commit_sha = getCommitSHA(config.repo_name)
def docker_bindings = [
'buildRegistry': env.buildRegistry,
'version': version,
'build_cmd': build_cmd,
'push_to_s3': push_to_s3,
's3_path': s3_path,
'local_path': local_path,
'include_s3_files': include_s3_files,
'exclude_s3_files': exclude_s3_files,
'acl': acl,
'phantomjs': phantomjs,
'skip_sonar': skip_sonar,
'testCMD': testCMD,
'CLOUD_PROVIDER': env.CLOUD_PROVIDER,
'npm_install_arg': npm_install_arg
]
docker_bindings['arch'] = config.arch
docker_bindings['pbac_enabled'] = pbac_enabled
docker_bindings['pbac_scope_name'] = pbac_scope_name ?: ''
// Map BUILD_ENV to pbac format (stg/int/prd)
def pbac_env = env.cicd_environment
docker_bindings['pbac_env'] = pbac_env
docker_bindings['useCacPath'] = useCacPath
// Validate pbac configuration
if (pbac_enabled && !pbac_scope_name) {
error('pbac_scope_name is required when pbac_enabled is true in config.yaml')
}
stage(stageName('Creating build files')) {
dir(repo_name) {
if (!keep_package_lock) {
removePackageLock()
}
if (!skip_npmrc) {
getNpmRc(secret_name, team, bu)
}
getEnvFile(secret_name, team, bu, useCacPath)
if (require_pemfiles) {
getPemFile(secret_name, team, bu)
}
if (require_mainfest) {
getManifestJson(secret_name, team, bu)
}
def npm_registry = ''
if (fileExists('.npmrc')) {
npm_registry = sh(
returnStdout: true,
script: "grep '^@homelab:registry=' .npmrc | head -1 | cut -d'=' -f2 | tr -d '\\r\\n'"
).trim()
echo "Detected npm registry: ${npm_registry ?: 'default (none found)'}"
} else {
echo ".npmrc not found, skipping registry extraction"
}
docker_bindings['npm_registry'] = npm_registry
}
}
if (skip_sonar){
stage(stageName('Checking quality gate')){
log.info("Skipping the quality gate check as sonar scan is skipped or this is a hotfix ")
}
}
stage(stageName('Building docker images')) {
// Login to docker
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
}
}
catch (Exception e) {
env.msg = 'Error in Docker login'
env.error_msg_to_db = 'Error in Docker login'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${docker_repo} || aws ecr create-repository --region ${env.region} --repository-name ${docker_repo}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
echo 'Skipping - Registry Creation in GCP.'
}
}
catch (Exception e) {
env.msg = "Error in creating ECR repository ${docker_repo}"
env.error_msg_to_db = "Error in creating ECR repository ${docker_repo}"
currentBuild.result = 'FAILURE'
}
try {
dir(repo_name) {
addSSHKey.create()
withCredentials([usernamePassword(credentialsId: "svc-devops-homelab-token", usernameVariable: 'GIT_USER', passwordVariable: 'GIT_TOKEN')]) {
sh """
echo "GITHUB_TOKEN=${GIT_TOKEN}" >> .env
echo "GIT_COMMIT_SHA='${commit_sha}'" >> .env
"""
}
withCredentials([string(credentialsId: env.sonarToken, variable: 'TOKEN')]) {
sh """
echo "SONAR_HOST_URL='${env.sonarURL}'" >> .env
echo "SONAR_TOKEN='${TOKEN}'" >> .env
echo "SONAR_WS_TIMEOUT=120" >> .env
"""
if (env.CHANGE_ID) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
}
sh """
echo "SONAR_CHANGE_ID='${env.CHANGE_ID}'" >> .env
echo "SONAR_CHANGE_BRANCH='${env.CHANGE_BRANCH}'" >> .env
echo "SONAR_CHANGE_TARGET='${env.CHANGE_TARGET}'" >> .env
"""
scmType = 'pr'
}
else {
sh """
echo "SONAR_BRANCH_NAME='${env.BRANCH_NAME}'" >> .env
"""
}
echo 'Make sure to update script section in package.json for Sonar Analysis to be successfull.'
docker_bindings['scmType'] = scmType
}
if (!fileExists('Dockerfile')) {
constructObj.renderTemplate(docker_bindings, 'node-Dockerfile', 'Dockerfile-' + artifactId)
sh "cat Dockerfile-${artifactId}"
if (push_to_s3) {
if (env.CLOUD_PROVIDER == 'AWS') {
sh """
echo 'FROM amazon/aws-cli:2.2.0 as push_env' >> Dockerfile-${artifactId}
echo 'COPY --from=build-env /usr/src/app /app' >> Dockerfile-${artifactId}
echo 'RUN ls -al && aws s3 cp /app/${local_path} s3://${s3_path}${include_s3_files}${exclude_s3_files} --recursive --cache-control max-age=31536000,public${acl}' >> Dockerfile-${artifactId}
"""
}
else if (env.CLOUD_PROVIDER == 'GCP' ) {
sh """
echo 'FROM asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622/admin/devops/google-cloud-sdk:458.0.0-alpine as push_env' >> Dockerfile-${artifactId}
echo 'COPY --from=build-env /usr/src/app /app' >> Dockerfile-${artifactId}
# echo 'RUN ls -al /app && find /app/${local_path} -type f \\( -name "*.js" -o -name "*.map" \\) && find /app/${local_path} -type f \\( -name "*.js" -o -name "*.map" \\) | wc -l && gsutil -m cp -r /app/${local_path}* gs://${s3_path}' >> Dockerfile-${artifactId}
echo "RUN ls -al /app && gsutil -m rsync -r -x \'${include_s3_files} ${exclude_s3_files}\' /app/${local_path} gs://${s3_path}" >> Dockerfile-${artifactId}
echo "RUN if [ -f /app/${local_path}index.html ]; then gsutil cp /app/${local_path}index.html gs://${s3_path}; fi" >> Dockerfile-${artifactId}
"""
}
}
if (phantomjs) {
sh """
echo 'FROM build-env' >> Dockerfile-${artifactId}
echo 'RUN apt-get update && apt-get install -y libfontconfig' >> Dockerfile-${artifactId}
echo 'WORKDIR /usr/src/app' >> Dockerfile-${artifactId}
echo 'RUN npm install pm2 -g' >> Dockerfile-${artifactId}
echo 'RUN pm2 install pm2-metrics' >> Dockerfile-${artifactId}
if [ "${custom_pm2_metrics}" = "true" ]; then
echo 'RUN pm2 install pm2-prom-module' >> Dockerfile-${artifactId}
echo 'RUN pm2 set pm2-prom-module:port 9200' >> Dockerfile-${artifactId}
echo 'RUN pm2 restart pm2-prom-module' >> Dockerfile-${artifactId}
fi
echo 'RUN rm -rf /root/.ssh/id_rsa && apt-get remove -y git openssh-client bzip2' >> Dockerfile-${artifactId}
"""
}
else {
sh """
echo 'FROM ${env.buildRegistry}/build/node:${version}-alpine-secure-multiarch_v1.0' >> Dockerfile-${artifactId}
echo 'RUN npm install pm2 -g' >> Dockerfile-${artifactId}
if [ "${skip_pm2_metrics}" != "true" ]; then
echo 'RUN pm2 install pm2-metrics' >> Dockerfile-${artifactId}
fi
if [ "${custom_pm2_metrics}" = "true" ]; then
echo 'RUN pm2 install pm2-prom-module' >> Dockerfile-${artifactId}
echo 'RUN pm2 set pm2-prom-module:port 9200' >> Dockerfile-${artifactId}
echo 'RUN pm2 restart pm2-prom-module' >> Dockerfile-${artifactId}
fi
echo 'WORKDIR /app' >> Dockerfile-${artifactId}
echo 'COPY --from=build-env /usr/src/app /app' >> Dockerfile-${artifactId}
"""
}
if (docker_bindings['useCacPath']) {
sh "echo 'RUN truncate -s 0 .env' >> Dockerfile-${artifactId}"
}
sh "cat Dockerfile-${artifactId}"
def imageList = "${env.buildRegistry}/build/node:${version}-alpine-secure-multiarch_v1.0 " +
"asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622/admin/devops/google-cloud-sdk:458.0.0-alpine " +
"${env.buildRegistry}/build/node:${version}-slim-secure-multiarch_v2.0"
sh """
max_attempts=5
images=\"$imageList\"
for image in \$images; do
attempt=1
until docker pull \"\$image\"; do
if [ \$attempt -eq \$max_attempts ]; then
echo \"Failed to pull \$image after \$attempt attempts.\"
exit 1
fi
echo \"Pull failed for \$image, retrying in 2 seconds... (Attempt \$attempt/\$max_attempts)\"
attempt=\$((attempt + 1))
sleep 2
done
done
"""
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
def docker_cmd = "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} -f Dockerfile-${artifactId} . "
sh(script: docker_cmd)
dockerUtilObj.retryDockerPush("docker push ${env.registry}/${docker_repo}:${tag}")
}
else {
def docker_cmd = "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} -f Dockerfile-${artifactId} ."
sh(script: docker_cmd)
}
//Remove dockerfile
sh(script: "rm -rf Dockerfile-${artifactId}")
}
else {
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
sh(script: "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} . ")
dockerUtilObj.retryDockerPush("docker push ${env.registry}/${docker_repo}:${tag}")
}
else {
log.info("Skipping Docker Push - ${env.cicd_environment} env")
sh(script: "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} .")
}
}
}
}
catch (Exception e) {
env.msg = 'Error in building DockerFile Or Pushing To ECR'
env.error_msg_to_db = env.msg
log.error(env.msg + '\n' + e.toString())
currentBuild.result = env.FAILURE
throw e
}
return [tag , deployArgo]
}
}
def getVaultSecret(String vault_cmd) {
log.info("Fetching secrets from Vault with vault-${env.cicd_environment}-token with CMD - ${vault_cmd}")
if (env.INFRA_ENV == 'toolchain' && env.TOOLCHAIN_ENV) {
boolean skipToolchainOverride = vault_cmd.contains("npmrc") || vault_cmd.contains("-secrets-")
if (!skipToolchainOverride) {
vault_cmd = vault_cmd.replace("homelab/${env.cicd_environment}-cac/", "homelab/toolchain/${env.TOOLCHAIN_ENV}/${env.cicd_environment}-cac/")
vault_cmd = vault_cmd.replace("homelab/${env.cicd_environment}/", "homelab/toolchain/${env.TOOLCHAIN_ENV}/${env.cicd_environment}/")
log.info("TOOLCHAIN OVERRIDE (Application Config):")
log.info(" New Toolchain Vault CMD: ${vault_cmd}")
} else {
log.info("TOOLCHAIN: Reading from standard (non-toolchain) Vault path for npmrc / pem secret.")
}
}
withCredentials([string(credentialsId: "${env.vaultToken}", variable: 'TOKEN')]) {
env.VAULT_ADDR = "${env.vaultURL}"
env.VAULT_TOKEN = "${TOKEN}"
sh(script:"${vault_cmd}")
env.VAULT_TOKEN = 'empty'
env.VAULT_ADDR = env.VAULT_TOKEN
}
}
@@ -0,0 +1,42 @@
package com.homelab.stages
def run(String build_tool){
try {
switch (build_tool) {
case 'maven':
return new buildMaven()
break;
case 'docker':
return new buildDocker()
break;
case ~/^maven-.*/:
return new buildMaven()
break;
case ~/^python-.*/:
return new buildPython()
break;
case ~/^node-.*/:
return new buildNode()
break;
case ~/^rust.*/:
return new buildRust()
break;
case ~/^go.*/:
return new buildGo()
break;
case 'gradle':
return new buildGradle()
break;
case 'php':
return new buildPhp()
default:
return defaultBuild()
}
}
catch (Exception e) {
env.msg = 'Error in selecting the build tool (check the spell) Error: ' + e.toString()
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
+104
View File
@@ -0,0 +1,104 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.getDockerParams
def buildDckr(Map config){
def btObj = new buTeamMapping()
def constructObj = new constructTemplate()
def dparam_obj = new getDockerParams()
def team = btObj.get_team_initials(config.team)
def repo_name = config.repo_name
def deployArgo = config.deployArgo ?: false
// def region = dparam_obj.getRegion(env.cicd_environment)
// def registry = dparam_obj.getRegistry(env.cicd_environment)
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
def tag = dparam_obj.getTag(repo_name)
def docker_bindings = [
"repo_name": repo_name,
'buildRegistry': env.buildRegistry
]
if (config.containsKey("copy_file")){
if (env.CLOUD_PROVIDER == 'AWS'){
def recursive = config.copy_file.recursive ? " --recursive" : ""
dir(repo_name){
dir('copied_files'){
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
}
}
} else if (env.CLOUD_PROVIDER == 'GCP'){
def recursive = config.copy_file.recursive ? " -r" : ""
dir(repo_name){
dir('copied_files'){
sh(script:"gsutil cp${recursive} ${config.copy_file.path} .")
}
}
}
docker_bindings["copy_file"] = true
docker_bindings["copy_target"] = config.copy_file.target ?: "/opt/target"
}
else{
docker_bindings["copy_file"] = false
}
stage(stageName('Building docker images')){
// Login to docker
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
}
}
catch(Exception e) {
env.msg = "Error in Docker login"
env.error_msg_to_db = "Error in Docker login"
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
try{
if (env.CLOUD_PROVIDER == "AWS"){
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${docker_repo} || aws ecr create-repository --region ${env.region} --repository-name ${docker_repo}")
}
else if (env.CLOUD_PROVIDER == 'GCP'){
echo "Skipping - Registry Creation in GCP."
}
}
catch(Exception e){
env.msg = "Error in creating ECR repository ${docker_repo}"
env.error_msg_to_db = "Error in creating ECR repository ${docker_repo}"
currentBuild.result = "FAILURE"
}
try {
dir(repo_name){
if(!fileExists("Dockerfile")){
constructObj.renderTemplate(docker_bindings,'php-Dockerfile','Dockerfile-php')
if (env.cicd_environment != 'ftr') {
sh(script: "docker build --tag ${env.registry}/${docker_repo}:${tag} -f Dockerfile-php . && docker push ${env.registry}/${docker_repo}:${tag}")
}
else {
log.info("Skipping Docker builds for PHP in ${env.cicd_environment} env")
}
//Remove dockerfile
sh(script: "rm -rf Dockerfile-php")
}
else {
sh(script: "docker build --tag ${env.registry}/${docker_repo}:${tag} . && docker push ${env.registry}/${docker_repo}:${tag}")
}
}
}
catch (Exception e) {
env.msg = "Error in building DockerFile Or Pushing To artifact registry"
env.error_msg_to_db = "Error in building DockerFile Or Pushing To image registry"
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
return [tag , deployArgo]
}
+221
View File
@@ -0,0 +1,221 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.getDockerParams
import com.homelab.utilities.addSSHKey
import com.homelab.utilities.dockerUtilities
/*
Function to get the version from pom.xml
input arguments:
repo_name: String parameter to change the directory where pom.xml is located
*/
// def getArtifactId(String repo_name){
// dir("$repo_name"){
// if (fileExists("package.json")) {
// return sh(returnStdout: true, script: 'jq -r .name package.json').trim()
// }
// }
// }
// def getAwsSecret(def secret_name, def destination_file){
// sh (script:"aws secretsmanager get-secret-value --secret-id ${secret_name} --query SecretString --output text > ${destination_file}")
// }
// def getCommitid(String repo_name){
// dir("$repo_name"){
// return sh(returnStdout: true, script: 'git log -1 --format=%h').trim()
// }
// }
// def getNpmRc(def secret_name){
// def npmrc_file = secret_name + "-npmrc"
// sh (script:"aws secretsmanager get-secret-value --secret-id ${npmrc_file} --query SecretString --output text| jq -r .HOMELAB_NPMRC_SECRET > .npmrc")
// }
// def getPemFile(def secret_name){
// def pem_secret_name = secret_name + "-secrets"
// if(env.BUILD_ENV == 'stage'){
// sh (script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_dev > 1_public_secret_dev.pem")
// sh (script:"cat 1_public_secret_dev.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_dev.pem")
// }
// sh (script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_prod > 1_public_secret_prod.pem")
// sh (script:"cat 1_public_secret_prod.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_prod.pem")
// }
// def getEnvFile(def secret_name){
// def env_file = secret_name + "-env"
// def destination_file = ".env"
// sh(script:"aws secretsmanager get-secret-value --secret-id ${env_file} | jq --raw-output '.SecretString' | jq '.' | jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' > .env")
// }
// def getManifestJson(def secret_name){
// def manifest_file = secret_name+"-manifest"
// def destination_file = "public/manifest.json"
// getAwsSecret(manifest_file,destination_file)
// }
/*
Fuction to build maven docker repo
*/
def buildDckr(Map config) {
def btObj = new buTeamMapping()
def constructObj = new constructTemplate()
def dparam_obj = new getDockerParams()
def addSSHKey = new addSSHKey()
def team = btObj.get_team_initials(config.team)
def repo_name = config.repo_name
def deployArgo = config.deployArgo ?: false
def modules_requirements_file = config.modules_requirements_file ?: 'requirements.txt'
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
def tag = dparam_obj.getTag(repo_name)
if (env.INFRA_ENV == 'toolchain') {
def dockerUtilObj = new dockerUtilities()
if (dockerUtilObj.imageExists(env.registry, docker_repo, tag)) {
log.info("Toolchain: Image found in registry for ${docker_repo}:${tag}. Skipping build step.")
return [tag, deployArgo]
} else {
log.info("Toolchain: Image missing for ${docker_repo}:${tag}. Proceeding with build.")
}
}
// TODO: create value binding for dockerfile render
def docker_bindings = [
'buildRegistry': env.buildRegistry,
'modules_requirements_file': modules_requirements_file,
'arch': config.arch,
'buildRegistry': env.buildRegistry
]
docker_bindings['arch'] = config.arch
// stage("Create build files"){
// dir(repo_name){
// getNpmRc(secret_name)
// getEnvFile(secret_name)
// if (require_pemfiles){getPemFile(secret_name)}
// if (require_mainfest){getManifestJson(secret_name)}
// }
// }
stage(stageName('Building docker images')) {
// Login to docker
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
}
}
catch (Exception e) {
env.msg = 'Error in Docker login'
env.error_msg_to_db = 'Error in Docker login'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
if (config.containsKey('copy_file')) {
def recursive = config.copy_file.recursive ? ' --recursive' : ''
docker_bindings['copy_file_path'] = config.copy_file.path
dir(repo_name) {
dir('copied_files') {
if (env.CLOUD_PROVIDER == 'AWS') {
// delete any old data
sh(script:'rm -rf *')
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
// delete any old data
docker_bindings['recursive'] = '-r'
sh(script:'rm -rf *')
sh(script:"gsutil cp ${docker_bindings.recursive} ${docker_bindings.copy_file_path} .")
}
}
}
docker_bindings['copy_file'] = true
docker_bindings['copy_target'] = config.copy_file.target ?: '/app/'
}
else {
docker_bindings['copy_file'] = false
}
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${docker_repo} || aws ecr create-repository --region ${env.region} --repository-name ${docker_repo}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
echo 'Skipping - Registry Creation in GCP.'
}
}
catch (Exception e) {
env.msg = "Error in creating ECR repository ${docker_repo}"
env.error_msg_to_db = "Error in creating ECR repository ${docker_repo}"
currentBuild.result = env.FAILURE
}
try {
dir(repo_name) {
addSSHKey.create()
if (!fileExists('Dockerfile')) {
// print(config)
constructObj.renderTemplate(docker_bindings,config.dockerBuildVersion+'-Dockerfile','Dockerfile-'+repo_name)
sh "cat Dockerfile-${repo_name}"
// withCredentials([string(credentialsId: 'homelab-github-ssh-prv-key', variable: 'SSH_PRIVATE_KEY_S')]) {
// withCredentials(bindings: [sshUserPrivateKey(credentialsId: 'homelab-ssh-github-key', \
// keyFileVariable: 'SSH_PRIVATE_KEY', \
// passphraseVariable: '', \
// usernameVariable: '')]) {
//withCredentials([string(credentialsId: 'git_private_key', variable: 'gitkey')]) {
// sh """
// set +x
// docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile-${repo_name}" . && docker push ${env.registry}/${docker_repo}:${tag}
// set -x
// """
if (env.CLOUD_PROVIDER == 'AWS') {
sh """
set +x
docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile-${repo_name}" .
docker push ${env.registry}/${docker_repo}:${tag}
set -x
"""
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
sh """
set +x
docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile-${repo_name}" .
docker push ${env.registry}/${docker_repo}:${tag}
set -x
"""
}
else {
log.info("Skipping Docker builds for Python in ${env.cicd_environment} env")
}
}
// }
// }
//Remove dockerfile
sh(script: "rm -rf Dockerfile-${repo_name}")
}
else {
sh "cat Dockerfile"
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh """
set +x
docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile" .
docker push ${env.registry}/${docker_repo}:${tag}
set -x
"""
}
}
}
}
catch (Exception e) {
env.msg = "Error in building DockerFile Or Pushing To ECR. For Full Error Details - ${e}"
env.error_msg_to_db = 'Error in building DockerFile Or Pushing To ECR'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
return [tag , deployArgo]
}
}
+315
View File
@@ -0,0 +1,315 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.getDockerParams
import com.homelab.utilities.addSSHKey
def buildDckr(Map config) {
env.RUSTPRIVATE = 'github.com/Homelab'
def btObj = new buTeamMapping()
def constructObj = new constructTemplate()
def dparam_obj = new getDockerParams()
def addSSHKey = new addSSHKey()
def deployArgo = config.deployArgo ?: true
def team = btObj.get_team_initials(config.team)
def modules = config.modules ?: ['module_less']
def repo_name = config.repo_name
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
def tag = dparam_obj.getTag(repo_name)
def buildx = config.containsKey('buildx') ? config.buildx : true
def docker_bindings = [:]
def skip_sonar = config.skip_sonar ?: false
def version = config.dockerBuildVersion.split('-')[-1]
def repoType = config.repo_type ?: 'microservice'
docker_bindings['version'] = version
docker_bindings['base_dir'] = config.base_dir ?: false
docker_bindings['buildRegistry'] = env.buildRegistry
docker_bindings['build_packages'] = getSystemPackages(config, 'build_packages')
docker_bindings['runtime_packages'] = getSystemPackages(config, 'runtime_packages')
stage('Build docker images') {
// Login to docker
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
}
}
catch (Exception e) {
env.msg = 'Error in Docker login'
env.error_msg_to_db = env.msg
log.error(env.msg + '. Error: ' + e.toString())
currentBuild.result = env.FAILURE
throw e
}
if (config.containsKey('copy_file')) {
def recursive = config.copy_file.recursive ? ' --recursive' : ''
dir(repo_name) {
dir('copied_files') {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
} else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script:"gsutil cp${recursive} ${config.copy_file.path} .")
}
}
}
docker_bindings['copy_file'] = true
docker_bindings['copy_target'] = config.copy_file.target ?: '/app/'
docker_bindings['base_dir'] = config.base_dir ?: false
}
else {
docker_bindings['copy_file'] = false
}
try {
dir(repo_name) {
sonar_scan(repo_name, skip_sonar, version )
if (repoType != 'microservice') {
log.info("Skipping Docker build for ${repoType} repo type")
return [tag, deployArgo]
}
addSSHKey.create()
if (!fileExists('Dockerfile')) {
for (module in modules) {
def module_name = (module instanceof LinkedHashMap) ? module.keySet()[0] : module
docker_bindings['module_property'] = (module instanceof LinkedHashMap) ? module[module_name] : [ : ]
docker_bindings['module'] = module_name
docker_bindings['binary_name'] = (module == 'module_less') ? repo_name : module_name
constructObj.renderTemplate(docker_bindings, 'rust-Dockerfile', 'Dockerfile-' + module_name)
sh "cat Dockerfile-${module_name}"
module_repo = (module == 'module_less') ? docker_repo : docker_repo + '/' + module_name
try {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${module_repo} || aws ecr create-repository --region ${env.region} --repository-name ${module_repo}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
echo 'Skipping - Registry Creation in GCP.'
}
}
catch (Exception e) {
env.msg = "Error in creating ECR repository ${module_repo}"
env.error_msg_to_db = "Error in creating ECR repository ${module_repo}"
currentBuild.result = 'FAILURE'
}
if (buildx) {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "set +x && docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} --push .")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if (env.cicd_environment != 'ftr') {
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} . && docker push ${env.registry}/${module_repo}:${tag}")
}
else {
log.info("Skipping Docker Push - ${env.cicd_environment} env")
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} .")
}
}
}
else {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} . && docker push ${env.registry}/${module_repo}:${tag}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if (env.cicd_environment != 'ftr') {
sh(script: """
set +x
# Setup buildx for ARM64 builds
docker buildx rm mybuilder || true
docker buildx create --name mybuilder --driver docker-container --bootstrap
docker buildx use mybuilder
# Build for ARM64
docker buildx build --platform linux/arm64 --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} --push .
""")
}
else {
log.info("Skipping Docker Push - ${env.cicd_environment} env")
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} .")
}
}
}
//Remove dockerfile
sh(script: "rm -rf Dockerfile-${module_name}")
}
}
else {
sh "cat Dockerfile"
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
if (env.cicd_environment != 'ftr') {
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
}
else {
log.info("Skipping Docker Push - ${env.cicd_environment} env")
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} .")
}
}
}
}
}
catch (Exception e) {
env.msg = 'Error in building DockerFile Or Pushing To ECR'
env.error_msg_to_db = env.msg
log.error(env.msg + '. Error: ' + e.toString())
currentBuild.result = env.FAILURE
throw e
}
return [tag, deployArgo]
}
}
// Services may need native libraries that are not part of a shared base image
// (for example, database client headers used by a crate's build script). The
// keys are language-neutral for future reuse; currently only the Rust builder
// consumes them. Restrict values to Debian package names so config data cannot
// alter the rendered Dockerfile instruction.
def getSystemPackages(Map config, String key) {
def configuredPackages = config[key]
if (configuredPackages == null) {
return []
}
if (!(configuredPackages instanceof List)) {
throw new IllegalArgumentException("${key} must be a YAML list of system package names")
}
def packages = configuredPackages.collect { packageName -> packageName?.toString()?.trim() }
if (packages.any { packageName -> !packageName || !(packageName ==~ /^[a-z0-9][a-z0-9+.-]*$/) }) {
throw new IllegalArgumentException("${key} contains an invalid system package name")
}
return packages.unique()
}
/*
Fuction to execute sonar scan
Input Arguments:
repo_name: repository name
skip_sonar: boolena parameter to skip sonar scan
*/
def sonar_scan(String repo_name, boolean skip_sonar , String version ) {
try {
stage('Run sonar scan') {
if (!skip_sonar) {
withSonarQubeEnv('sonarqube-test') {
if (env.CHANGE_ID) {
if (env.CLOUD_PROVIDER == 'AWS') {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
}
sh(script: "mvn sonar:sonar -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
}
sh(script: "curl -LO https://go.dev/dl/go${go_version}.linux-amd64.tar.gz")
sh(script: "tar -xvzf go${go_version}.linux-amd64.tar.gz -C /usr/local",returnStdout: true)
sh(script:"rm -Rf go${go_version}.linux-amd64.tar.gz")
// Set Go environment variables
env.PATH = "/usr/local/go/bin:${env.PATH}"
// Run Go mod tidy and tests
sh (script: "go mod tidy")
int testExitCode = sh(script: "go test -short -coverprofile=./cov.out ./...", returnStatus: true)
if (testExitCode != 0) {
sh(script: "echo Go tests failed, but the pipeline will continue.")
}
// Download and extract Sonar Scanner
sh(script: "curl -LO https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip")
sh(script: "unzip -o sonar-scanner-cli-5.0.1.3006-linux.zip -d /usr/local/sonar-scanner")
sh(script: "rm -Rf sonar-scanner-cli-5.0.1.3006-linux.zip")
env.PATH = "/usr/local/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin:${env.PATH}"
// Run Sonar Scanner
sh(script:"sonar-scanner -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.go.coverage.reportPaths=./cov.out ")
}
}
else {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: "mvn sonar:sonar -Dsonar.branch.name=${env.BRANCH_NAME}")
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script: "curl -LO https://go.dev/dl/go${go_version}.linux-amd64.tar.gz")
sh(script: "tar -xvzf go${go_version}.linux-amd64.tar.gz -C /usr/local",returnStdout: true)
sh(script:"rm -Rf go${go_version}.linux-amd64.tar.gz")
// Set Go environment variables
env.PATH = "/usr/local/go/bin:${env.PATH}"
// Run Go mod tidy and tests
sh (script: "go mod tidy")
int testExitCode = sh(script: "go test -short -coverprofile=./cov.out ./...", returnStatus: true)
if (testExitCode != 0) {
sh(script: "echo Go tests failed, but the pipeline will continue.")
}
// Download and extract Sonar Scanner
sh(script: "curl -LO https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip")
sh(script: "unzip -o sonar-scanner-cli-5.0.1.3006-linux.zip -d /usr/local/sonar-scanner")
sh(script: "rm -Rf sonar-scanner-cli-5.0.1.3006-linux.zip")
env.PATH = "/usr/local/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin:${env.PATH}"
// Run Sonar Scanner
sh(script:"sonar-scanner -Dsonar.go.coverage.reportPaths=./cov.out -Dproject.settings=`pwd`/sonar-project.properties -Dsonar.branch.name=${env.BRANCH_NAME}")
}
}
}
}
else {
log.info('Skipping - Sonar Scan')
}
}
stage("Quality Gate"){
if (skip_sonar){
log.info("Sonar scan is skipped. Marking this stage as passed.")
}
else if (!env.CHANGE_ID){
log.info("Skipping quality gate check on Branches. Marking this stage as passed.")
}
else{
timeout(time: 600, unit: 'SECONDS') {
def qg = waitForQualityGate()
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE'){
if (qg.status != 'OK') {
log.warn("Quality gate failed: ${qg.status}, but continuing pipeline execution.")
}
}
}
}
}
}
catch (Exception e) {
dir("$repo_name") {
withSonarQubeEnv('sonarqube-test') {
if (env.CLOUD_PROVIDER == 'AWS') {
sh(script: 'mvn sonar:sonar')
}
else if (env.CLOUD_PROVIDER == 'GCP') {
sh(script: "Quality Gate Failed !")
}
}
}
}
}
+30
View File
@@ -0,0 +1,30 @@
package com.homelab.stages
// Adapted from the original: same structure (dir-scoped checkout,
// optional submodule init if .gitmodules exists), just the credential
// default changed from Homelab's internal service account
// (svc-devops-homelab) to this homelab's Gitea credential. Set GITHUB_CRED
// in the pipeline env if you ever need to override it per-job.
def run(Map param) {
try {
stage(stageName('Checkout from git')) {
dir("${param.repo_name}") {
checkout scm
if (fileExists('.gitmodules')) {
log.info('Repository has submodules - running git submodule init and update --recursive')
def credId = env.GITHUB_CRED?.trim()
if (!credId || credId == 'null') credId = 'gitea-ci-credentials'
withCredentials([gitUsernamePassword(credentialsId: credId, gitToolName: 'git-tool')]) {
sh 'git submodule init && git submodule update --recursive'
}
}
}
}
}
catch (Exception e) {
env.msg = 'Error cloning the job. Error: ' + e.toString()
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
+906
View File
@@ -0,0 +1,906 @@
package com.homelab.stages
import com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterDefinition
import com.homelab.utilities.getYamlParameter
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.gitActions
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.constructParam
import com.homelab.utilities.getDockerParams
import com.homelab.utilities.nodePoolSelection
def run(String repo_name, def deployment_order, def tag, def build_team, def dockerBuildVersion, String notify_channel) {
def userInput = ''
// def helm_repo_name = 'devops-helm-charts'
// def argo_repo_name = 'devops-argo-config'
def gitObj = new gitActions()
def constructParam = new constructParam()
def yamlObj = new getYamlParameter()
def branch_name = env.BRANCH_NAME
def branch_param_map = ['(master|main|gcp-main|gcp-master|farmiso-main)': ['branch':'main', 'envrn':'prd'],
'(develop|gcp-dev)':['branch':'develop', 'envrn':'stg']]
if (env.CHANGE_ID) {
branch_param_map = [
'(develop|gcp-dev)':['branch':'feature', 'envrn':'ftr'],
'(master|main|gcp-main|gcp-master|farmiso-main)':['branch':'pre-prod', 'envrn':'int']]
branch_name = env.CHANGE_TARGET
}
def branch_param = branch_param_map.collectEntries { key, value -> branch_name.matches(key) ? value : [ : ] }
if (branch_param == [:]) {
branch_param = ['branch':'feature', 'envrn':'ftr']
}
def helm_branch_name = branch_param.branch
def argo_branch_name = branch_param.branch
def envrn = branch_param.envrn
if (env.CHANGE_ID && env.CHANGE_BRANCH != "develop" && env.CHANGE_TARGET == "main" ) {
def allowedNonDevelopPrDeploymentToInt = constructParam.allowedNonDevelopPrDeploymentToIntRepos(repo_name)
if (!allowedNonDevelopPrDeploymentToInt) {
log.error("******** ONLY DEVELOP BRANCH PR ALLOWED FOR PRE-PROD ENV DEPLOYMENT ********")
return
}
}
log.info('Please provide apps to deploy')
timeout(unit: 'SECONDS', time: 300) {
userInput = wait_for_user_input(deployment_order)
}
// change deployment order according to user input
if (userInput == '') {
log.info('No Deployments selected. Running remaining steps.')
return
}
else if (!userInput.contains('All')) {
deployment_order = userInput.split(',') as List
}
commit_branch_name = env.CHANGE_ID ? env.CHANGE_BRANCH : env.BRANCH_NAME
def commit_id = gitObj.fetchLatestCommitId(repo_name, commit_branch_name)
env.COMMIT_ID = commit_id
env.SERVICES = deployment_order
//Show selected applications to deploy
log.info('Following services will be deployed:\n' + deployment_order.join('\n'))
// Clone the repo just once for all the deployments
gitObj.clone("${WORKSPACE}", "${env.helm_repo_name}", "${helm_branch_name}")
// Clone the repo just once for all the deployments
gitObj.clone("${WORKSPACE}", "${env.argo_repo_name}", "${argo_branch_name}")
for (deployment in deployment_order) {
try {
def isMultizoneEnabled = constructParam.isMultizoneEnabled(deployment)
log.info(" Multi-zone enabled for ${deployment} - ${isMultizoneEnabled}")
if (isMultizoneEnabled) {
def multizone = "Multi-zone enabled for this deployable ${deployment}. Please use Ringmaster for deployment."
currentBuild.result = env.FAILURE
throw new Exception(multizone)
}
def value_binding = yamlObj.getParam("${repo_name}/deployments", "${deployment}.yaml")
constructParam.perDeploymentVars(value_binding)
// if(envrn == 'prd') {
// stage("${deployment}: Downscale Pods in Preprod Env") {
// preprod_downscale(repo_name, deployment,notify_channel)
// }
// }
stage("${deployment}: Update Argo App") {
update_argo_repo(repo_name, deployment, argo_branch_name, envrn)
}
stage('Update Argo App of apps') {
refresh_app_of_apps(envrn)
}
stage("${deployment}: Update Helm Repo") {
update_helm_repo(repo_name, deployment, tag, build_team, helm_branch_name, envrn, dockerBuildVersion, notify_channel)
}
stage("${deployment}: Refresh & Sync App in argoCD") {
refresh_and_sync(repo_name, deployment, envrn)
}
}
catch (Exception e) {
log.error(e)
currentBuild.result = env.FAILURE
}
}
}
// def preprod_downscale(String repo_name, String deployment, String notify_channel){
// def yamlobj = new getYamlParameter()
// def deploy_values = yamlobj.getParam("${repo_name}/deployments","${deployment}.yaml")
// def app_name = deploy_values.app_name
// def bu = deploy_values.bu
// def slack_channel = notify_channel
// def as_enabled = deploy_values.environment.'int'.as_enabled
// log.info("########################### Invoking Jenkins Job to Downscale Pods in Preprod Environment. ###########################")
// build wait: false, job: 'downscale-preprod-eks', parameters:[string(name:'app_name', value:"${app_name}"),
// string(name:'bu',value:"${bu}"),
// string(name:'as_enabled',value:"${as_enabled}"),
// string(name:'slack_channel',value:"${slack_channel}")]
// }
def wait_for_user_input(def deployments) {
def userInput = ''
String choices = 'All,' + deployments.join(',')
int visibleItemCount = 1 + deployments.size()
def multiSelect = new ExtendedChoiceParameterDefinition('deployments', //name
'PT_CHECKBOX', // parameter type
choices, //values
'', //projectName
'', //propertyFile
'', //groovyScript
'', //groovyScriptFile
'', //bindings
'', //groovyClasspath
'', //propertyKey
'', //defaultValue
'', //defaultPropertyFile
'', //defaultGroovyScript
'', //defaultGroovyScriptFile
'', //defaultBindings
'', //defaultGroovyClasspath
'', //defaultPropertyKey
'', //descriptionPropertyValue
'', //descriptionPropertyFile
'', //descriptionGroovyScript
'', //descriptionGroovyScriptFile
'', //descriptionBindings
'', //descriptionGroovyClasspath
'', //descriptionPropertyKey
'', //javascriptFile
'', //javascript
false, //saveJSONParameterToFile
false, //quoteValue
visibleItemCount, //visibleItemCount
'Choose Deployments', //description
',') //multiSelectDelimiter
stage('wait for user input') {
try {
echo "Skipping User Input - ${env.skip_user_input}"
if (env.skip_user_input.toBoolean()) {
userInput = 'All'
}
else {
userInput = input message: 'Choose applications to deploy', ok: 'Deploy', parameters: [multiSelect]
}
return userInput
}
catch (Exception e) {
env.msg = 'Error in taking userInput' + e.toString()
env.error_msg_to_db = 'Error Taking User Input for Deployment of Applications'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
}
def update_helm_repo(String repo_name, String deployment, String tag, String build_team, String helm_branch_name, String envrn, def dockerBuildVersion, String notify_channel) {
def yamlobj = new getYamlParameter()
def btObj = new buTeamMapping()
def gitObj = new gitActions()
def templateObj = new constructTemplate()
def nodePoolSelection = new nodePoolSelection()
// def helm_repo_name = 'devops-helm-charts'
def value_yaml_file = get_value_yaml_file(dockerBuildVersion)
def value_binding1 = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
if (value_binding1.containsKey('cron')) {
if (value_binding1['cron']) {
value_yaml_file = 'cron-values.yaml'
}
}
def env_norm = ['prd':'prod', 'int':'pre-prod', 'stg':'stg', 'ftr':'feature']
def pr_num = ''
def commit_status = 0
try {
// Get values from deployments/deployment.yaml
isfeatureDeployment = (envrn == 'ftr') ? true : false
value_binding1 = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
def memory_request = value_binding1.environment[env.cicd_environment].memory_request
def cpu_request = value_binding1.environment[env.cicd_environment].cpu_request
def priority_v2 = value_binding1.priority_v2
def commit_id = env.COMMIT_ID
if ( env.CLOUD_PROVIDER == 'GCP' && !value_binding1.containsKey('cron') ) {
nodeSelectorValue = nodePoolSelection.run(memory_request, cpu_request.toString(),priority_v2)
echo "Node Selector Value is ${nodeSelectorValue}"
value_binding1['nodeSelectorValue'] = nodeSelectorValue
}
def xms = ''
def xmx = ''
if (!value_binding1.containsKey('cron')) {
if (dockerBuildVersion.contains('maven') || dockerBuildVersion.contains('gradle') || dockerBuildVersion.contains('go')) {
value_binding1['activeProcessorCount'] = calculate_active_processors(cpu_request.toString())
}
if (dockerBuildVersion.contains('maven') || dockerBuildVersion.contains('gradle')) {
def memory_limit = value_binding1.environment[env.cicd_environment].memory_limit
def deployment_args = value_binding1.environment[env.cicd_environment].deployment_args
echo "memory_limit is ${memory_limit}"
def memory_string = memory_limit
echo "performing operations on this memory string - ${memory_string}"
def memory_value = ''
if ( memory_string.contains('M') ) {
memory_value = memory_string.replaceAll('Mi', '')
memory_value = memory_value.replaceAll('M', '')
try {
memory_value = memory_value.toInteger()
}
catch (NumberFormatException e) {
memory_value = memory_value.toDouble()
memory_value = memory_value.toInteger()
}
}
else if ( memory_string.contains('G') ) {
memory_value = memory_string.replaceAll('Gi', '')
memory_value = memory_value.replaceAll('G', '')
try {
memory_value = memory_value.toInteger() * 1024
}
catch (NumberFormatException e) {
memory_value = memory_value.toDouble() * 1024
memory_value = memory_value.toInteger()
}
}
memory_value = memory_value * 0.5
memory_value = memory_value.toInteger()
xms = "${memory_value}M"
xmx = "${memory_value}M"
echo "xms and xmx from memory value - ${memory_value} are - ${xms} ${xmx}"
for (arg in deployment_args) {
if (arg.contains('Xms')) {
xms = arg.replaceAll('.*Xms', '')
echo "xms from deployment_args - ${xms}"
}
if (arg.contains('Xmx')) {
xmx = arg.replaceAll('.*Xmx', '')
echo "xmx from deployment_args - ${xmx}"
}
}
echo "final xms and xmx - ${xms} ${xmx}"
}
}
value_binding1['dockerBuildVersion'] = dockerBuildVersion == 'python-3.10.12' ? 'python-3.7' : dockerBuildVersion
deployEnv = !value_binding1['environment'].containsKey(envrn) && isfeatureDeployment ? 'stg' : envrn
Map value_binding = value_binding1['environment'].collectEntries { key, value -> deployEnv.matches(key) ? value : [ : ] }
value_binding1.remove('environment')
value_binding1.putAll(value_binding)
value_binding1['repo_name'] = repo_name
value_binding1['environment'] = envrn
value_binding1['environment_norm'] = env_norm[envrn]
value_binding1['tag'] = tag
value_binding1['repo_name'] = repo_name
value_binding1['build_team'] = build_team
value_binding1['commit_id'] = commit_id
def buini = btObj.get_bu_initials(value_binding1.bu)
def bu = btObj.get_bu_initials(value_binding1.bu)
def teamini = btObj.get_team_initials(value_binding1.team)
def team = btObj.get_team_initials(value_binding1.team)
def app_name = value_binding1.app_name
def app_branch = app_name + '-' + helm_branch_name
def app_helm_repo = "${env.helm_repo_name}/${env.helmChartsPath}/${buini}/${teamini}/${app_name}"
def ingress_val = value_binding1.ingress_val ?: (env.CHANGE_ID) ? "pr-${CHANGE_ID}" : "${BRANCH_NAME}"
sh "chmod -R 777 ${app_helm_repo}"
sh "yq . ${app_helm_repo}/values_properties.yaml -y > a.yaml;mv a.yaml ${app_helm_repo}/values_properties.yaml"
sh "cat ${app_helm_repo}/values_properties.yaml"
if (isfeatureDeployment) {
env.ingress_val = ingress_val
value_binding1['env_ns'] = ingress_val
value_binding1['vault_env'] = value_binding1['create_vault_path'] ? ingress_val : 'stg'
sh(returnStdout: true, script: """
mkdir -p ${app_helm_repo}/${ingress_val}
sed "s/INGRESS_PR_NUMBER/${ingress_val}/g" ${app_helm_repo}/values_properties.yaml > ${app_helm_repo}/${ingress_val}/values_properties.yaml
""")
value_binding2 = yamlobj.getParam("${app_helm_repo}/${ingress_val}", 'values_properties.yaml')
app_branch = ingress_val + '-' + app_branch
}
else {
value_binding1['vault_env'] = envrn
value_binding1['env_ns'] = envrn
value_binding2 = yamlobj.getParam("${app_helm_repo}", 'values_properties.yaml')
echo '722 Printing Value Binding 2'
print value_binding2
}
value_binding1.putAll(value_binding2)
value_binding1['bu_norm'] = value_binding1['bu']
value_binding1['team_norm'] = value_binding1['team']
value_binding1['bu'] = buini
value_binding1['team'] = teamini
def serviceTypes = ["httpstateless", "consumer", "producer", "scheduler", "worker", "grpc", "web", "websocket", "cache", "database"]
//validate service type params
if (value_binding1['service_type']){
// Validating the parameter type
if(!(value_binding1['service_type'] instanceof List) || value_binding1['service_type'].isEmpty()){
env.msg = 'You have not specified service_type correctly. Exiting the pipeline.'
log.error(env.msg)
sh 'exit 1'
}
// Check if value_binding1['service_type'] contains any service type not in servicesType
def invalidServiceTypes = value_binding1['service_type'].findAll { !serviceTypes.contains(it) }
if (!invalidServiceTypes.isEmpty()) {
log.error("Invalid service type(s): ${invalidServiceTypes.join(',')}. Allowed service_types are: ${serviceTypes.join(',')}")
sh 'exit 1'
}
value_binding1['service_type_norm'] = value_binding1['service_type'].join(',')
}
else{
value_binding1['service_type_norm']=''
}
// Enable backward compatibility for missing keys
enable_backward_compatibility(value_binding1)
value_binding1['prismsdk_environment'] = 'PRODUCTION' // THIS KEY WILL CHANGE ONCE 3RD CONFIRM TO STANDERIZE
if (envrn == 'stg'){
value_binding1['otel_enabled'] = true
value_binding1['prismsdk_environment'] = 'SANDBOX' // THIS KEY WILL CHANGE ONCE 3RD CONFIRM TO STANDERIZE
}
// Override hot fix and notify_channel
value_binding1['canary']['skipAnalysis'] = (env.hot_fix) ? true : value_binding1['canary']['skipAnalysis']
value_binding1['canary']['slackChannel'] = notify_channel
value_binding1['xms'] = xms
value_binding1['xmx'] = xmx
value_binding1['CLOUD_PROVIDER'] = env.CLOUD_PROVIDER
def appConfig = value_binding1['appConfigEnabled']
if (appConfig) {
def configModule = (value_binding1["module"] == 'module_less') ? repo_name : value_binding1["module"]
// read the static config from configs directory
def value_binding3 = yamlobj.getParamAsString("${repo_name}/configs/${configModule}", "application-${envrn}.yml")
// to update in values.yaml
value_binding1['staticAppConfigData'] = value_binding3
//read the dynamic config from config directory
def value_binding4 = yamlobj.getParamAsString("${repo_name}/configs/${configModule}", "application-dyn-${envrn}.yml")
// to update in values.yaml
value_binding1['dynamicAppConfigData'] = value_binding4
echo 'printing the values_binding3 and values_binding4'
print value_binding3
print value_binding4
value_binding1['vault_env'] = value_binding1['vault_env'] + '-cac'
}
gitObj.preDeleteBranch(env.helm_repo_name, helm_branch_name, app_branch)
gitObj.branchCheckOut(env.helm_repo_name, app_branch)
echo "Helm Step - Value Binding 1 - ${value_binding1}"
if (isfeatureDeployment) {
echo 'It is feature deployment'
templateObj.renderTemplate(value_binding1, value_yaml_file, "${app_helm_repo}/${ingress_val}/values.yaml")
echo 'Before YAML Linting'
sh "cat ${app_helm_repo}/${ingress_val}/values.yaml"
sh "yq . ${app_helm_repo}/${ingress_val}/values.yaml -y > a.yaml;mv a.yaml ${app_helm_repo}/${ingress_val}/values.yaml"
echo 'After YAML Linting'
sh "cat ${app_helm_repo}/${ingress_val}/values.yaml"
gitObj.add(env.helm_repo_name, "${env.helmChartsPath}/${buini}/${teamini}/${app_name}/${ingress_val}/values.yaml")
}
else {
echo 'Not a feature deployment'
templateObj.renderTemplate(value_binding1, value_yaml_file, "${app_helm_repo}/values.yaml")
echo 'Before YAML Linting'
sh "cat ${app_helm_repo}/values.yaml"
// Perform YAML linting
sh "yq . ${app_helm_repo}/values.yaml -y > a.yaml; mv a.yaml ${app_helm_repo}/values.yaml"
echo 'After YAML Linting'
sh "cat ${app_helm_repo}/values.yaml"
// creating a map from final values.yaml to check canary enforcement conditions
def valuesMap = yamlobj.getParam("${app_helm_repo}", "values.yaml")
// Canary enforcement for sp0 services
// Also will have to check for cron, worker and scheduler services
def enforceCanary = false
if (!app_helm_repo.contains("cron") && !app_helm_repo.contains("worker") && !app_helm_repo.contains("scheduler") && !app_helm_repo.contains("consumer") && (valuesMap["labels"]["priority_v2"] == "sp0" || valuesMap["labels"]["priority_v2"] == "up0") && !value_binding1["addHeadless"] && !value_binding1['dockerBuildVersion'].contains("node") && envrn == "prd") {
enforceCanary = true
}
def proceed = false
def deploymentFailureErrorMessage
if (enforceCanary) {
if (valuesMap["canary"]["enabled"] == true) {
if (valuesMap["canary"]["skipAnalysis"] == false) {
if (valuesMap["canary"]["enableManualPromotion"] == true) {
proceed = true
} else {
error("Error: Update the enableManualPromotion parameter")
}
} else {
error("Error: Update the skipAnalysis parameter")
}
} else {
error("Error: Enable canary and retry")
}
} else {
proceed = true
}
// Check if priority labels and environment conditions require critical dependency check
def criticalPriorities = ["sp0", "up0", "cp0", "sp1", "up1", "cp1"]
def priority = valuesMap["labels"]["priority_v2"]
if (criticalPriorities.contains(priority) && (envrn == "prd")) {
isDependabotCritcal = dependabotCriticalCheck(repo_name)
if (isDependabotCritcal) {
error("Error: Critical vulnerabilities found in repo: " + repo_name + " \nPlease resolve the alerts marked with CRITICAL here and retry: https://github.com/Homelab/" + repo_name + "/security/dependabot and Retry.")
}
}
def param = new constructParam()
log.info("checking if appConfig is enabled")
def isAppConfigDisabled = param.appConfigDisabledForbidden(appConfig, repo_name, envrn, dockerBuildVersion)
if (isAppConfigDisabled){
error("Error: appConfig is disabled , onboard your application with config-as-code changes")
}
sh "cat ${app_helm_repo}/values.yaml"
gitObj.add(env.helm_repo_name, "${env.helmChartsPath}/${buini}/${teamini}/${app_name}/values.yaml")
}
commit_status = gitObj.codeCommit(env.helm_repo_name, app_branch, 'Generating values yaml file')
if (commit_status == 0) {
gitObj.codePush(env.helm_repo_name, app_branch)
pr_num = gitObj.createPR(app_name, env.helm_repo_name, helm_branch_name, app_branch, 'Merge helm values file')
gitObj.mergePR(env.helm_repo_name, pr_num, app_branch)
gitObj.deleteBranch(env.helm_repo_name, helm_branch_name, app_branch)
}
}
catch (FileNotFoundException e) {
env.msg = 'Error Updating Helm Repo ' + e.toString()
env.error_msg_to_db = 'File Not Found'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
catch (Exception e) {
env.msg = 'Error Updating Helm Repo ' + e.toString()
env.error_msg_to_db += 'Error Updating in Helm repo. Git error message - ' + env.error_part_msg_to_db
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
def refresh_and_sync(String repo_name, String deployment, String envrn) {
def yamlobj = new getYamlParameter()
def deploy_values = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
def app_name = deploy_values.app_name
// def argoUrl = (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'main' || env.CHANGE_TARGET == 'master' || env.CHANGE_TARGET == 'main') ? 'prod-ops-argocd.homelab.com' : 'stg-dev-argocd.homelabtest.in'
def argoEnv = (envrn == 'ftr') ? env.ingress_val : envrn
log.info('########################### Pulling latest changes in ArgoCD. ###########################')
withCredentials([usernamePassword(credentialsId: env.argoCreds, passwordVariable: 'ARGO_PASSWORD', usernameVariable: 'ARGO_USERNAME')]) {
try {
sh """
set +x
argocd login ${env.argoURL}:443 --username ${ARGO_USERNAME} --password ${ARGO_PASSWORD} --grpc-web
argocd app get --hard-refresh ${argoEnv}-${app_name} --grpc-web
"""
}
catch (Exception e) { //added try catch block here
env.msg = "Error in hard refresh of app ${app_name} full error: ${e}"
env.error_msg_to_db += "Error in hard refresh of app ${app_name};"
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
log.info('########################### Syncing latest changes in ArgoCD. ###########################')
return_value = sh(returnStatus: true, script: "argocd app sync ${argoEnv}-${app_name} --grpc-web --http-retry-max 3 --retry-backoff-duration 1m") as Integer
if (return_value == 0) {
log.info('App synced succesfully.')
} else {
env.msg = 'App sync failed. Please check in ArgoCD UI.'
env.error_msg_to_db += "Error Argo App sync failed ${app_name};"
log.error(env.msg)
error "${env.msg}"
}
}
}
def refresh_app_of_apps(String envrn) {
try {
appofapps = env.argoIncubator
log.info('########################### Pulling latest changes in ArgoCD for App of Apps. ###########################')
withCredentials([usernamePassword(credentialsId: env.argoCreds, passwordVariable: 'ARGO_PASSWORD', usernameVariable: 'ARGO_USERNAME')]) {
sh """
set +x
argocd login ${env.argoURL}:443 --username ${ARGO_USERNAME} --password ${ARGO_PASSWORD} --grpc-web
argocd app sync ${appofapps} --grpc-web --http-retry-max 3 --retry-backoff-duration 1m || true
"""
}
}
catch (Exception e) {
env.msg = 'Error in App Sync' + e.toString()
env.error_msg_to_db += 'Error syncing app of app;'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
def update_argo_repo(String repo_name, String deployment, String argoBranch, String envrn) {
def yamlobj = new getYamlParameter()
def btObj = new buTeamMapping()
def gitObj = new gitActions()
def templateObj = new constructTemplate()
// def argo_repo_name = 'devops-argo-config'
def pr_num = ''
def commit_status = 0
def commit_id = env.COMMIT_ID
value_binding1 = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
value_binding1['CLOUD_PROVIDER'] = env.CLOUD_PROVIDER
isfeatureDeployment = (envrn == 'ftr') ? true : false
deployEnv = !value_binding1['environment'].containsKey(envrn) && isfeatureDeployment ? 'stg' : envrn
Map value_binding = value_binding1['environment'].collectEntries { key, value -> deployEnv.matches(key) ? value : [ : ] }
value_binding1.remove('environment')
value_binding1.putAll(value_binding)
value_binding1['environment'] = envrn
def buini = btObj.get_bu_initials(value_binding1.bu)
def bu = value_binding1.bu
def teamini = btObj.get_team_initials(value_binding1.team)
def team = value_binding1.team
def app_name = value_binding1.app_name
def filename = "${teamini}-${app_name}.yaml"
def helm_values_path = "${env.helmChartsPath}/${buini}/${teamini}/${app_name}"
def app_branch = app_name + '-' + argoBranch
def ingress_val = value_binding1.ingress_val ?: (env.CHANGE_ID) ? "pr-${CHANGE_ID}" : "${BRANCH_NAME}"
value_binding1['helm_version'] = value_binding1.helm_version ?: env.defaultHelmChartVersion
if (isfeatureDeployment) {
value_binding1['env_ns'] = ingress_val
value_binding1['helm_values_path'] = "${env.helmChartsPath}/${buini}/${teamini}/${app_name}/${ingress_val}"
filename = ingress_val + '-' + "${app_name}.yaml"
app_branch = ingress_val + '-' + app_branch
}
else {
value_binding1['env_ns'] = envrn
value_binding1['helm_values_path'] = "${env.helmChartsPath}/${buini}/${teamini}/${app_name}"
filename = "${teamini}-${app_name}.yaml"
}
value_binding1['server'] = env.clusterName
value_binding1['clusterName'] = env.clusterName
value_binding1['branch_name'] = argoBranch
value_binding1['buini'] = buini
value_binding1['teamini'] = teamini
value_binding1['priority_v2'] = value_binding1.priority_v2 ?: 'cp3'
value_binding1['primary_owner'] = value_binding1.primary_owner != null ? value_binding1.primary_owner.split('@')[0] : value_binding1.team
value_binding1['secondary_owner'] = value_binding1.secondary_owner != null ? value_binding1.secondary_owner.split('@')[0] : value_binding1.team
value_binding1['argoAppNS'] = env.argoAppNS
value_binding1['commit_id'] = commit_id
log.info(commit_id)
gitObj.preDeleteBranch(env.argo_repo_name, argoBranch, app_branch)
gitObj.branchCheckOut(env.argo_repo_name, app_branch)
echo "Argo Step - Value Binding 1 - ${value_binding1}"
dir(env.argo_repo_name) {
def dirExists = sh(script: "cat ${env.argoAppsPath}/${filename}", returnStatus: true)
if ( dirExists != 0 ) {
sh "mkdir -p ${env.argoAppsPath}"
sh "touch ${env.argoAppsPath}/${filename}"
}
sh "chmod -R 777 ${env.argoAppsPath}/${filename}"
templateObj.renderTemplate(value_binding1, 'argoApp.yaml', "${env.argoAppsPath}/${filename}")
sh "yq . ${env.argoAppsPath}/${filename} -y > a.yaml;mv a.yaml ${env.argoAppsPath}/${filename}"
sh "cat ${env.argoAppsPath}/${filename}"
}
gitObj.add(env.argo_repo_name, "${env.argoAppsPath}/${filename}")
commit_status = gitObj.codeCommit(env.argo_repo_name, app_branch, "onboarding ${deployment} app to ARGO")
if (commit_status == 0) {
gitObj.codePush(env.argo_repo_name, app_branch)
pr_num = gitObj.createPR(app_name, env.argo_repo_name, argoBranch, app_branch, 'Merge argo application configuration')
gitObj.mergePR(env.argo_repo_name, pr_num, app_branch)
gitObj.deleteBranch(env.argo_repo_name, argoBranch, app_branch)
}
}
def calculate_active_processors(String cpu_request) {
// If lowercase m is present
if (cpu_request.contains('m')) {
// Remove m
cpu_request = cpu_request.replaceAll('m', '')
// If this fails means invalid input is given with m, so we can let the pipeline fail here
// Convert to double, divide by thousand to get another double, round it off and then convert to integer.
return Math.ceil(cpu_request.toDouble() / 1000).toInteger()
}
return Math.ceil(cpu_request.toDouble()).toInteger()
}
def enable_backward_compatibility(def bindings) {
// def primaryEmailInitial = bindings.primary_owner.tokenize( '@' )[0]: null
// def secondaryEmailInitial = bindings.secondary_owner.tokenize( '@' )[0]: null
def dparam_obj = new getDockerParams()
bindings['registry'] = env.registry
def canary_default = [
'progressDeadlineSeconds': 300,
'analysisInterval': '120s',
'analysisThreshold': 5,
'analysisMaxWeight': 5,
'analysisStepWeight': 5,
'analysisMetrics':[
'thresholdRangeMin': 0.99,
'interval': '1m'],
'skipAnalysis': false
]
def statefulset_default = [
'updateStrategy': 'RollingUpdate',
'volumeType': 'dynamic',
'dynamicVolume':[
'accessMode': 'ReadWriteMany',
'mountPath': '/opt/data',
'size': '5Gi',
'storageClass': ''],
'staticVolume':[
'accessMode': 'ReadWriteMany',
'size': '5Gi',
'mountPath': '/opt/data',
'storageClass': '',
'volumeHandle': '',
'csiDriver': '']
]
bindings['hostAliases'] = bindings.hostAliases ?: false
if (env.CLOUD_PROVIDER == 'AWS') {
bindings['nodeSelector'] = bindings.nodeSelector ?: (env.cicd_environment == 'stg' || env.cicd_environment == 'ftr') ? bindings.bu : ( env.cicd_environment == 'int' ? bindings.bu + '-int' : bindings.team )
bindings['nodeSelectorValue'] = 'dedicated'
}
else if (env.CLOUD_PROVIDER == 'GCP') {
if (env.cicd_environment == 'int') {
bindings['nodeSelector'] = bindings.nodeSelector ?: 'cloud.google.com/compute-class'
} else {
bindings['nodeSelector'] = bindings.nodeSelector ?: 'dedicated'
}
bindings['nodeSelectorValue'] = bindings.nodeSelectorValue ?: 'megatetra'
}
bindings['triggers'] = bindings.triggers ?: false
bindings['host'] = bindings.host ?: false
bindings['hosts'] = bindings.hosts ?: false
bindings['grpc_host'] = bindings.grpc_host ?: false
bindings['grpc_hosts'] = bindings.grpc_hosts ?: false
bindings['serviceAccount'] = bindings.serviceAccount ?: false
bindings['ingress_annotations'] = bindings.ingress_annotations ?: ''
bindings['canary'] = bindings.canary ?: canary_default
bindings['minCanaryReplicas'] = bindings.canary?.minCanaryReplicas ?: bindings.minCanaryReplicas ?: bindings.as_min
bindings['maxCanaryReplicas'] = bindings.canary?.maxCanaryReplicas ?: bindings.maxCanaryReplicas ?: bindings.as_max
bindings['enableManualPromotion'] = bindings.canary?.enableManualPromotion ?: bindings.enableManualPromotion ?: false
bindings['cpu_limit'] = bindings.cpu_limit ?: bindings.cpu_request
bindings['slowStartWindow'] = bindings.slowStartWindow ?: false
bindings['slowStartAggression'] = bindings.slowStartAggression ?: '1'
bindings['slowStartMinPercent'] = bindings.slowStartMinPercent ?: '10'
bindings['lifecycle'] = bindings.lifecycle ?: false
bindings['maxSurge'] = bindings.maxSurge ?: '50'
bindings['as_down_pod_count'] = bindings.as_down_pod_count ?: '2'
bindings['as_up_pod_count'] = bindings.as_up_pod_count ?: '2'
bindings['as_up_pod_percentage'] = bindings.as_up_pod_percentage ?: '10'
bindings['createContourGateway'] = bindings.createContourGateway ?: false
bindings['service_annotations'] = bindings.service_annotations ?: false
bindings['pod_annotations'] = bindings.pod_annotations ?: false
bindings['kind'] = bindings.kind ?: 'deployment'
bindings['statefulset'] = bindings.statefulset ?: statefulset_default
bindings['contourResponseTimeout'] = bindings.contourResponseTimeout ?: false
bindings['pdbMinAvailable'] = bindings.pdbMinAvailable ?: ''
bindings['pdbMaxUnavailable'] = bindings.pdbMaxUnavailable ?: '10%'
// Just to keep compatibility for services which still use grpc_port
// If someone has supplied primary_port, then it is used
// Else we check for grpc_port, and that is used
// If none of the above is supplied, then app_port is used just like normal flow
bindings['primary_port'] = bindings.primary_port ?: bindings.grpc_port ?: bindings.app_port
bindings['grpc_port'] = bindings.grpc_port ?: false
// bindings['xmx'] = bindings.xmx ?: '50.0'
// bindings['xms'] = bindings.xms ?: '50.0'
bindings['enableWebsocket'] = bindings.enableWebsocket ?: false
bindings['external_secrets_annotations'] = bindings.external_secrets_annotations ?: ''
bindings['liveness_failure_threshold'] = bindings.liveness_failure_threshold ?: '5'
bindings['liveness_period_seconds'] = bindings.liveness_period_seconds ?: bindings.team_norm == 'ml-platform' ? '5' : '10'
bindings['liveness_success_threshold'] = bindings.liveness_success_threshold ?: '1'
bindings['liveness_timeout_seconds'] = bindings.liveness_timeout_seconds ?: '2'
bindings['readiness_failure_threshold'] = bindings.readiness_failure_threshold ?: '5'
bindings['readiness_period_seconds'] = bindings.liveness_period_seconds ?: bindings.team_norm == 'ml-platform' ? '5' : '10'
bindings['readiness_success_threshold'] = bindings.liveness_success_threshold ?: '1'
bindings['readiness_timeout_seconds'] = bindings.liveness_timeout_seconds ?: '2'
bindings['addon_ports'] = bindings.addon_ports ?: false
validateRequiredMetadata(bindings)
bindings['priority_v2'] = bindings.priority_v2 ?: 'cp3'
bindings['primary_owner'] = bindings.primary_owner != null ? bindings.primary_owner.split('@')[0] : bindings.team_norm
bindings['secondary_owner'] = bindings.secondary_owner != null ? bindings.secondary_owner.split('@')[0] : bindings.team_norm
if (env.CLOUD_PROVIDER == 'AWS') {
if (bindings.otel_enabled == null) {
bindings['otel_enabled'] = (bindings.priority_v2 == 'cp1' || bindings.priority_v2 == 'up1') ? true : false
}
else {
bindings['otel_enabled'] = bindings.otel_enabled
}
}
else if (env.CLOUD_PROVIDER == 'GCP') {
echo "Checking for otel value - ${bindings.otel_enabled}"
if (bindings.otel_enabled == null) {
bindings['otel_enabled'] = true
}
else {
bindings['otel_enabled'] = bindings.otel_enabled
}
// Setting metrics mode -> avaliable options: telegraf | otel | dual
echo "Setting metrics mode - ${bindings.metrics_mode}"
// Setting default values
bindings['telegraf_metrics'] = false
bindings['otel_metrics'] = false
bindings['metrics_mode'] = bindings.metrics_mode?.toLowerCase()
def samplerArg = bindings.otel_traces_sampler_arg
def isValidSamplerArg = false
if (samplerArg != null && (samplerArg instanceof Float || samplerArg instanceof Double)) {
if (samplerArg >= 0.0 && samplerArg <= 1.0) {
isValidSamplerArg = true
}
}
bindings['otel_traces_sampler_arg'] = isValidSamplerArg ? samplerArg : '0.1'
log.info("1 - otel_traces_sampler_arg - ${bindings.otel_traces_sampler_arg}")
log.info("2 - ${bindings['otel_traces_sampler_arg']}")
// Enabling metrics based on mode
if (bindings.metrics_mode == 'telegraf') {
bindings['telegraf_metrics'] = true
}
else if (bindings.metrics_mode == 'otel') {
bindings['otel_metrics'] = true
}
else if (bindings.metrics_mode == 'dual') {
bindings['telegraf_metrics'] = true
bindings['otel_metrics'] = true
}
else {
bindings['metrics_mode'] = 'telegraf'
bindings['telegraf_metrics'] = true
}
}
bindings['command'] = bindings.command
if (bindings.command == null) {
bindings['command'] = get_default_command(bindings['dockerBuildVersion'])
}
bindings['as_down_stable_window'] = bindings.as_down_stable_window ?: '1800'
bindings['podDistributionSkew'] = bindings.podDistributionSkew ?: false
bindings["appConfigEnabled"] = bindings.appConfigEnabled ?: false
bindings["addHeadless"] = bindings.addHeadless?: false
}
def get_value_yaml_file(def dockerBuildVersion) {
switch (dockerBuildVersion) {
case ~/^maven-.*/: return 'values.yaml'
case ~/^node-.*/: return 'node-values.yaml'
case ~/^python-.*/: return 'python-values.yaml'
case ~/^go.*/: return 'go-values.yaml'
case 'php': return 'php-values.yaml'
case 'gradle': return 'values.yaml'
}
}
def get_default_command(def dockerBuildVersion) {
switch (dockerBuildVersion) {
case ~/^maven-.*/: return 'java'
case ~/^node-.*/: return 'pm2-runtime'
case ~/^go.*/: return '/app/server'
case 'php': return 'apache2-foreground'
case 'gradle': return 'java'
default: return null
}
}
def validateRequiredMetadata(def bindings) {
def requiredParams = ['primary_owner', 'secondary_owner', 'priority_v2', 'service_type']
def serviceOwners = ['primary_owner', 'secondary_owner']
// Validate if required parameters are present
for (param in requiredParams) {
if (bindings[param] == null || bindings[param] == '') {
env.msg = 'You have not supplied ' + param + '. Exiting the pipeline.'
log.error(env.msg)
sh 'exit 1'
}
}
// Validate if the owners are valid or not
if (env.cicd_environment == 'dev' || env.cicd_environment == 'ftr' || env.cicd_environment == 'stg') {
return
}
for (param in serviceOwners) {
final String owner = bindings[param]
final String url = "https://pulse.homelabgcp.in/api/anonymous-User/userexist?email=${owner}"
final def(String response, String code) = sh(returnStdout: true, script: """
set +x
curl -s -X GET -w '\n%{response_code}' $url
set -x
""").trim().tokenize('\n')
if (code != "200") {
// Let's not break the pipeline in case the API fails
log.info("Received ${code} code from Pulse while checking for user. Skipping the checks further and letting the pipeline proceed.")
} else {
// Check if the user exists
def jqCommand = "echo '${response}' | jq -r '.exists'"
def userExists = sh(returnStdout: true, script: """
set +x
${jqCommand}
set -x
""").trim()
if (userExists != "true") {
env.msg = "Invalid value provided in ${param}. Check if the user ${owner} exists"
log.error(env.msg)
sh 'exit 1'
}
}
}
}
def dependabotCriticalCheck(def repoName) {
def repo = repoName
def alerts
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_CRED}", usernameVariable:'user', passwordVariable: 'token')]) {
validateDependabot = httpRequest httpMode: 'GET',
customHeaders: [
[name: 'Accept', value: 'application/vnd.github+json'],
[maskValue: true, name: 'Authorization', value: 'Bearer ' + token]
],
url: "https://api.github.com/repos/homelab/${repo}/dependabot/alerts?state=open&per_page=100",
validResponseCodes: '200',
timeout: 10
alerts = readJSON(text: validateDependabot.content)
}
// if (!alerts) {
// println "Failed to fetch alerts for ${repo}."
// return false
// }
// Parse the JSON response
if (!alerts) {
println "No alerts found for ${repo}."
return false
}
def criticalCount = 0
alerts.each { alert ->
def severity = alert?.security_vulnerability?.severity
if (severity == "critical") {
criticalCount++
}
}
if (criticalCount > 0) {
println "Critical vulnerabilities found in ${repo}: ${criticalCount}"
return true // Critical vulnerabilities found
} else {
println "No critical vulnerabilities in ${repo}."
return false // No critical vulnerabilities found
}
}
+32
View File
@@ -0,0 +1,32 @@
package com.homelab.stages
def run(Map param){
//get the jar for deployment, get it from the params module
//run the ansible playbook
//create the inventory file with given ip in parameters
def server_ip = param.run_automation.server_ip
def app_name = param.run_automation.app_name
def healthcheck_api = param.run_automation.healthcheck_api
def app_port = param.run_automation.app_port
def repo_name = param.repo_name
deploy(server_ip,repo_name,app_name,healthcheck_api,app_port)
}
def deploy(String server_ip, String repo_name, String app_name, String healthcheck_api, String app_port){
try{
stage('Deploying JAR'){
sh "echo '$server_ip' > host_file.txt"
echo "inventory created"
def playbook_content = libraryResource 'com/homelab/deployJar.yaml'
writeFile file:"deployJar.yaml", text: playbook_content
sh(returnStatus: true, script: "ansible-playbook -i host_file.txt -u 'ubuntu' -e 'env=stage' -e 'app_name=${app_name}' -e 'repo_name=${repo_name}' -e 'healthcheck_api=${healthcheck_api}' -e 'app_port=${app_port}' deployJar.yaml -v")
}
}
catch( Exception e) {
env.msg = "Error while deploying JAR. Please check console output for more details."
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
@@ -0,0 +1,144 @@
package com.homelab.stages
import com.homelab.utilities.getYamlParameter
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatterBuilder
def run(String repo_name, def deployment_order, def tag, def build_team, def dockerBuildVersion, String notify_channel){
def yamlobj = new getYamlParameter()
def applicationNames = []
def jobStatusMap=["SUCCESS": "BUILD_STATUS_COMPLETED","FAILURE": "BUILD_STATUS_FAILED","UNSTABLE":"BUILD_STATUS_FAILED"]
def build_user = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
for (deployment in deployment_order){
def value_binding = yamlobj.getParam("${repo_name}/deployments","${deployment}.yaml")
applicationNames.add(value_binding['app_name'])
}
def end_time = getDateTime()
def working_env = (env.CLOUD_PROVIDER == "GCP")? "gcp_${env.cicd_environment}" : env.cicd_environment
def jsonMap = [:]
jsonMap["hot_fix"] = env.hot_fix ? true : false
jsonMap["job_name"] = env.JOB_NAME ? env.JOB_NAME.split('/')[0] : "unknown"
jsonMap["build_no"] = env.BUILD_NUMBER
jsonMap["image"] = tag
jsonMap["applications"] = applicationNames
jsonMap["job_status"] = jobStatusMap[currentBuild?.currentResult]
jsonMap["err_msg"] = env.error_msg_to_db
jsonMap["end_time"] = end_time
jsonMap["email"] = currentBuild?.rawBuild?.getCause(Cause.UserIdCause)?.getUserId()
jsonMap["start_time"] = env.STARTTIME
jsonMap["build_team"] = build_team
jsonMap["docker_build_version"] = dockerBuildVersion
jsonMap["notify_channel"] = notify_channel
jsonMap["branch_name"] = env.CHANGE_ID ? env.CHANGE_BRANCH : env.BRANCH_NAME
jsonMap["commit_id"] = env.commit_id
jsonMap["deploy_argo"] = env.deployArgo != null ? env.deployArgo.toBoolean() : false
jsonMap["pr_number"] = env.CHANGE_ID ? env.CHANGE_ID.toInteger() : 0
final String baseUrl
switch (env.cicd_environment) {
case 'prd':
case 'int':
baseUrl = 'https://ringmaster-api.homelabgcp.in/api/v1/key/cicd/cd/update'
break
default:
baseUrl = 'https://ringmaster-api.admin.homelabgcp.in/api/v1/key/cicd/cd/update'
}
final String url = "${baseUrl}?workingEnv=${working_env}"
final String header = "Content-Type: application/json"
final String jsonData = writeJSON returnText: true, json: jsonMap
if (build_user == "ringmaster-bot"){
callApi(url, header, jsonData)
} else{
def newCICD_Payload = [:]
newCICD_Payload["repo_name"] = repo_name
newCICD_Payload["source_branch"] = env.CHANGE_ID ? env.CHANGE_BRANCH : env.BRANCH_NAME
newCICD_Payload["pull_request_number"] = env.CHANGE_ID ? env.CHANGE_ID.toInteger() : 0
newCICD_Payload["env"] = env.cicd_environment
newCICD_Payload["job_name"] = env.JOB_NAME ? env.JOB_NAME.split('/')[0] : "UNKNOWN"
newCICD_Payload["sub_job_name"] = env.JOB_NAME ? env.JOB_NAME.split('/')[1] : "UNKNOWN"
newCICD_Payload["build_number"] = env.BUILD_NUMBER.toInteger()
newCICD_Payload["image_tag"] = tag
newCICD_Payload["build_detailed_error"] = env.error_msg_to_db
switch (env.cicd_environment) {
case 'prd':
case 'int':
cicdBaseUrl = 'http://turbo-turtle.homelabgcp.in'
break
default:
cicdBaseUrl = 'http://turbo-turtle.admin.homelabgcp.in'
}
final String newCICD_JSON = writeJSON returnText: true, json: newCICD_Payload
//log.info("New CICD JSON - ${newCICD_JSON}")
final String newCICD_URL = cicdBaseUrl + "/api/v1/ci/jenkins/callback"
final String newCICD_Header = "Content-Type: application/json"
// Use a temporary file to store the JSON payload
// This avoids shell quoting issues completely
final String jsonFilePath = "cicd_payload_${env.BUILD_NUMBER}_${System.currentTimeMillis()}.json"
try {
// 1. Write the JSON payload to a temporary file
// The writeJSON step ensures the content is valid JSON, escaping internal characters
writeFile(file: jsonFilePath, text: writeJSON(returnText: true, json: newCICD_Payload))
final String newCICD_JSON_log = readFile(file: jsonFilePath)
log.info("New CICD JSON (from file) - ${newCICD_JSON_log.take(500)}...") // Log a snippet
final def(String response, String code) = sh(
returnStdout: true,
script: """
curl -s -X POST \\
-H '$newCICD_Header' \\
-w '\\n%{response_code}' \\
$newCICD_URL \\
-d @$jsonFilePath
"""
).trim().tokenize("\n")
if (code != "200") {
log.error("CICD Application API call failed with error code - ${code}, response - ${response}")
}
} catch (Exception e) {
log.error("CICD Application API call failed - " + "Error: " + e.toString())
throw e
} finally {
sh(script: "rm -f ${jsonFilePath}", returnStatus: true)
}
}
}
def callApi(String url, String header, String jsonData){
try{
withCredentials([usernamePassword(credentialsId: "ringmaster-token", usernameVariable:'user', passwordVariable: 'token')]){
final def(String response, String code) = sh(returnStdout: true, script: "curl -s -X POST -H '$header' -H 'Authorization: $token' -w '\\n%{response_code}' $url -d '$jsonData'").trim().tokenize("\n")
log.info("HTTP response status code : ${code}")
if(code != "200"){
log.error("API call failed with error code - ${code}, response - ${response}")
currentBuild.result = 'FAILURE'
}
}
}
catch ( Exception e) {
log.error("API call failed")
currentBuild.result = 'FAILURE'
throw e
}
}
@NonCPS
def getDateTime() {
// Get the current date and time in IST
def currentDateTime = ZonedDateTime.now()
// Create a formatter for the desired pattern
def formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss")
.appendOffset("+HH:mm", "+00:00")
.toFormatter()
// Format the current date and time using the formatter
def formattedDateTime = currentDateTime.format(formatter)
return formattedDateTime;
}
+185
View File
@@ -0,0 +1,185 @@
package com.homelab.stages
import com.homelab.utilities.constructTemplate
import com.homelab.utilities.gitActions
import com.homelab.utilities.buTeamMapping
import com.homelab.utilities.getDockerParams
import com.homelab.stages.multiBranchPipeline
def run(Map params) {
def btObj = new buTeamMapping()
def gitObj = new gitActions()
def environments = ['stg':'develop', 'int':'pre-prod', 'prd':'main', 'ftr':'feature']
final Map<?, ?> modifiedParams = new HashMap<>(params)
// def helm_repo_name = 'devops-helm-charts'
// def argoRepo = "devops-argo-config"
def deployment_order = modifiedParams.app_names.replaceAll('\\s', '').split(', ') as List
modifiedParams['deployment_order'] = deployment_order
stage('Create Jenkinsfile') {
jenkinsfileCreate(modifiedParams)
}
modifiedParams['bu'] = btObj.get_bu_initials(modifiedParams['bu'])
modifiedParams['team'] = btObj.get_team_initials(modifiedParams['team'])
log.info(modifiedParams)
stage('Update Helm charts') {
gitObj.clone("${WORKSPACE}", "${env.helm_repo_name}", null)
environments.each {
modifiedParams['environment'] = it.key
def helm_branch_name = it.value
for (deployment in deployment_order) {
modifiedParams['app_name'] = deployment
modifiedParams['host'] = helm_branch_name == 'feature' ? "INGRESS_PR_NUMBER-${deployment}.dev.internal.homelabtest.in" : "${deployment}.${it.key}.internal.homelabtest.in"
modifiedParams['host'] = (helm_branch_name == 'pre-prod') ? "${deployment}.${modifiedParams.bu}.internal.homelab.co" : modifiedParams.host
//If branch is main, then assuming the env as prod
//Making nodeSelector changes to hypercore or hypermem & based on arch, so that it moves to common node pool
if (helm_branch_name == 'main') {
def cpu = modifiedParams['cpu_request']
def memory = modifiedParams['memory_request']
def nodeSelector = 'hypercore'
if (cpu.contains('m')) {
def numericValue = cpu.replaceAll('\\D+', '').toDouble()
def gb = numericValue / 1000
cpu = gb.toString()
}
def mem_dgt = memory.replaceAll('\\D+', '').toDouble()
memory = memory.contains('Mi') ? mem_dgt / 1024 : mem_dgt
def ratio = cpu.toDouble() / memory.toDouble()
def arch = modifiedParams['arch']
def graviton_required = false
if (arch == 'arm64') {
graviton_required = true
}
if (ratio <= 1 / 3) {
if (graviton_required) {
nodeSelector = 'hypermem-arm64'
} else {
nodeSelector = 'hypermem'
}
} else {
if (graviton_required) {
nodeSelector = 'hypercore-arm64'
} else {
nodeSelector = 'hypercore'
}
}
modifiedParams['nodeSelector'] = nodeSelector
}
updateHelmRepo(modifiedParams, env.helm_repo_name, helm_branch_name)
}
}
}
// stage("Update Argo Application"){
// gitObj.clone("${WORKSPACE}", "${argoRepo}", null)
// environments.each{
// modifiedParams["environment"] = it.key
// def argo_branch_name = it.value
// for (deployment in deployment_order){
// modifiedParams["app_name"] = deployment
// updateArgoRepo(modifiedParams,argoRepo,argo_branch_name)
// }
// }
// }
stage('Create ECR repo') {
def buildDockerObj = new getDockerParams()
def modules = buildDockerObj.getModules(modifiedParams['repo_name'])
if (modules == null) {
modules = ['module_less']
}
for (module in modules) {
def ecr_repo_name = (module == 'module_less') ? "${modifiedParams.team}/${modifiedParams.repo_name.toLowerCase()}" : "${modifiedParams.team}/${modifiedParams.repo_name.toLowerCase()}/${module}"
try {
sh "aws ecr create-repository --repository-name ${ecr_repo_name}"
}
catch (Exceptione) {
log.info('repository already present')
}
}
}
stage('Create Multibranch pipeline') {
def multiBranchPipelineObj = new multiBranchPipeline()
multiBranchPipelineObj.applicationOnboard(params['bu'], params['repo_name'])
}
}
def jenkinsfileCreate(Map config) {
def constructObj = new constructTemplate()
def gitObj = new gitActions()
def repo_name = config.repo_name
def deployment_order = config.deployment_order
def repo_branch_name = 'eks_onboarding'
config['branch_params'] = ''
config['excludedMoudles'] = config.excludedMoudles.replaceAll('\\s', '').split(', ') as List
gitObj.clone("${WORKSPACE}", "${config.repo_name}", null)
gitObj.branchCheckOut(config.repo_name, repo_branch_name)
deployment_yaml_file = get_deployment_yaml_file(config['dockerBuildVersion'])
dir(repo_name) {
sh(script:'mkdir -p deployments/')
sh 'chmod -R 777 .'
constructObj.renderTemplate(config, 'Jenkinsfile', 'Jenkinsfile')
gitObj.add('.', 'Jenkinsfile')
constructObj.renderTemplate(config, 'config.yaml', 'config.yaml')
gitObj.add('.', 'config.yaml')
dir('deployments') {
for (deployment in deployment_order) {
config['app_name'] = deployment
constructObj.renderTemplate(config, deployment_yaml_file, deployment + '.yaml')
gitObj.add('.', deployment + '.yaml')
}
}
}
gitObj.codeCommit(repo_name, repo_branch_name, 'Generating build and deployments files')
gitObj.codePush(repo_name, repo_branch_name)
}
def updateHelmRepo(Map config, String helm_repo_name, String helm_branch_name) {
def constructObj = new constructTemplate()
def gitObj = new gitActions()
def filepath = "${env.helmChartsPath}/${config.bu}/${config.team}/${config.app_name}"
def file_name = 'values_properties.yaml'
def app_branch = config.app_name + '-' + helm_branch_name + '-helm'
def pr_num = ''
def commit_status = 0
gitObj.branchCheckOut(env.helm_repo_name, helm_branch_name)
gitObj.branchCheckOut("${env.helm_repo_name}", "${app_branch}")
// create filepath
dir(env.helm_repo_name) {
sh(script:"mkdir -p ${filepath}" ,returnStdout:true)
sh 'chmod -R 777 .'
constructObj.renderTemplate(config, file_name, filepath + '/' + file_name)
}
// git commit and push
gitObj.add(env.helm_repo_name, filepath + '/' + file_name)
commit_status = gitObj.codeCommit(env.helm_repo_name, app_branch, 'Generating properties values yaml file')
if (commit_status == 0) {
gitObj.codePush(env.helm_repo_name, app_branch)
pr_num = gitObj.createPR(config.app_name, env.helm_repo_name, helm_branch_name, app_branch, 'Merge helm values properties')
gitObj.mergePR(env.helm_repo_name, pr_num, app_branch)
gitObj.deleteBranch(env.helm_repo_name, helm_branch_name, app_branch)
}
}
def get_deployment_yaml_file(def dockerBuildVersion) {
switch(dockerBuildVersion) {
case ~/^maven-.*/: return 'deployment.yaml'
case ~/^node-.*/: return 'node-deployment.yaml'
case ~/^python-.*/: return 'python-deployment.yaml'
case ~/^go.*/: return 'go-deployment.yaml'
case 'gradle': return 'gradle-deployment.yaml'
case 'php': return 'php-deployment.yaml'
}
}
+24
View File
@@ -0,0 +1,24 @@
package com.homelab.stages
def run(String repo_name){
stage(stageName("Check for Hot Fix")){
dir(repo_name){
if (env.CHANGE_ID) {
def source_branch_name = env.CHANGE_BRANCH
log.info("Source-Branch : ${source_branch_name}")
source_branch_name = source_branch_name.toLowerCase()
if (source_branch_name.contains("hotfix_")){
env.hot_fix = true
log.info("***************** Enabling Hot-fix workflow *****************")
}
else if (source_branch_name.matches('^revert-\\d+-.+$')){
env.hot_fix = true
log.info("***************** Enabling Hot-fix workflow for revert branch *****************")
}
}
else {
log.info("**** Branch: ${env.BRANCH_NAME}, So Skipping Hot-fix check ****")
}
}
}
}
+35
View File
@@ -0,0 +1,35 @@
package com.homelab.stages
// New stage — the homelab equivalent of the real system's config.yaml
// convention. Verified by reading constructParam.groovy directly first:
// it does NOT read config.yaml itself — helmGenerator.groovy renders
// devops-lib's own resources/com/homelab/config.yaml TEMPLATE into each
// SERVICE repo's own root as a real, committed config.yaml, and various
// stages then read that file directly (runHooks.groovy's
// hooks.<phase> block, buildNode.groovy's s3_path/pbac_scope_name, etc.).
//
// This is the simplified equivalent: if the checked-out repo has a
// config.yaml at its root, read it and merge its keys into the
// pipeline's config Map — repo-committed values override whatever the
// Jenkinsfile call passed in. Mutates config in place (Groovy Maps are
// passed by reference), so every later stage that already has a
// reference to the same object sees the merge — nothing needs to be
// reassigned or returned.
//
// Entirely optional — a repo with no config.yaml just keeps running on
// the Jenkinsfile call's defaults, same as before this existed.
def run(Map config) {
stage(stageName('Load config.yaml')) {
dir(config.repo_name) {
if (fileExists('config.yaml')) {
def repoConfig = readYaml file: 'config.yaml'
if (repoConfig instanceof Map) {
config.putAll(repoConfig)
log.info("loadConfig: merged config.yaml — keys: ${repoConfig.keySet()}")
}
} else {
log.info('loadConfig: no config.yaml in repo root, using Jenkinsfile call defaults only.')
}
}
}
}
@@ -0,0 +1,73 @@
package com.homelab.stages
import jxl.*
import hudson.util.PersistedList
import jenkins.model.Jenkins
import jenkins.branch.*
import jenkins.plugins.git.*
import org.jenkinsci.plugins.workflow.multibranch.*
import com.cloudbees.hudson.plugins.folder.*
import org.jenkinsci.plugins.github_branch_source.*
import org.jenkinsci.plugins.workflow.libs.*
import hudson.scm.SCM
import hudson.plugins.git.*
import net.gleske.scmfilter.impl.trait.*
def applicationOnboard(String foldername, String repo_name) {
// Bring some values in from ansible using the jenkins_script modules wierd "args" approach (these are not gstrings)
String folderName = "${foldername}"
String repoName = "${repo_name}"
String scriptPath = "Jenkinsfile"
String gitRepo = "https://github.com/Homelab/${repo_name}.git"
String mBPName = "${repo_name}-cicd"
String credentialsId = env.GITHUB_CRED
Jenkins jenkins = Jenkins.instance // saves some typing
// Get the folder where this job should be
// def folder = jenkins.getItem(folderName)
// //Create the folder if it doesn't exist
// if (folder == null) {
// folder = jenkins.createProject(Folder.class, folderName)
// }
// Multibranch creation/update
WorkflowMultiBranchProject mbp
def view = jenkins.getView(foldername)
Item item = jenkins.getItem(mBPName)
if ( item != null ) {
// Update case
mbp = (WorkflowMultiBranchProject) item
} else {
// Create case
mbp = jenkins.createProject(WorkflowMultiBranchProject.class, mBPName)
}
// Configure the script this MBP uses
mbp.getProjectFactory().setScriptPath(scriptPath)
def implicit = false
def defaultVersion = "master"
def traits = []
GitHubSCMSource gitHubSCMSource = new GitHubSCMSource("Homelab", repoName, gitRepo, implicit)
gitHubSCMSource.credentialsId = credentialsId
BranchDiscoveryTrait branchDiscoveryTrait = new BranchDiscoveryTrait(3)
OriginPullRequestDiscoveryTrait pullRequestTrait = new OriginPullRequestDiscoveryTrait(1)
WildcardSCMHeadFilterTrait wildcardSCMHeadFilterTrait = new WildcardSCMHeadFilterTrait('gcp-main*','','','*')
traits.add(branchDiscoveryTrait)
traits.add(pullRequestTrait)
traits.add(wildcardSCMHeadFilterTrait)
gitHubSCMSource.setTraits(traits)
BranchSource branchSource = new BranchSource(gitHubSCMSource)
NoTriggerBranchProperty noTriggerBranchProperty = new NoTriggerBranchProperty()
BranchProperty[] ntbp = [noTriggerBranchProperty]
branchSource.setStrategy(new DefaultBranchPropertyStrategy(ntbp))
PersistedList sources = mbp.getSourcesList()
sources.clear()
sources.add(branchSource)
view.add(mbp)
}
+14
View File
@@ -0,0 +1,14 @@
package com.homelab.stages
// Rewritten for this homelab. The original posted to Homelab's real Slack
// workspace, an internal deployment-tracker API, and Ringmaster (their
// deployment-approval system) — none of which exist here. This just logs
// to the build console, which is all a solo homelab pipeline needs;
// swap in a real notification channel later if you ever want one (e.g.
// a Gitea webhook back to a commit status, or your own Slack if you set
// one up).
def run(Map config) {
stage(stageName('Notify')) {
log.info("Build ${currentBuild.currentResult} for ${config.repo_name} — tag ${env.TAG} — ${env.BUILD_URL}")
}
}
+134
View File
@@ -0,0 +1,134 @@
package com.homelab.stages
// Simplified from the real devops-lib's vars/runHooks.groovy — same core
// mechanism: repo-declared scripts under hooks.<phase> in config.yaml,
// blocking by default with an explicit `blocking: false` opt-out to
// advisory (log + continue), repo-relative path validation (no absolute
// paths, no `..` traversal), interpreter resolution (explicit >
// extension > shebang), python venv + requirements provisioning, a
// timeout per hook. Dropped: the deploy-phase cdHookRunner reuse (we
// don't have a separate deploy-phase repo checkout — everything happens
// in one pipeline) and the Turbo-Turtle callback system (TT_* env vars
// renamed to HOOK_* — same shape, no Homelab-specific meaning attached).
//
// Called as runHooksObj.run(config, 'pre_build') / 'post_build' from
// vars/homelabPipeline.groovy, around the buildDocker stage.
def run(Map config, String phase) {
def hooksBlock = config?.hooks
if (!(hooksBlock instanceof Map)) {
return
}
def hooks = hooksBlock[phase]
if (!(hooks instanceof List) || hooks.isEmpty()) {
return
}
String repoName = config.repo_name
hooks.eachWithIndex { hook, idx ->
runOneHook(repoName, phase, idx, hook)
}
}
def runOneHook(String repoName, String phase, int idx, def hook) {
String name = (hook?.name ?: "${phase}-${idx}").toString()
String script = hook?.script?.toString()
String interpreter = hook?.interpreter?.toString()
String requirements = hook?.requirements?.toString()
// Blocking by default; only an explicit `blocking: false` demotes to advisory.
boolean blocking = !(hook?.blocking?.toString() == 'false')
int timeoutSeconds = 600
if (hook?.timeout_seconds) {
try { timeoutSeconds = hook.timeout_seconds.toString().toInteger() } catch (ignored) { timeoutSeconds = 600 }
}
if (!script) {
error("runHooks: ${phase}[${idx}] '${name}' has no 'script' path in config.yaml.")
}
assertRepoRelative(script, "${phase}[${idx}] '${name}' script")
if (requirements) {
assertRepoRelative(requirements, "${phase}[${idx}] '${name}' requirements")
}
stage(stageName("${phase}: ${name}")) {
container('docker-cli') {
dir(repoName) {
if (!fileExists(script)) {
error("runHooks: ${phase} hook '${name}' script not found in repo: ${script}")
}
List<String> hookEnv = [
"HOOK_PHASE=${phase}",
"HOOK_NAME=${name}",
"HOOK_REPO=${repoName}",
"HOOK_IMAGE_TAG=${env.TAG ?: ''}",
]
String runCmd = buildRunCommand(script, interpreter, requirements)
log.info("runHooks: executing ${phase} hook '${name}' (${script}), blocking=${blocking}, timeout=${timeoutSeconds}s")
withEnv(hookEnv) {
try {
timeout(time: timeoutSeconds, unit: 'SECONDS') {
sh(script: runCmd)
}
} catch (Exception e) {
if (blocking) {
log.error("runHooks: blocking ${phase} hook '${name}' failed: ${e}")
throw e
}
log.warning("runHooks: advisory ${phase} hook '${name}' failed (non-blocking): ${e}")
currentBuild.description = (currentBuild.description ? currentBuild.description + " | " : "") + "hook(${name}) advisory-failed"
}
}
}
}
}
}
def buildRunCommand(String script, String interpreter, String requirements) {
String interp = interpreter
if (!interp) {
if (script.endsWith('.py')) {
interp = 'python3'
} else if (script.endsWith('.sh')) {
interp = 'bash'
}
}
boolean isPython = (interp == 'python3' || interp == 'python' || script.endsWith('.py'))
// docker:27-cli is minimal Alpine — only `sh` is guaranteed present.
// bash/python3 need installing on demand, unlike the real system's
// pod images which already bundle a full toolchain.
String installLine = ''
if (isPython) {
installLine = 'apk add --no-cache python3 py3-pip py3-virtualenv >/dev/null'
} else if (interp == 'bash') {
installLine = 'apk add --no-cache bash >/dev/null'
}
if (requirements && isPython) {
String py = interp ?: 'python3'
return """
set -e
${installLine}
${py} -m venv .hook_venv
. .hook_venv/bin/activate
pip install --quiet --disable-pip-version-check -r ${requirements}
${py} ${script}
""".stripIndent().trim()
}
if (interp) {
return "set -e\n${installLine}\n${interp} ${script}"
}
// No interpreter resolved — rely on the script's shebang.
return "set -e\nchmod +x ${script}\n./${script}"
}
// Rejects absolute paths and parent-directory traversal so a hook can
// only execute code that lives inside the checked-out repo.
def assertRepoRelative(String p, String what) {
if (p.startsWith('/')) {
error("runHooks: ${what} path '${p}' must be repo-relative, not absolute.")
}
if (p == '..' || p.startsWith('../') || p.contains('/../')) {
error("runHooks: ${what} path '${p}' must not traverse outside the repo ('..').")
}
}
@@ -0,0 +1,24 @@
package com.homelab.stages
def run(Map config) {
try {
def repo_name = config.repo_name
stage('Security scan') {
if (config.skip_security_scan) {
log.info('Skipping - Security Scan')
}
else {
final String url = '172.31.5.29:63232/scans'
final def(String response, String code) = sh(returnStdout: true, script: "curl -s -X POST -H 'Content-Type: application/json' -w '\\n%{response_code}' $url -d '{\"reponame\":\"$repo_name\",\"branch\":\"$BRANCH_NAME\"}'").trim().tokenize('\n')
log.info("HTTP response status code : ${code}")
log.info("Response: ${response}")
}
}
}
catch ( Exception e) {
env.msg = 'failed in security scan . Please check console output for more details.'
log.error(env.msg)
currentBuild.result = env.FAILURE
throw e
}
}
+39
View File
@@ -0,0 +1,39 @@
package com.homelab.stages
// New stage — the homelab equivalent of devops-lib's real
// refresh_and_sync (deployArgoCD.groovy step 4: `argocd app sync
// --hard-refresh`). Without this, the pipeline stops at the tag-bump
// commit and a human has to remember to click Sync in the ArgoCD UI —
// their real system never leaves that step manual. Uses the ArgoCD REST
// API directly via curl (scoped jenkins-ci token, not the argocd CLI
// binary — avoids adding another container to dind-pod.yaml just for
// this one call) against the server's internal cluster-DNS Service, not
// the ingress — same pod-to-pod reasoning as pushing to Harbor.
//
// ARGOCD_TOKEN comes from the pod env (dind-pod.yaml, ESO-managed secret)
// — not Jenkins' own credential store, staying consistent with the rest
// of this pipeline. It's marked optional there, so if it's genuinely
// empty (bootstrap token not generated yet), fail loudly here with a
// clear message rather than a confusing curl auth error.
//
// Expects in config:
// argo_app_name the Application's metadata.name, e.g. demo-go-app
// argo_server_url e.g. http://argocd-admin-prd-server.argocd.svc.cluster.local
def run(Map config) {
stage(stageName('Sync ArgoCD Application')) {
container('docker-cli') {
if (!env.ARGOCD_TOKEN?.trim()) {
log.error('ARGOCD_TOKEN is empty — the jenkins-ci account token has not been generated yet. See secretstores/argocd-jenkins-ci-token.yaml for the one-time bootstrap steps.')
error('Skipping ArgoCD sync: no token available.')
}
sh """
apk add --no-cache curl >/dev/null
curl -sf -X POST \\
-H "Authorization: Bearer \$ARGOCD_TOKEN" \\
-H "Content-Type: application/json" \\
-d '{}' \\
"${config.argo_server_url ?: 'http://argocd-admin-prd-server.argocd.svc.cluster.local'}/api/v1/applications/${config.argo_app_name}/sync"
"""
}
}
}
@@ -0,0 +1,43 @@
package com.homelab.stages
// New stage — no equivalent this simple existed before. The original's
// version (deployArgoCD.groovy's update_helm_repo, ~900 lines) ran a full
// PR workflow — branch, commit, push, create PR, merge PR, delete branch
// — against Homelab's real GitHub org, driven by a BU/team templating
// engine and canary-enforcement rules. Solo homelab, no reviewer to wait
// on: this just clones, bumps the tag with yq, commits, and pushes
// straight to main. ArgoCD picks up the change on its own next sync
// (manual, same as everything else in this repo — see the sister repo's
// README on sync policy).
//
// Expects in config:
// repo_name the app's repo name — also the first path segment
// under devops-helm-charts/values/
// service_name second path segment; same as repo_name for a
// single-service repo, different for a monorepo with
// several services sharing one repo
// helm_repo_url e.g. http://gitea.192.168.1.7.nip.io/mukul/devops-helm-charts.git
// image_tag_yq_path yq path to the tag field, e.g. .deployment.image.tag
// gitea_cred Jenkins credential ID for a Gitea push-capable token (default: gitea-ci-credentials)
//
// Values file path is computed, not passed directly:
// devops-helm-charts/values/<repo_name>/<service_name>/values.yaml
def run(Map config) {
def valuesFile = "values/${config.repo_name}/${config.service_name}/values.yaml"
stage(stageName('Update Helm chart image tag')) {
withCredentials([usernamePassword(credentialsId: config.gitea_cred ?: 'gitea-ci-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS')]) {
dir('helm-chart-repo') {
deleteDir()
def authedUrl = config.helm_repo_url.replaceFirst('http://', "http://\${GIT_USER}:\${GIT_PASS}@")
sh """
git clone ${authedUrl} .
yq -i '${config.image_tag_yq_path} = "${env.TAG}"' ${valuesFile}
git config user.email 'jenkins-ci@homelab.local'
git config user.name 'jenkins-ci'
git commit -am 'ci: bump ${config.repo_name}/${config.service_name} image tag to ${env.TAG}'
git push origin main
"""
}
}
}
}
@@ -0,0 +1,18 @@
package com.homelab.stages
import com.homelab.utilities.buTeamMapping
def run(String bu, String team,String module){
stage("Validate BU and Team"){
def vObj = new buTeamMapping()
if (vObj.validate(bu,team)){
log.info("Correct team and BU values")
}
else{
error "Incorrect BU and team values provided"
}
if(module == ""){
error "Module can't be empty. if there is no module, Please provide the parameter value as module_less"
}
}
}
@@ -0,0 +1,28 @@
package com.homelab.utilities
def create() {
withCredentials([file(credentialsId: 'ssh-private-key', variable: 'FILE')]) {
sh """
cat ${FILE} > ./id_github_jenkins
chmod 600 ./id_github_jenkins
# Ensure .ssh directory has correct permissions
chmod 700 /root/.ssh
# Fix SSH config file permissions if it exists
if [ -f /root/.ssh/config ]; then
chmod 600 /root/.ssh/config
chown root:root /root/.ssh/config
fi
# Fix existing SSH private key permissions if it exists
if [ -f /root/.ssh/id_rsa ]; then
chmod 600 /root/.ssh/id_rsa
chown root:root /root/.ssh/id_rsa
fi
# Fix any other SSH key files that might exist
find /root/.ssh -type f -name "id_*" -exec chmod 600 {} \\; 2>/dev/null || true
"""
}
}
@@ -0,0 +1,125 @@
/*
Purpose: Utility function to return BU and their respective teams
Author: Avinash kumar Lodhi
*/
package com.homelab.utilities
def validate(String bu, String team) {
def bu_team_map = ['supply':['supplier-ads', 'supplier-ads-frontend', 'experience', 'fulfilment', 'fulfilment-frontend','financial-services', 'cataloging', 'cataloging-frontend', 'payout', 'payout-frontend', 'supplier-acquisition-activation', 'supplier-service', 'returns', 'supply-shared', 'display-ads', 'offers','transact', 'supplier-live-commerce'],
'demand':['comms-platform', 'live-commerce', 'shopping-platform', 'product-feed', 'search', 'product-meta', 'user-growth', 'web', 'transact', 'communications', 'discovery-platform', 'offers', 'android-platform', 'ios', 'demand-shared', 'discovery-ranking'],
'farmiso':['farmiso'],
'admin':['devops'],
'central':['shared', 'devops', 'psec', 'dbe'],
'dataengg':['data-platform', 'dataengg-shared', 'data-intelligence', 'data-platform-consumption', 'data-platform-ingestion', 'data-platform-nrt', 'data-platform-prism-frmw', 'data-platform-experimentation'],
'datascience':['data-science', 'ml-platform', 'for-you', 'recommendation', 'catalog-listing-page', 'search', 'advertisement', 'explore', 'pricing', 'product-match', 'catalog-taxonomy', 'brand-infringment', 'fds', 'return-reimbursements', 'fullfilment', 'ugc-moderation-analysis', 'home-page', 'core', 'usergrowth', 'demand-forecast', 'catalog-qc'],
'mcache':['mcache', 'mcache-shared', 'supplier-service'],
'infra':['devops', 'dbe']
]
if (bu == null || team == null) {
return null
}
return bu_team_map[bu].contains(team)
}
def get_initials(String targetMap, String targetString) {
def bu_initials = ['supply':'supl',
'demand':'dmnd',
'farmiso':'farm',
'admin':'admn',
'central':'cntr',
'dataengg':'deng',
'datascience':'dsci',
'mcache':'mche',
'infra':'infr'
]
def team_initials = ['supplier-ads':'ads',
'comms-platform': 'cplat',
'supplier-ads-frontend':'fads',
'experience':'xp',
'fulfilment':'fnf',
'fulfilment-frontend':'ffnf',
'financial-services':'fsvc',
'cataloging':'ctlng',
'cataloging-frontend':'fctlg',
'payout':'pay',
'payout-frontend':'fpay',
'supplier-acquisition-activation':'saa',
'supplier-service':'ssvc',
'seller-services': 'sis',
'live-commerce':'lcom',
'shopping-platform':'splat',
'product-feed':'pfeed',
'search':'srch',
'product-meta':'pmeta',
'user-growth':'grwth',
'web':'web',
'transact':'trnst',
'communications':'comms',
'discovery-platform':'dplat',
'farmiso':'farm',
'devops':'devop',
'offers':'offer',
'returns':'retrn',
'android-platform':'andrd',
'ios':'ios',
'shared':'xcntr',
'supply-shared':'xsupl',
'demand-shared':'xdmnd',
'data-platform':'dp',
'data-science':'ds',
'ml-platform':'ml',
'dataengg-shared':'xdeng',
'datascience-shared':'xdsci',
'data-intelligence':'di',
'recommendation':'rcmnd',
'catalog-listing-page':'ctllp',
'advertisement':'adv',
'explore':'explr',
'pricing':'price',
'product-match':'patch',
'catalog-taxonomy':'ctltx',
'brand-infringment':'brndi',
'fds':'fds',
'return-reimbursements':'retrr',
'fullfilment':'flfmt',
'ugc-moderation-analysis':'umdra',
'home-page':'hpage',
'usergrowth':'ugrwt',
'demand-forecast':'dmndf',
'catalog-qc':'ctlqc',
'data-platform-consumption':'dpcon',
'data-platform-ingestion':'dping',
'data-platform-nrt':'dpnrt',
'data-platform-prism-frmw':'dpprf',
'data-platform-experimentation':'dpexp',
'display-ads':'dplay',
'discovery-ranking':'drank',
'mcache':'mche',
'mcache-shared':'xmche',
'supplier-live-commerce':'slcom',
'trust-and-safety': 'tns',
'valmo': 'vlm',
'psec':'psec',
'dbe':'dbe',
'dev-productivity':'devprd']
if (targetString == null) {
return null
}
switch (targetMap) {
case 'bu_initials':
return bu_initials[targetString]
case 'team_initials':
return team_initials[targetString]
default:
return 'Undefined option'
}
}
def get_team_initials(String team) {
return get_initials('team_initials', team)
}
def get_bu_initials(String bu) {
return get_initials('bu_initials', bu)
}
@@ -0,0 +1,391 @@
package com.homelab.utilities
def getWhitelistedRepos(fileName){
dir('whitelist'){
git(
url: "https://github.com/Homelab/whitelists.git",
branch: "main",
credentialsId: 'cicd-github-app',
)}
def yaml = readYaml file: "whitelist/${fileName}.yaml"
return yaml.get("repos", []) as Set
}
def getWhitelistedDeployable(fileName, keyName){
dir('whitelist'){
git(
url: "https://github.com/Homelab/whitelists.git",
branch: "main",
credentialsId: 'cicd-github-app',
)}
def yaml = readYaml file: "whitelist/${fileName}.yaml"
return yaml.get(keyName, []) as Set
}
/*
* return `true` if we should not proceed
*/
def isMultizoneEnabled( String deployable){
def WHITELIST = getWhitelistedDeployable("multizone-enabled-repos" , "multizone_enabled_deployables")
if (WHITELIST.contains(deployable)) {
return true
}
return false
}
/*
* return `true` if we should not proceed
*/
def skipSonarCheckForbidden(Map config, Map environment_map) {
def branch = env.BRANCH_NAME
def environment = environment_map.getOrDefault(branch, "int")
def WHITELIST = getWhitelistedRepos("skip-sonar-whitelist")
// returns 'true' if we aren't skipping sonar
// or if we're allowed to skip sonar
def build_version = config['dockerBuildVersion']
def repo_name = config['repo_name']
// if not a maven build OR if it a hotfix we exit early and dont care what skip_sonar is
if (WHITELIST.contains(repo_name)|| !(build_version.contains("maven")) || environment != "prd" || branch.contains("hotfix")){
return false;
}
return config['skip_sonar'];
}
def skipSonarCheckForGo(Map config) {
def branch = env.BRANCH_NAME
def WHITELIST = getWhitelistedRepos("skip-sonar-whitelist")
def build_version = config['dockerBuildVersion']
def repo_name = config['repo_name']
echo "env.INFRA_ENV: ${env.INFRA_ENV}"
if (WHITELIST.contains(repo_name)|| branch.contains("hotfix")|| env.INFRA_ENV == "toolchain"){
return true;
}
return false;
}
/*
* return `true` if we should not proceed
*/
def appConfigDisabledForbidden(boolean appConfigEnabled, String repo_name, String environment, String build_version){
def branch = env.CHANGE_TARGET
def WHITELIST = getWhitelistedRepos("app-config-disabled")
if (WHITELIST.contains(repo_name) || environment!="stg"){
return false
}
if (!(build_version.contains("maven") || build_version.contains("gradle"))){
return false
}
return !appConfigEnabled
}
/*
* return `true` if we should not proceed
*/
def allowedNonDevelopPrDeploymentToIntRepos( String repo_name){
def WHITELIST = getWhitelistedRepos("allowedNonDevelopPrDeploymentToInt")
if (WHITELIST.contains(repo_name)) {
return true
}
return false
}
/*
* return `true` if we should not proceed
*/
def ValidateCacConfigForRepo(boolean ValidateConfig, String repo_name ){
def branch = env.CHANGE_TARGET
def WHITELIST = getWhitelistedRepos("ValidateCacConfig")
if (WHITELIST.contains(repo_name)) {
return true
}
return ValidateConfig
}
def getToolchainEnv() {
def paramsAction = currentBuild.rawBuild.getAction(hudson.model.ParametersAction.class)
if (paramsAction) {
echo "paramsAction: ${paramsAction}"
def p = paramsAction.getParameter("TOOLCHAIN_ENV")
echo "p: ${p}"
if (p) {
return p.getValue()?.toString()
}
}
return null
}
def run(Map config) {
def branch_name = env.BRANCH_NAME
if (env.INFRA_ENV == 'toolchain' && (config.build_tool?.startsWith('node-') || config.dockerBuildVersion?.startsWith('node-'))) {
branch_name = 'develop' // develop maps to stg in the environment_map
def tcEnv = getToolchainEnv()
env.TOOLCHAIN_ENV = tcEnv
log.info("Successfully extracted TOOLCHAIN_ENV from trigger cause: ${env.TOOLCHAIN_ENV}")
}
def environment_map = ['master':'prd', 'main':'prd', 'develop':'stg', 'gcp-main':'prd', 'farmiso-main':'prd', 'gcp-master':'prd', 'gcp-dev':'stg']
env.skip_user_input = config.skip_user_input ?: false
// don't allow skip_sonar
if (skipSonarCheckForbidden(config, environment_map)){
throw new Exception("Not allowed to skip sonar (skip_sonar in config.yaml)")
}
if (env.CHANGE_ID) {
branch_name = env.CHANGE_TARGET
environment_map = ['master':'int', 'main':'int', 'gcp-main':'int', 'farmiso-main':'int', 'gcp-master':'int', 'develop':'ftr', 'gcp-dev':'ftr']
}
environment_map[branch_name] = environment_map[branch_name] ?: 'ftr'
if (config.containsKey('branch_params')) {
Map branch_config = config['branch_params'].collectEntries { key, value -> branch_name.matches(key) ? value : [ : ] }
config.remove('branch_params')
config.putAll(branch_config)
}
if (config.containsKey('environment')) {
Map envrionment_config = config['environment'].collectEntries { key, value -> environment_map[branch_name].matches(key) ? value : [ : ] }
config.remove('environment')
config.putAll(envrionment_config)
}
env.GITHUB_CRED = 'svc-devops-homelab'
env.cicd_environment = environment_map[branch_name]
env.helm_repo_name = 'devops-helm-charts'
env.argo_repo_name = 'devops-argo-config'
echo "Branch Name - ${branch_name} and Environment - ${env.cicd_environment}"
if (env.CLOUD_PROVIDER == 'AWS') {
def prodAccountID = '847438129436'
def prodRegion = 'ap-southeast-1'
def prodObjBucket = 'homelab-prod-artifacts'
def devAccountID = '766380763301'
def devObjBucket = 'homelab-stg-artifacts'
def devRegion = 'ap-south-1'
def accountDetails = [
'prd': [
'accountID': prodAccountID,
'region': prodRegion,
'objBucket': prodObjBucket
],
'int': [
'accountID': prodAccountID,
'region': prodRegion,
'objBucket': prodObjBucket
],
'stg': [
'accountID': devAccountID,
'region': devRegion,
'objBucket': devObjBucket
],
'ftr': [
'accountID': devAccountID,
'region': devRegion,
'objBucket': devObjBucket
]
]
env.accountID = accountDetails[env.cicd_environment]['accountID']
env.region = accountDetails[env.cicd_environment]['region']
env.registry = "${env.accountID}.dkr.ecr.${env.region}.amazonaws.com"
env.buildRegistry = env.registry
env.helmChartsPath = 'charts'
env.defaultHelmChartVersion = '1.0.10'
env.objBucket = accountDetails[env.cicd_environment]['objBucket']
env.skip_notify = false
echo "${env.accountID}.dkr.ecr.${env.region}.amazonaws.com"
}
else if (env.CLOUD_PROVIDER == 'GCP') {
def prodVaultURL = 'https://vault-prd.homelabgcp.in'
def prodVaultToken = 'vault-prd-token'
def prodSonarURL = 'https://sonarqube-prd.homelabgcp.in'
def prodSonarToken = 'sonar-token-prod'
def prodSonarEnv = 'sonarqube-test'
def prodGoProxyUrl = 'https://athens-prd.homelabgcp.in'
def prdDockerHost = 'dind-prd-svc'
def preProdDockerHost = 'dind-int-svc'
def prodGCPProject = "homelab-${config.bu}-prd-0622"
def preprodGCPProject = "homelab-shared-int-0525"
def devVaultURL = 'https://vault-dev.homelabgcp.in'
def devVaultToken = 'vault-dev-token'
def devSonarURL = "https://sonarqube-${config.bu}-dev.homelabgcp.in"
def devSonarToken = "sonar-token-${config.bu}-dev"
def devSonarEnv = "sonar-${config.bu}-dev"
def devGoProxyUrl = 'https://athens-dev.homelabgcp.in'
def devGCPProject = "homelab-${config.bu}-dev-0622"
def devDockerHost = 'dind-dev-new-svc.jenkins-new.svc.cluster.local'
def toolchainDockerHost = 'toolchain-dind-dev-svc.jenkins-toolchain.svc.cluster.local'
def accountDetails = [
'prd': [
'vaultURL': prodVaultURL,
'vaultToken': prodVaultToken,
'sonarURL': prodSonarURL,
'sonarToken': prodSonarToken,
'GCPProject': prodGCPProject,
'sonarEnv': prodSonarEnv,
'GCPLBProject': prodGCPProject,
'goProxyUrl': prodGoProxyUrl,
'dockerHost': prdDockerHost
],
'int': [
'vaultURL': prodVaultURL,
'vaultToken': prodVaultToken,
'sonarURL': prodSonarURL,
'sonarToken': prodSonarToken,
'GCPProject': preprodGCPProject,
'sonarEnv': prodSonarEnv,
'GCPLBProject': prodGCPProject,
'goProxyUrl': prodGoProxyUrl,
'dockerHost': preProdDockerHost
],
'stg': [
'vaultURL': devVaultURL,
'vaultToken': devVaultToken,
'sonarURL': devSonarURL,
'sonarToken': devSonarToken,
'GCPProject': devGCPProject,
'sonarEnv': devSonarEnv,
'GCPLBProject': devGCPProject,
'goProxyUrl': devGoProxyUrl,
'dockerHost': devDockerHost
],
'ftr': [
'vaultURL': devVaultURL,
'vaultToken': devVaultToken,
'sonarURL': devSonarURL,
'sonarToken': devSonarToken,
'GCPProject': devGCPProject,
'sonarEnv': devSonarEnv,
'GCPLBProject': devGCPProject,
'goProxyUrl': devGoProxyUrl,
'dockerHost': devDockerHost ]
]
env.GCPProject = accountDetails[env.cicd_environment]['GCPProject']
env.GCPLBProject = accountDetails[env.cicd_environment]['GCPLBProject']
env.registry = 'asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622'
if (env.INFRA_ENV == 'toolchain') {
env.registry = 'asia-southeast1-docker.pkg.dev/homelab-central-dev-0622/toolchain'
}
env.buildRegistry = 'asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622/admin'
env.helmChartsPath = env.cicd_environment == 'prd' ? 'values_v3' : 'values_v2'
env.defaultHelmChartVersion = '2.0.0'
env.objBucket = "gcs-infr-dvps-homelab-artifacts-${env.cicd_environment}"
env.vaultURL = accountDetails[env.cicd_environment]['vaultURL']
env.vaultToken = accountDetails[env.cicd_environment]['vaultToken']
env.sonarURL = accountDetails[env.cicd_environment]['sonarURL']
env.sonarToken = accountDetails[env.cicd_environment]['sonarToken']
env.sonarEnv = accountDetails[env.cicd_environment]['sonarEnv']
env.goProxyUrl = accountDetails[env.cicd_environment]['goProxyUrl']
env.skip_notify = true
env.DOCKER_HOST = accountDetails[env.cicd_environment]['dockerHost']
if (env.INFRA_ENV == 'toolchain') {
env.DOCKER_HOST = toolchainDockerHost
}
}
echo "Bucket and Image Repo Details - ${env.registry} ${env.buildRegistry} ${env.objBucket}"
echo "Docker Host - ${env.DOCKER_HOST}"
}
def perDeploymentVars(Map value_binding) {
env.BU = value_binding.bu
echo "${env.BU}"
if (env.CLOUD_PROVIDER == 'AWS') {
def inClusterName = 'https://kubernetes.default.svc'
def prodArgoURL = 'prod-ops-argocd.homelab.com'
def devArgoURL = 'stg-dev-argocd.homelabtest.in'
def prodK8sCluster = [
'supply': 'https://211689C65F4496AAA76FE19B29E24B6E.yl4.ap-southeast-1.eks.amazonaws.com',
'demand': 'https://9059D138B6277A0EA592BA7F4B680CEC.gr7.ap-southeast-1.eks.amazonaws.com',
'dataengg': 'https://806ADE97231CA65D2A0FFB780352630D.yl4.ap-southeast-1.eks.amazonaws.com',
'datascience': 'https://E34D119516F751AFD1A61043B0514726.yl4.ap-southeast-1.eks.amazonaws.com',
'central': 'https://C95FCEDF7CEE890F531E1D0488BEC6C3.gr7.ap-southeast-1.eks.amazonaws.com',
'mcache': 'https://2FFADF214AD8BE58769C5F2797987E50.gr7.ap-southeast-1.eks.amazonaws.com'
]
def devK8sCluster = [
'supply': inClusterName,
'demand': inClusterName,
'dataengg': inClusterName,
'datascience': inClusterName,
'central': inClusterName,
'mcache': inClusterName
]
def accountDetails = [
'prd': [
'argoURL': prodArgoURL,
'argoIncubator': 'prod-app-of-apps',
'serverMap': prodK8sCluster
],
'int': [
'argoURL': prodArgoURL,
'argoIncubator': 'int-app-of-app',
'serverMap': prodK8sCluster
],
'stg': [
'argoURL': devArgoURL,
'argoIncubator': 'app-of-apps',
'serverMap': devK8sCluster
],
'ftr': [
'argoURL': devArgoURL,
'argoIncubator': 'ftr-app-of-apps',
'serverMap': devK8sCluster
]
]
env.clusterName = accountDetails[env.cicd_environment]['serverMap'][env.BU]
env.argoAppsPath = 'applications'
env.argoURL = accountDetails[env.cicd_environment]['argoURL']
env.argoCreds = 'argocd-jenkins'
env.argoIncubator = accountDetails[env.cicd_environment]['argoIncubator']
env.argoAppNS = 'argocd'
}
else if (env.CLOUD_PROVIDER == 'GCP') {
def prodArgoURL = "argocd-${env.BU}-prd.homelabgcp.in"
def prodArgoCreds = "argocd-${env.BU}-prd-creds"
def preprodArgoURL = "argocd-shared-int.homelabgcp.in"
def preprodArgoCreds = "argocd-shared-int-creds"
def devArgoURL = 'argocd-dev.homelabgcp.in'
def devArgoCreds = 'argocd-dev-creds'
def accountDetails = [
'prd': [
'argoURL': prodArgoURL,
'argoCreds': prodArgoCreds,
'argoAppNS': "argocd-${env.BU}-prd",
'clusterName': "k8s-${env.BU}-prd-ase1"
],
'int': [
'argoURL': preprodArgoURL,
'argoCreds': preprodArgoCreds,
'argoAppNS': "argocd-shared-int",
'clusterName': "k8s-shared-int-ase1"
],
'stg': [
'argoURL': devArgoURL,
'argoCreds': devArgoCreds,
'argoAppNS': "argocd-dev",
'clusterName': "k8s-${env.BU}-stg-ase1"
],
'ftr': [
'argoURL': devArgoURL,
'argoCreds': devArgoCreds,
'argoAppNS': "argocd-dev",
'clusterName': "k8s-${env.BU}-stg-ase1"
]
]
env.clusterName = accountDetails[env.cicd_environment]['clusterName']
if (env.cicd_environment == 'int') {
env.argoAppsPath = "applications_v2/k8s-${env.BU}-int-ase1"
} else {
env.argoAppsPath = "applications_v2/${env.clusterName}"
}
env.argoURL = accountDetails[env.cicd_environment]['argoURL']
env.argoCreds = accountDetails[env.cicd_environment]['argoCreds']
env.argoAppNS = accountDetails[env.cicd_environment]['argoAppNS']
env.argoIncubator = "incubator-apps-k8s-${env.BU}-${env.cicd_environment}-ase1"
}
echo "${env.clusterName} ${env.argoURL} ${env.argoIncubator}"
}
@@ -0,0 +1,26 @@
package com.homelab.utilities
// Ported from the real devops-lib's constructTemplate.groovy — same
// SimpleTemplateEngine mechanism (${...} interpolation, <% %> control
// flow), same @NonCPS requirement (the engine is a non-serialisable Java
// object; calling this across a `parallel` boundary would break). Dropped
// the Homelab-specific `add_file`/PBAC resolution — this homelab has no
// equivalent convention yet; add it back here if that's ever needed.
def run(Map binding, String text) {
return _construct(binding, text)
}
def renderTemplate(Map binding, String templateFile, String fileName) {
def template = libraryResource 'com/homelab/' + templateFile
def renderedTemplate = run(binding, template.toString())
writeFile file: fileName, text: renderedTemplate
}
@NonCPS
def _construct(Map binding, String text) {
binding = new HashMap(binding)
def engine = new groovy.text.SimpleTemplateEngine()
def template = engine.createTemplate(text).make(binding)
engine = null
return template.toString()
}
@@ -0,0 +1,52 @@
package com.homelab.utilities
def retryDockerPush(String cmd) {
int maxAttempts = 5
int attempt = 1
while (attempt <= maxAttempts) {
try {
sh cmd
break
} catch (err) {
if (attempt == maxAttempts) {
error("Command failed after ${maxAttempts} attempts: ${err}")
}
echo "Command failed, retrying... (${attempt}/${maxAttempts})"
sleep 3
attempt++
}
}
}
def imageExists(String registry, String repoName, String tag) {
int maxAttempts = 5
int attempt = 1
while (attempt <= maxAttempts) {
try {
if (env.CLOUD_PROVIDER == 'GCP') {
def result = sh(
script: "gcloud container images list-tags ${registry}/${repoName} --filter='tags:${tag}' --format='get(tags)'",
returnStdout: true
).trim()
return result != ""
}
else if (env.CLOUD_PROVIDER == 'AWS') {
echo "AWS not supported."
return false
}
} catch (Exception e) {
echo "Attempt ${attempt}/${maxAttempts} failed: Error checking image existence: ${e.toString()}"
if (attempt == maxAttempts) {
echo "Max attempts reached. Assuming image does not exist or service is down."
return false
}
echo "Retrying in 5 seconds..."
sleep 5
attempt++
}
}
return false
}
@@ -0,0 +1,61 @@
package com.homelab.utilities
def getCommitid(String repo_name) {
dir(repo_name) {
def gitCmd = env.INFRA_ENV == 'toolchain' ? 'git -c safe.directory="$(pwd)"' : 'git'
def commitID = sh(returnStdout: true, script: "${gitCmd} log -1 --format=%h").trim()
env.commit_id = sh(returnStdout: true, script: "${gitCmd} log -1 --format=%H").trim()
return commitID
}
}
def getVersion(String repo_name) {
dir(repo_name) {
if (fileExists('pom.xml')) {
return sh(returnStdout: true, script: 'xq -r .project.version pom.xml').trim()
}
else if (fileExists('package.json')) {
return sh(returnStdout: true, script: 'jq -r .version package.json').trim()
}
else {
return '1.0'
}
}
}
def getModules(String repo_name) {
dir(repo_name) {
if (fileExists('pom.xml')) {
modules = sh(returnStdout: true, script: 'xq -r .project.modules.module[] pom.xml 2>/dev/null || xq -r .project.modules.module pom.xml 2>/dev/null || echo empty').trim()
if (modules == 'empty' || modules == 'null') {
return null
}
modules = modules.split('\n') as List
return modules
}
else {
return null
}
}
}
def getTag(String repo_name) {
def version = getVersion(repo_name)
def commitID = getCommitid(repo_name)
def date = new Date()
def timesha = date.getTime()
def tag = "v${version}-${commitID}-${timesha}"
if (env.INFRA_ENV == 'toolchain') {
tag = "v${version}-${commitID}"
}
return tag
}
def getTagShort(String repo_name) {
def version = getVersion(repo_name)
def commitID = getCommitid(repo_name)
def tagShort = "v${version}-${commitID}"
return tagShort
}
@@ -0,0 +1,16 @@
package com.homelab.utilities
def getParam(String wd, String fileName = 'config.yaml') {
dir(wd) {
def config = readYaml file: fileName
return config
}
}
//read as string
def getParamAsString(String wd, String fileName = 'config.yaml') {
dir(wd) {
// Read the entire file content as a string
def yamlContent = readFile(file: fileName)
return yamlContent
}
}
+342
View File
@@ -0,0 +1,342 @@
package com.homelab.utilities
def clone(String path, String repo_name, String branch_name) {
log.info("Cloning repo - ${repo_name}, branch - ${branch_name} in path - ${path}")
dir(path) {
try {
sh "rm -rf ${repo_name}"
}
catch (Exception e) {
log.info('Repo not present, proceeding to clone the repo.')
}
try {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
if (branch_name) {
sh "git clone -b ${branch_name} https://github.com/Homelab/${repo_name}.git"
}
else {
sh "git clone https://github.com/Homelab/${repo_name}.git"
}
}
}
catch (Exception e) {
env.msg += "\n\nFAILED -\n ```Unable to clone repo - ${repo_name}, branch - ${branch_name} in path - ${path}.\n Full Erroror Details - ${e}```"
env.error_msg_to_db += "Unable to Clone Repo ${repo_name};"
log.error("${env.msg}")
throw e
}
}
}
def branchCheckOut(String path, String branch_name) {
log.info("Checking out to branch - ${branch_name} in path - ${path}")
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh 'git fetch'
try {
sh "git checkout ${branch_name}"
sh 'git pull --ff-only'
}
catch (Exception e) {
log.info('Creating new branch')
sh "git checkout -b ${branch_name}"
}
sh 'git branch'
}
}
}
def add(String path, String git_add_file) {
log.info("Adding files - ${git_add_file} to git, in path - ${path}")
try {
dir(path) {
sh "git add ${git_add_file}"
}
}
catch (Exception e) {
env.msg += "\n\nFAILED -\n ```Unable to add files - ${git_add_file} to git, in path - ${path}.\n Full Erroror Details - ${e}```"
env.error_msg_to_db += 'Unable to Add Files;'
log.error("${env.msg}")
throw e
}
}
def createTag(path, git_tag, tag_message) {
log.info("Creating tag - ${git_tag} in git, in path - ${path}")
try {
dir(path) {
sh "git tag ${git_tag} -m '${tag_message}'"
}
}
catch (Exception e) {
env.msg += "\n\nFAILED -\n ```Unable to create tag - ${git_tag} in git, in path - ${path}.\n Full Erroror Details - ${e}```"
env.error_msg_to_db += "Unable to Create Tag ${git_tag};"
log.error("${env.msg}")
throw e
}
}
def codeCommit(String path, String branch_name, String commit_message) {
log.info("Commiting code with message - ${commit_message} in git, in path - ${path}")
def global_arg = '--global'
def gitName = 'svc-devops-homelab'
def gitEmail = 'devops@homelab.com'
def gitEnv = [
"GIT_AUTHOR_NAME=${gitName}",
"GIT_AUTHOR_EMAIL=${gitEmail}",
"GIT_COMMITTER_NAME=${gitName}",
"GIT_COMMITTER_EMAIL=${gitEmail}",
]
dir(path) {
withEnv(gitEnv) {
if (sh(returnStatus: true, script: "git diff-index --quiet ${branch_name} 2>/dev/null")) {
try {
sh "git commit -m '${commit_message}'"
return 0
}
catch (Exception e) {
env.msg += "\n\nFAILED -\n ```Unable to commit code with message - ${commit_message} to git in path - ${path}.\n Full Erroror Details - ${e}```"
env.error_msg_to_db += 'Unable to Commit;'
log.error("${env.msg}")
return 1
}
}
else {
log.info("No changes were made in branch - ${branch_name}")
return 1
// log.error("${env.msg}")
}
}
}
}
def codePush(String path, String branch_name) {
log.info("Pushing code in branch - ${branch_name} in path - ${path}")
try {
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
try {
sh "git push -f --set-upstream origin ${branch_name}"
}
catch (Exception e) {
sh 'git pull --ff-only'
sh 'git push -f'
}
}
}
}
catch (Exception e) {
env.msg += "\n\nFAILED -\n ```Unable to push code in branch - ${branch_name} in path - ${path}.\n Full Erroror Details - ${e}```"
env.error_msg_to_db += "Unable to push code in branch - ${branch_name};"
log.error("${env.msg}")
throw e
}
}
def tagPush(String path) {
log.info("Pushing tag in path - ${path}")
try {
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh 'git push --tags'
}
}
}
catch (Exception e) {
env.msg += "\n\nFAILED -\n ```Unable to push tag from path - ${path}.\n Full Erroror Details - ${e}```"
env.error_msg_to_db += 'Unable to Push tag;'
log.error("${env.msg}")
throw e
}
}
def createPR(String app_name, String repo_name, String base_branch, String target_branch, String pr_message) {
log.info("Creating PR with message - ${pr_message} in Github for repo ${repo_name}")
def body = "{\"title\":\"${app_name} ${base_branch} Onboarding\",\"body\":\"${pr_message}\",\"head\":\"${target_branch}\",\"base\":\"${base_branch}\"}"
def pr_num = 'empty'
try {
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_CRED}", usernameVariable:'user', passwordVariable: 'token')]) {
create_pr = httpRequest httpMode: 'POST',
customHeaders: [
[name: 'Accept', value: 'application/vnd.github+json'],
[maskValue: true, name: 'Authorization', value: 'Bearer ' + token]
],
requestBody: body,
url: "https://api.github.com/repos/Homelab/${repo_name}/pulls",
validResponseCodes: '201',
timeout: 10
def create_pr_json = readJSON(text: create_pr.content)
try {
error_filter = create_pr_json.errors.message[0]
}
catch (Exception e) {
error_filter = 'empty'
}
if ( error_filter.contains('No commits between') ) {
log.info("No changes were made in branch - ${target_branch}. Skipping - PR Creation...\n${create_pr_json}")
}
else if ( error_filter.contains('A pull request already exists') ) {
log.info(error_filter)
}
else {
pr_num = create_pr_json.number
pr_url = create_pr_json.url
log.info("PR Number - ${pr_num} and URL - ${pr_url}")
}
return pr_num.toString()
}
}
catch (Exception e) {
env.msg = "\n\nFAILED -\n Unable to create PR with message - ${pr_message}.\n Full Error Details - ${e}"
def error_code = "${e}".split('Status code')[1]
error_code = error_code.toString()
error_code = error_code.split(' ')
error_code = error_code[1]
env.error_part_msg_to_db = "Unable to Create PR for base branch - ${base_branch}, target branch - ${target_branch}, Repo Name - ${repo_name}. Failed with status code ${error_code};" //Akshay has asked to remove it
log.error("${env.msg}")
throw e
}
}
def mergePR(String repo_name, String pr_num, String target_branch) {
log.info("Merging PR ${pr_num} in repo - ${repo_name}")
if ( "${pr_num}" == 'empty' || pr_num == null) {
log.info("No changes were made in branch - ${target_branch}. Skipping - PR Merge...")
}
else {
try {
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_CRED}", usernameVariable:'user', passwordVariable: 'token')]) {
merge_pr = httpRequest httpMode: 'PUT',
customHeaders: [
[name: 'Accept', value: 'application/vnd.github+json'],
[maskValue: true, name: 'Authorization', value: 'Bearer ' + token]
],
url: "https://api.github.com/repos/Homelab/${repo_name}/pulls/${pr_num}/merge",
validResponseCodes: '200',
timeout: 10
def merge_pr_json = readJSON(text: merge_pr.content)
if ( merge_pr_json.message.contains('not mergeable') ) {
env.msg += "\n\nFAILED -\n Unable to merge PR - ${pr_num}. PR URL - https://github.com/Homelab/${repo_name}/pulls/${pr_num}. ${merge_pr_json.message}"
log.error("${env.msg}")
error("${env.msg}")
}
else {
log.info("PR Merge was SUCCESSFUL. Message - ${merge_pr_json.message}")
}
}
}
catch (Exception e) {
env.msg = "\n\nFAILED -\n Unable to merge PR - ${pr_num} \n Full Error Details - ${e}"
def error_code = "${e}".split('Status code')[1]
error_code = error_code.toString()
error_code = error_code.split(' ')
error_code = error_code[1]
env.error_part_msg_to_db = "Unable to Merge PR - ${pr_num}, Repo Name - ${repo_name} status code ${error_code};" //Akshay has asked to remove it
log.error("${env.msg}")
throw e
}
}
}
def deleteBranch(String path, String base_branch, String target_branch) {
log.info("Deleting branch - ${target_branch}")
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
sh 'git fetch'
try {
sh "git checkout ${base_branch}"
sh "git branch -d ${target_branch}"
sh "git push -d origin ${target_branch}"
}
catch (Exception e) {
env.msg = "\n\nFAILED -\n Unable to delete branch - ${target_branch}.\n Full Error Details - ${e}"
env.error_msg_to_db = "Unable to Delete Branch ${target_branch};"
log.error("${env.msg}")
throw e
}
sh 'git branch'
}
}
}
def preDeleteBranch(String path, String base_branch, String target_branch) {
log.info("Pre Deleting branch - ${target_branch}")
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
try {
sh 'git fetch'
sh "git checkout ${base_branch}"
sh "git push -d origin ${target_branch}"
}
catch (Exception e) {
echo "Pre Delete Branch - Unable to delete branch ${target_branch}"
}
sh 'git branch'
}
}
}
def fetchDiffFilesForPullRequest(String path , String target_branch){
log.info("check diff from target branch - ${target_branch}")
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
try {
sh "git fetch origin ${target_branch}:${target_branch}"
sh "git branch"
def changedFiles = sh(script: "git diff --name-only HEAD ${target_branch}", returnStdout: true).trim()
echo "${changedFiles}"
return changedFiles
}
catch (Exception e) {
echo "failed to get the diff files from ${target_branch}"
}
}
}
}
def fetchDiffFilesForPushRequest(String path ){
log.info("check diff of current and previous commit for ${path}")
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
try {
sh "git fetch origin"
// Get the latest commit hash and the previous commit hash
def currentCommit = sh(script: "git rev-parse HEAD", returnStdout: true).trim()
def previousCommit = sh(script: "git rev-parse HEAD~1", returnStdout: true).trim()
// Capture the list of changed files in a Groovy variable
def changedFiles = sh(script: "git diff --name-only ${previousCommit} ${currentCommit}", returnStdout: true).trim()
return changedFiles
}
catch (Exception e) {
echo "failed to get the diff files for ${path}"
}
}
}
}
def fetchLatestCommitId(String path, String branch) {
log.info("Checking the latest commit for branch '${branch}' in path '${path}'")
dir(path) {
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
try {
// Fetch the latest changes for the specific branch
sh "git fetch origin ${branch}:${branch}"
// Check out the specified branch
sh "git checkout ${branch}"
// Get the latest commit hash for the branch
def currentCommit = sh(script: "git rev-parse ${branch}", returnStdout: true).trim()
return currentCommit
} catch (Exception e) {
echo "Failed to get the latest commit for branch '${branch}' in path '${path}': ${e.message}"
return null
}
}
}
}
@@ -0,0 +1,141 @@
package com.homelab.utilities
def run(String memory_request, String cpu_request, String priority_v2) {
echo 'Code to select node pool based on environment'
switch (env.cicd_environment) {
case 'prd':
echo 'Code to select node pool based on memory and cpu request in prd'
def mem_req_part = memory_request
def cpu_req_part = cpu_request
def priority = priority_v2
echo "mem_req_part is ${mem_req_part}"
echo "cpu_req_part is ${cpu_req_part}"
if ( mem_req_part.contains('M') ) {
mem_req = mem_req_part.replaceAll('Mi', '')
mem_req = mem_req.replaceAll('M', '')
try {
mem_req = mem_req.toDouble()
}
catch (NumberFormatException e) {
mem_req = mem_req.toDouble()
//mem_req = mem_req.toInteger()
}
}
else if ( mem_req_part.contains('G') ) {
mem_req = mem_req_part.replaceAll('Gi', '')
mem_req = mem_req.replaceAll('G', '')
try {
mem_req = mem_req.toDouble() * 1024
}
catch (NumberFormatException e) {
mem_req = mem_req.toDouble() * 1024
//mem_req = mem_req.toInteger()
}
}
echo "memory_request is ${memory_request} ${mem_req}"
if ( cpu_req_part.contains('m') ) {
cpu_req = cpu_req_part.replaceAll('m', '')
try {
cpu_req = cpu_req.toDouble()
}
catch (NumberFormatException e) {
cpu_req = cpu_req.toDouble()
//cpu_req = cpu_req.toInteger()
}
}
else {
try {
cpu_req = cpu_req_part.toDouble() * 1000
}
catch (NumberFormatException e) {
cpu_req = cpu_req_part.toDouble() * 1000
//cpu_req = cpu_req.toInteger()
}
}
echo "cpu_request is ${cpu_request} ${cpu_req}"
def ratio = (mem_req / cpu_req).toDouble()
if ( cpu_req > mem_req ) {
ratio = 2
}
//ratio = ratio.toInteger()
echo "Ratio - ${ratio}"
if(priority.equalsIgnoreCase('cp1')||priority.equalsIgnoreCase('cp2')||priority.equalsIgnoreCase('cp3')||priority.equalsIgnoreCase('up1')||priority.equalsIgnoreCase('up2')||priority.equalsIgnoreCase('up3')||priority.equalsIgnoreCase('sp1')||priority.equalsIgnoreCase('sp2')||priority.equalsIgnoreCase('sp3')){
low_priority="lite"
if ( ratio >= 2.5 ) {
ratiovalue = "tetra"
}
else {
ratiovalue = "duo"
}
if ( cpu_req >=2200 ) {
nodename = "sumo"
}
else{
nodename= "mega"
}
nodeSelectorvalue = "${nodename}${ratiovalue}${low_priority}"
println nodeSelectorvalue
break
}
else{
if ( ratio > 5.5 ) {
ratiovalue = 'octa'
}
else if ( ratio >= 2.5 ) {
ratiovalue = 'tetra'
}
else {
ratiovalue = 'duo'
}
if ( cpu_req >= 2200) {
nodevalue = 'sumo'
}
else if ( cpu_req < 2200 && cpu_req >= 1000) {
nodevalue = 'mega'
}
else {
nodevalue = 'compact'
}
switch ( env.BU?.toLowerCase() ) {
case 'supply':
if (nodevalue == 'sumo' && (ratiovalue == 'hexa' || ratiovalue == 'octa')){
ratiovalue = 'tetra'
}
else if (nodevalue == 'mega' && (ratiovalue == 'quad' || ratiovalue == 'octa')){
ratiovalue = 'tetra'
}
else if (nodevalue == 'compact' && ratiovalue == 'trio'){
ratiovalue = 'tetra'
}
break
case 'demand':
if (nodevalue == 'mega' && ratiovalue == 'quad'){
ratiovalue = 'tetra'
}
else if (nodevalue == 'compact' && (ratiovalue == 'octa' || ratiovalue == 'trio')){
ratiovalue = 'tetra'
}
break
}
nodeSelectorvalue = "${nodevalue}${ratiovalue}"
break
}
case 'int':
echo 'Shared node pool for int/pre-prod'
nodeSelectorvalue = "preprod-cost-optimized"
break
case ['dev', 'ftr', 'stg']:
echo 'Shared node pool for dev and ftr'
nodeSelectorvalue = "${env.BU}-shared"
break
default:
log.error('Unable to fetch environment')
}
return nodeSelectorvalue
}