From 67e87247ea414b4e732b9f6b6bb3d112f1f53883 Mon Sep 17 00:00:00 2001 From: William Petit Date: Thu, 17 Aug 2023 07:43:26 -0600 Subject: [PATCH] feat(standard-make): create standard make-based pipeline --- .../com/cadoles/standard-make/Dockerfile | 14 ++ vars/standardMakePipeline.groovy | 137 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 resources/com/cadoles/standard-make/Dockerfile create mode 100644 vars/standardMakePipeline.groovy diff --git a/resources/com/cadoles/standard-make/Dockerfile b/resources/com/cadoles/standard-make/Dockerfile new file mode 100644 index 0000000..642b8fa --- /dev/null +++ b/resources/com/cadoles/standard-make/Dockerfile @@ -0,0 +1,14 @@ +ARG JQ_VERSION=1.6 + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + wget tar curl ca-certificates \ + openssl bash git unzip build-essential + +COPY add-letsencrypt-ca.sh /root/add-letsencrypt-ca.sh + +RUN bash /root/add-letsencrypt-ca.sh \ + && rm -f /root/add-letsencrypt-ca.sh + +RUN wget -O /usr/local/bin/jq https://github.com/stedolan/jq/releases/download/jq-${JQ_VERSION}/jq-linux64 \ + && chmod +x /usr/local/bin/jq \ No newline at end of file diff --git a/vars/standardMakePipeline.groovy b/vars/standardMakePipeline.groovy new file mode 100644 index 0000000..27e7db2 --- /dev/null +++ b/vars/standardMakePipeline.groovy @@ -0,0 +1,137 @@ +import org.jenkinsci.plugins.pipeline.modeldefinition.Utils + +def call(Map options = [:]) { + + Map hooks = options.get('hooks', [ + 'pre': null, + 'post-success': null, + 'post-always': null, + 'post-failure': null, + ]) + String testTask = options.get('testTask', 'test') + String buildTask = options.get('buildTask', 'build') + String releaseTask = options.get('releaseTask', 'release') + String jobHistory = options.get('jobHistory', '10') + + String baseImage = options.get('baseImage', 'reg.cadoles.com/proxy_cache/library/ubuntu:22.04') + String baseDockerfile = options.get('baseDockerfile', '') + String dockerfileExtension = options.get('dockerfileExtension', '') + + node { + properties([ + buildDiscarder(logRotator(daysToKeepStr: jobHistory, numToKeepStr: jobHistory)), + ]) + + stage('Cancel older jobs') { + int buildNumber = env.BUILD_NUMBER as int + if (buildNumber > 1) { + milestone(buildNumber - 1) + } + + milestone(buildNumber) + } + + stage('Checkout project') { + checkout(scm) + } + + stage('Run in container') { + try { + def containerImage = buildContainerImage(baseImage, baseDockerfile, dockerfileExtension) + containerImage.inside('-v /var/run/docker.sock:/var/run/docker.sock') { + String repo = env.JOB_NAME + if (env.BRANCH_NAME ==~ /^PR-.*$/) { + repo = env.JOB_NAME - "/${env.JOB_BASE_NAME}" + } + + stage('Run pre hooks') { + runHook(hooks, 'pre') + } + + stage('Run tests') { + catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') { + String testReport = runTask('testTask', testTask, true) + + if (!!testReport?.trim()) { + if (env.CHANGE_ID) { + gitea.commentPullRequest(repo, env.CHANGE_ID, "# Test report\n\n ${testReport}") + } else { + print testReport + } + } + } + } + + stage('Build project') { + runTask('buildTask', buildTask) + } + + stage('Release project') { + runTask('releaseTask', releaseTask) + } + } + } catch(Exception ex) { + runHook(hooks, 'post-failure', [ex]) + throw ex + } finally { + runHook(hooks, 'post-always') + cleanWs() + } + + runHook(hooks, 'post-success') + } + } +} + +void buildContainerImage(String baseImage, String baseDockerfile, String dockerfileExtension) { + String imageName = 'cadoles-standard-make-ci' + dir(".${imageName}") { + String dockerfile = '' + + if (baseDockerfile) { + dockerfile = baseDockerfile + } else { + dockerfile = libraryResource 'com/cadoles/standard-make/Dockerfile' + dockerfile = "FROM ${baseImage}\n" + dockerfile + } + + dockerfile = """ + ${dockerfile} + ${dockerfileExtension} + """ + + writeFile file:'Dockerfile', text: dockerfile + + String addLetsEncryptCA = libraryResource 'com/cadoles/common/add-letsencrypt-ca.sh' + writeFile file:'add-letsencrypt-ca.sh', text:addLetsEncryptCA + + String safeJobName = URLDecoder.decode(env.JOB_NAME).toLowerCase().replace('/', '-').replace(' ', '-') + String imageTag = "${safeJobName}-${env.BUILD_ID}" + + return docker.build("${imageName}:${imageTag}", '.') + } +} + +void runHook(Map hooks, String name, List args = []) { + if (!hooks[name]) { + println("No hook '${name}' defined. Skipping.") + return + } + + if (hooks[name] instanceof Closure) { + hooks[name](*args) + } else { + error("Hook '${name}' seems to be defined but is not a closure !") + } +} + +String runTask(String name, String task, Boolean returnStdout = false) { + if (!task) { + println("No task '${name}' defined. Skipping.") + return + } + + String result = sh(script: "make ${task}", returnStdout: returnStdout) + + return result +}