From 52d7f78fa602eb1336642a425e47f48ca757d34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E6=8C=AF=E5=AE=87?= <> Date: Tue, 21 Jan 2025 17:01:31 +0800 Subject: [PATCH] feat(pipeline): implement environment variable injection and dependencies resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙振宇 <> --- .../devops/DependenciesResolver.groovy | 88 +++++++++++++++++++ .../freeleaps/devops/EnvironmentVars.groovy | 8 +- .../com/freeleaps/devops/SourceFetcher.groovy | 12 ++- .../devops/enums/DependenciesManager.groovy | 27 ++++++ first-class-pipeline/tests/Jenkinsfile | 8 ++ first-class-pipeline/vars/pipelineCall.groovy | 58 +++++++++++- 6 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 first-class-pipeline/src/com/freeleaps/devops/DependenciesResolver.groovy create mode 100644 first-class-pipeline/src/com/freeleaps/devops/enums/DependenciesManager.groovy diff --git a/first-class-pipeline/src/com/freeleaps/devops/DependenciesResolver.groovy b/first-class-pipeline/src/com/freeleaps/devops/DependenciesResolver.groovy new file mode 100644 index 00000000..5153cda3 --- /dev/null +++ b/first-class-pipeline/src/com/freeleaps/devops/DependenciesResolver.groovy @@ -0,0 +1,88 @@ +package com.freeleaps.devops + +class DependenciesResolver { + def steps + def language + def cachingEnabled + def mgr + + DependenciesResolver(steps, language) { + this.steps = steps + this.language = language + } + + enableCachingSupport() { + this.cachingEnabled = true + } + + disableCachingSupport() { + this.cachingEnabled = false + } + + useManager(DependenciesManager mgr) { + if (mgr == DependenciesManager.UNKNOWN) { + steps.error("Unknown dependencies manager") + } + this.mgr = mgr + } + + def resolve(Map configurations) { + if (mgr == null) { + steps.error("Dependencies manager is not set") + } + + echo "Ready to resolve dependencies for ${language}..." + + echo "Using ${mgr.manager} to resolve dependencies..." + + if (cachingEnabled) { + echo "Dependencies caching is enabled" + } + + switch (mgr) { + case DependenciesManager.PIP: + if (configurations.PIP_REQUIREMENTS_FILE == null || configurations.PIP_REQUIREMENTS_FILE.isEmpty()) { + steps.error("PIP_REQUIREMENTS_FILE is required when using PIP as dependencies manager") + } + + def requirementsFile = configurations.PIP_REQUIREMENTS_FILE + + if (cachingEnabled) { + steps.cache(maxCacheSize: 512, caches: [[$class: 'ArbitraryFileCache', excludes: '', includes: '**/*', path: '.pip-cache']]) { + steps.sh "pip install -r ${requirementsFile} --cache-dir .pip-cache" + } + } else { + steps.sh "pip install -r ${requirementsFile}" + } + case DependenciesManager.NPM: + if (configurations.NPM_PACKAGE_JSON_FILE == null || configurations.NPM_PACKAGE_JSON_FILE.isEmpty()) { + steps.error("NPM_PACKAGE_JSON_FILE is required when using NPM as dependencies manager") + } + + def packageJsonFile = configurations.NPM_PACKAGE_JSON_FILE + + if (cachingEnabled) { + steps.cache(maxCacheSize: 512, caches: [[$class: 'ArbitraryFileCache', excludes: '', includes: '**/*', path: '.npm-cache']]) { + steps.sh "npm install --cache .npm-cache" + } + } else { + steps.sh "npm install" + } + case DependenciesManager.YARN: + if (configurations.YARN_PACKAGE_JSON_FILE == null || configurations.YARN_PACKAGE_JSON_FILE.isEmpty()) { + steps.error("YARN_PACKAGE_JSON_FILE is required when using YARN as dependencies manager") + } + + def packageJsonFile = configurations.YARN_PACKAGE_JSON_FILE + + if (cachingEnabled) { + steps.cache(maxCacheSize: 512, caches: [[$class: 'ArbitraryFileCache', excludes: '', includes: '**/*', path: '.yarn-cache']]) { + steps.sh "yarn install --cache-folder .yarn-cache" + } + } else { + steps.sh "yarn install" + } + default: + steps.error("Unsupported dependencies manager") + } + } \ No newline at end of file diff --git a/first-class-pipeline/src/com/freeleaps/devops/EnvironmentVars.groovy b/first-class-pipeline/src/com/freeleaps/devops/EnvironmentVars.groovy index e21002aa..29536033 100644 --- a/first-class-pipeline/src/com/freeleaps/devops/EnvironmentVars.groovy +++ b/first-class-pipeline/src/com/freeleaps/devops/EnvironmentVars.groovy @@ -10,7 +10,7 @@ class EnvironmentVars { } def injectVars(Map configurations) { - if (steps.env.SERVICE_NAME == null || steps.env.SERVICE_NAME.isEmpty()) { + if (configurations.SERVICE_NAME == null || configurations.SERVICE_NAME.isEmpty()) { steps.error("SERVICE_NAME is required") } steps.env.SERVICE_NAME = configurations.SERVICE_NAME @@ -20,17 +20,17 @@ class EnvironmentVars { steps.error("Unknown service language: ${configurations.SERVICE_LANG}") } - if (steps.env.SERVICE_GIT_REPO == null || steps.env.SERVICE_GIT_REPO.isEmpty()) { + if (configurations.SERVICE_GIT_REPO == null || configurations.SERVICE_GIT_REPO.isEmpty()) { steps.error("SERVICE_GIT_REPO is required") } steps.env.SERVICE_GIT_REPO = configurations.SERVICE_GIT_REPO - if (steps.env.SERVICE_GIT_BRANCH == null || steps.env.SERVICE_GIT_BRANCH.isEmpty()) { + if (configurations.SERVICE_GIT_BRANCH == null || configurations.SERVICE_GIT_BRANCH.isEmpty()) { steps.error("SERVICE_GIT_BRANCH is required") } steps.env.SERVICE_GIT_BRANCH = configurations.SERVICE_GIT_BRANCH - if (steps.env.ENVIRONMENT_SLUG == null || steps.env.ENVIRONMENT_SLUG.isEmpty()) { + if (configurations.ENVIRONMENT_SLUG == null || configurations.ENVIRONMENT_SLUG.isEmpty()) { steps.error("ENVIRONMENT_SLUG is required") } steps.env.ENVIRONMENT_SLUG = configurations.ENVIRONMENT_SLUG diff --git a/first-class-pipeline/src/com/freeleaps/devops/SourceFetcher.groovy b/first-class-pipeline/src/com/freeleaps/devops/SourceFetcher.groovy index 74b764ec..56fc8b53 100644 --- a/first-class-pipeline/src/com/freeleaps/devops/SourceFetcher.groovy +++ b/first-class-pipeline/src/com/freeleaps/devops/SourceFetcher.groovy @@ -7,7 +7,15 @@ class SourceFetcher { this.steps = steps } - def fetch(config) { - // TODO: Implement me! + def fetch(Map configurations) { + if (configurations.SERVICE_GIT_REPO == null || configurations.SERVICE_GIT_REPO.isEmpty()) { + steps.error("SERVICE_GIT_REPO is required") + } + + if (configurations.SERVICE_GIT_BRANCH == null || configurations.SERVICE_GIT_BRANCH.isEmpty()) { + steps.error("SERVICE_GIT_BRANCH is required") + } + + steps.git branch: configurations.SERVICE_GIT_BRANCH, credentialsId: 'git-bot-credentials', url: configurations.SERVICE_GIT_REPO } } \ No newline at end of file diff --git a/first-class-pipeline/src/com/freeleaps/devops/enums/DependenciesManager.groovy b/first-class-pipeline/src/com/freeleaps/devops/enums/DependenciesManager.groovy new file mode 100644 index 00000000..a8afe73c --- /dev/null +++ b/first-class-pipeline/src/com/freeleaps/devops/enums/DependenciesManager.groovy @@ -0,0 +1,27 @@ +package com.freeleaps.devops.enums + +enum DependenciesManager { + PIP('pip'), + NPM('npm'), + YARN('yarn'), + UNKNOWN('Unknown') + + final String manager + + DependenciesManager(String manager) { + this.manager = manager + } + + static DependenciesManager parse(String manager) { + switch (manager) { + case 'pip': + return DependenciesManager.PIP + case 'npm': + return DependenciesManager.NPM + case 'yarn': + return DependenciesManager.YARN + default: + return DependenciesManager.UNKNOWN + } + } +} \ No newline at end of file diff --git a/first-class-pipeline/tests/Jenkinsfile b/first-class-pipeline/tests/Jenkinsfile index 092f95c2..d048450b 100644 --- a/first-class-pipeline/tests/Jenkinsfile +++ b/first-class-pipeline/tests/Jenkinsfile @@ -2,5 +2,13 @@ library 'first-class-pipeline' def configurations = [:] + configurations.put('SERVICE_NAME', 'magicleaps') + configurations.put('SERVICE_LANG', 'Python') + configurations.put('SERVICE_GIT_REPO', '') + configurations.put('SERVICE_GIT_BRANCH', 'master') + configurations.put('ENVIRONMENT_SLUG', 'alpha') + + configurations.put('PY_DEPENDENCIES_MANAGER', 'PIP') + configurations.put('REQUIREMENTS_FILE_PATH', 'requirements.txt') pipelineCall(configurations) \ No newline at end of file diff --git a/first-class-pipeline/vars/pipelineCall.groovy b/first-class-pipeline/vars/pipelineCall.groovy index 21f252a1..ea545448 100644 --- a/first-class-pipeline/vars/pipelineCall.groovy +++ b/first-class-pipeline/vars/pipelineCall.groovy @@ -2,6 +2,8 @@ import com.freeleaps.devops.EnvironmentVars import com.freeleaps.devops.SourceFetcher +import com.freeleaps.devops.DependenciesResolver +import com.freeleaps.devops.enums.DependenciesManager def call(Map configurations) { def environmentVars = new EnvironmentVars(this) @@ -35,15 +37,67 @@ def call(Map configurations) { stage("Commit Linting If Enabled") { steps { script { - def enabled = "${configurations.COMMIT_MESSAGE_LINT_ENABLED}" + def enabled = configurations.COMMIT_MESSAGE_LINT_ENABLED - if (enabled == "true") { + if (enabled != null && enabled == "true") { print("Commit Linting is enabled") } } } } + stage("Build Agent Setup") { + steps { + script { + def buildAgentImage = configurations.BUILD_AGENT_IMAGE + if (buildAgentImage != null && !buildAgentImage.isEmpty()) { + echo "Not set BUILD_AGENT_IMAGE, using default build agent image" + + def language = env.SERVICE_LANG + switch(language) { + case PYTHON: + buildAgentImage = "python:3.10-slim-buster" + case NODE: + buildAgentImage = "node:lts-alpine" + default: + error("Unknown service language") + } + + env.BUILD_AGENT_IMAGE = buildAgentImage + } + } + } + } + + stage("Dependencies Resolving") { + agent { + docker { + image env.BUILD_AGENT_IMAGE + } + } + steps { + script { + def language = env.SERVICE_LANG + + def depManager = DependenciesManager.parse(configurations.DEPENDENCIES_MANAGER) + if (depManager == DependenciesManager.UNKNOWN) { + error("Unknown dependencies manager") + } + + def dependenciesResolver = new DependenciesResolver(this, language) + dependenciesResolver.useManager(depManager) + + if (configurations.BUILD_CACHE_ENABLED == "true") { + dependenciesResolver.enableCachingSupport() + } else { + dependenciesResolver.disableCachingSupport() + } + + dependenciesResolver.resolve(configurations) + } + } + } + } } } \ No newline at end of file