├── .mdl.rb ├── .mdlrc ├── .gitallowed ├── .sonarlint └── connectedMode.json ├── vars ├── README.md ├── dockerInfo.groovy ├── isDockerAvailable.groovy ├── dockerInferTag.groovy ├── Notify.groovy ├── isCommandAvailable.groovy ├── checkov.groovy ├── generateEnvVarDockerImages.groovy ├── printEnv.groovy ├── dockerLoginQuay.groovy ├── gcrGenerateEnvVarDockerImages.groovy ├── dockerLoginGitlab.groovy ├── dockerLoginGHCR.groovy ├── gitOwnerRepo.groovy ├── dockerAddTagIfNotExists.groovy ├── dockerPull.groovy ├── dockerPush.groovy ├── jenkinsCLICheckEnvVars.groovy ├── jenkinsJobListAPI.groovy ├── gitCurrentBranch.groovy ├── deploy.groovy ├── gitTagList.groovy ├── loadEnvVars.groovy ├── awsAuth.groovy ├── azureCLILogin.groovy ├── pipenvInstall.groovy ├── dockerLoginACR.groovy ├── loadCredentials.groovy ├── downloadDatree.groovy ├── jenkinsJobConfigXml.groovy ├── gcpDockerAuth.groovy ├── buildScript.groovy ├── gitHashList.groovy ├── sshKnownHostsAzureDevOps.groovy ├── tfsec.groovy ├── loadScript.groovy ├── stringToList.groovy ├── gcpRevokeApplicationCredentials.groovy ├── terraformValidate.groovy ├── dockerInferImageList.groovy ├── downloadYq.groovy ├── gcrTagDockerImage.groovy ├── terraformFmt.groovy ├── jenkinsJobList.groovy ├── dockerLogin.groovy ├── liquibaseStatus.groovy ├── liquibaseUpdate.groovy ├── dockerLoginECR.groovy ├── grypeFS.groovy ├── sshKnownHostsBitbucket.groovy ├── dockerInferImageTagList.groovy ├── sshKnownHostsGitLab.groovy ├── dockle.groovy ├── downloadClairctl.groovy ├── solrReIndex.groovy ├── scriptLockExecute.groovy ├── downloadKICS.groovy ├── sshKnownHostsGitHub.groovy ├── gitBranchList.groovy ├── jenkinsJobJenkinsfile.groovy ├── jenkinsJobBranch.groovy ├── gitMergePipeline.groovy ├── installPackages.groovy ├── downloadJenkinsCLI.groovy ├── gcrTagGitCommitShort.groovy ├── gitCommitShort.groovy ├── terragruntApply.groovy ├── gcrDockerImageExists.groovy ├── cloudflarePurgeCache.groovy ├── terragruntRefreshState.groovy ├── downloadDockle.groovy ├── terraformRefreshState.groovy ├── gcrDockerImagesExistWait.groovy ├── dockerLoginGCR.groovy ├── downloadTerragrunt.groovy ├── gitSetup.groovy ├── mapUserEmails.groovy ├── dockerLoginGAR.groovy ├── downloadTerraform.groovy ├── gitLogBrokenCommitters.groovy ├── jenkinsJobRepo.groovy ├── downloadSonarScanner.groovy ├── downloadGitHubReleaseBinary.groovy ├── sonarScanner.groovy ├── downloadCodeQL.groovy ├── gitMerge.groovy ├── printAuth.groovy ├── garDockerAuth.groovy ├── downloadGrype.groovy ├── terragruntInit.groovy ├── gcrDockerAuth.groovy ├── githubCreateRelease.groovy ├── gcpSetupApplicationCredentials.groovy ├── approval.groovy ├── gcpActivateServiceAccount.groovy ├── downloadTrivy.groovy ├── terragruntPlan.groovy ├── githubNextRelease.groovy ├── terraformInit.groovy ├── terraformApply.groovy └── downloadKustomize.groovy ├── .github ├── workflows │ ├── jenkinsfile.yaml │ ├── groovyc.yaml │ ├── sonarcloud.yaml │ ├── fork-sync.yaml │ ├── markdown.yaml │ ├── url_links.yaml.disabled │ ├── kics.yaml │ ├── fork-update-pr.yaml │ ├── grype.yaml │ ├── codeowners.yaml │ ├── validate.yaml │ ├── trivy.yaml │ ├── checkov.yaml │ ├── semgrep-cloud.yaml │ ├── yaml.yaml │ └── semgrep.yaml └── CODEOWNERS ├── LICENSE ├── Makefile ├── sonar-project.properties ├── .envrc-python ├── .checkov.yaml ├── .editorconfig └── .pre-commit-config.yaml /.mdl.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitallowed: -------------------------------------------------------------------------------- 1 | AWS_ACCOUNT_ID 2 | -------------------------------------------------------------------------------- /.sonarlint/connectedMode.json: -------------------------------------------------------------------------------- 1 | { 2 | "sonarCloudOrganization": "harisekhon", 3 | "projectKey": "HariSekhon_Jenkins" 4 | } 5 | -------------------------------------------------------------------------------- /vars/README.md: -------------------------------------------------------------------------------- 1 | Jenkins Shared Library 2 | ====================== 3 | 4 | Groovy Functions + Pipelines code to be reused between Jenkins Pipelines. 5 | 6 | See top-level [README](https://github.com/HariSekhon/Jenkins/blob/master/README.md) for documentation. 7 | -------------------------------------------------------------------------------- /vars/dockerInfo.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-15 02:08:29 +0100 (Mon, 15 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r I n f o 18 | // ========================================================================== // 19 | 20 | def call() { 21 | label 'Docker Info' 22 | timeout (time: 2, unit: 'MINUTES') { 23 | echo "Docker Info" 24 | sh( 25 | label: 'Docker Info', 26 | script: 'docker info' 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vars/isDockerAvailable.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-03 18:47:04 +0100 (Sun, 03 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // Liceese: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // I s D o c k e r A v a i l a b l e 18 | // ========================================================================== // 19 | 20 | // returns Boolean if 'docker' command is found in the $PATH 21 | // 22 | // XXX: could extend to check that Docker server is responding too 23 | 24 | def call () { 25 | isCommandAvailable('docker') 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/jenkinsfile.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/GitHub-Actions 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | --- 15 | name: Jenkinsfile 16 | 17 | on: # yamllint disable-line rule:truthy 18 | push: 19 | branches: 20 | - master 21 | - main 22 | paths: 23 | - '**/Jenkinsfile' 24 | pull_request: 25 | branches: 26 | - master 27 | - main 28 | paths: 29 | - Jenkinsfile 30 | workflow_dispatch: 31 | 32 | permissions: 33 | contents: read 34 | 35 | jobs: 36 | jenkinsfile: 37 | if: github.event.repository.fork == false 38 | name: Jenkinsfile 39 | uses: HariSekhon/GitHub-Actions/.github/workflows/jenkinsfile.yaml@master 40 | -------------------------------------------------------------------------------- /.github/workflows/groovyc.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/GitHub-Actions 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | --- 15 | name: Groovy Shared Library 16 | 17 | on: # yamllint disable-line rule:truthy 18 | push: 19 | branches: 20 | - master 21 | - main 22 | paths: 23 | - src/**/*.groovy 24 | - vars/*.groovy 25 | pull_request: 26 | branches: 27 | - master 28 | - main 29 | paths: 30 | - src/**/*.groovy 31 | - vars/*.groovy 32 | workflow_dispatch: 33 | 34 | permissions: 35 | contents: read 36 | 37 | jobs: 38 | groovyc: 39 | if: github.event.repository.fork == false 40 | name: Groovy Shared Library 41 | uses: HariSekhon/GitHub-Actions/.github/workflows/groovyc.yaml@master 42 | -------------------------------------------------------------------------------- /vars/dockerInferTag.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-14 04:43:32 +0100 (Sun, 14 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r I n f e r T a g 18 | // ========================================================================== // 19 | 20 | // Infers a Docker Image Tag from possible environment variables and returns it as a String 21 | 22 | def call(tag='') { 23 | return tag ?: 24 | env.DOCKER_TAG ?: 25 | env.CONTAINER_TAG ?: 26 | env.VERSION ?: 27 | env.GIT_COMMIT ?: 28 | 'latest' 29 | } 30 | -------------------------------------------------------------------------------- /vars/Notify.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-19 19:19:36 +0100 (Tue, 19 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // N o t i f y 18 | // ========================================================================== // 19 | 20 | // Wrapper to call all other notification functions to abstract any changes from the many calling pipelines 21 | 22 | def call () { 23 | if (env.NO_NOTIFY != 'true') { 24 | // add all notification methods here: 25 | // 26 | // emailNotify() 27 | // ... 28 | // 29 | slackNotify() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vars/isCommandAvailable.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-03 18:47:04 +0100 (Sun, 03 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // Liceese: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Checks if given command is available in $PATH 18 | // ========================================================================== // 19 | 20 | // returns Boolean whether given command is available in the $PATH 21 | 22 | def call (bin) { 23 | sh ( 24 | label: "Check if '$bin' command is available in \$PATH ($PATH)", 25 | returnStatus: true, 26 | script: "command -v '$bin' || type -P '$bin' || which '$bin'" 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /vars/checkov.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-19 17:54:02 +0000 (Wed, 19 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // C h e c k o v 18 | // ========================================================================== // 19 | 20 | def call (timeoutMinutes=10) { 21 | container('checkov') { 22 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 23 | ansiColor('xterm') { 24 | sh label: 'Checkov', 25 | script: 'checkov -d . -o junitxml > result.xml || true' 26 | junit 'result.xml' 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vars/generateEnvVarDockerImages.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-16 04:35:38 +0100 (Tue, 16 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Generate DOCKER_IMAGES environment variable with registry prefixes 18 | // ========================================================================== // 19 | 20 | def call(images=[], registry='') { 21 | echo "Generating DOCKER_IMAGES environment variable" + ( registry ? " with registry prefix '$registry'" : '') 22 | env.DOCKER_IMAGES = images.collect { (registry ? "$registry/" : '') + it.trim() }.join(',') 23 | return env.DOCKER_IMAGES 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Hari Sekhon 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2022-05-16 15:06:57 +0100 (Mon, 16 May 2022) 4 | # 5 | # vim:ts=4:sts=4:sw=4:noet 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # For serious Makefiles see the DevOps Bash tools repo: 15 | # 16 | # https://github.com/HariSekhon/DevOps-Bash-tools 17 | # 18 | # Makefile 19 | # Makefile.in - generic include file with lots of Make targets 20 | 21 | SHELL = /usr/bin/env bash 22 | 23 | .PHONY: * 24 | default: test clean 25 | @: 26 | 27 | test: 28 | @# script is in DevOps Bash tools repo, clone whole repo for dependency lib and put it in the $PATH 29 | check_groovyc.sh 30 | 31 | push: 32 | git push 33 | 34 | clean: 35 | @#echo "Removing .class files" 36 | find . -name '*.class' -exec rm {} \; 37 | 38 | wc: 39 | git ls-files Jenkinsfile vars/ | xargs wc -l 40 | 41 | sync: 42 | . .envrc; github_repo_fork_sync.sh 43 | -------------------------------------------------------------------------------- /vars/printEnv.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-08-31 17:53:01 +0100 (Tue, 31 Aug 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // P r i n t E n v i r o n m e n t V a r i a b l e s 18 | // ========================================================================== // 19 | 20 | // Call this at the start of every pipeline to make debugging easier by having this info always available 21 | 22 | def call () { 23 | timeout (time: 1, unit: 'MINUTES') { 24 | String label = 'Environment' 25 | sh ( 26 | label: "$label", 27 | script: ''' 28 | set -eux 29 | env | sort 30 | ''' 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # vim:ts=4:sts=4:sw=4:et 2 | # 3 | # Author: Hari Sekhon 4 | # Date: 2023-05-11 22:56:21 +0100 (Thu, 11 May 2023) 5 | # 6 | # https://github.com/HariSekhon/Jenkins 7 | # 8 | # License: see accompanying Hari Sekhon LICENSE file 9 | # 10 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 11 | # 12 | # https://www.linkedin.com/in/HariSekhon 13 | # 14 | 15 | # Required metadata 16 | sonar.organization=harisekhon 17 | sonar.projectName=Jenkins 18 | sonar.projectKey=HariSekhon_Jenkins 19 | sonar.projectVersion=1.0 20 | 21 | sonar.projectDescription=Jenkins 22 | 23 | sonar.links.homepage=https://github.com/HariSekhon/Jenkins 24 | sonar.links.scm=https://github.com/HariSekhon/Jenkins 25 | sonar.links.issue=https://github.com/HariSekhon/Jenkins/issues 26 | sonar.links.ci=https://github.com/HariSekhon/Jenkins/actions 27 | 28 | # directories to scan (defaults to sonar-project.properties dir otherwise) 29 | sonar.sources=. 30 | 31 | #sonar.language=py 32 | 33 | sonar.sourceEncoding=UTF-8 34 | -------------------------------------------------------------------------------- /vars/dockerLoginQuay.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:46:51 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to Quay Container Registry 18 | // ========================================================================== // 19 | 20 | // QUAY_USER and QUAY_TOKEN must be set in the calling environment 21 | 22 | def call (user='', token='') { 23 | user = user ?: env.QUAY_USER ?: error('dockerLoginQuay: user not specified and QUAY_USER not set in the environment') 24 | token = token ?: env.QUAY_TOKEN ?: error('dockerLoginQuay: token not specified and QUAY_TOKEN not set in the environment') 25 | dockerLogin(user, token, 'quay.io') 26 | } 27 | -------------------------------------------------------------------------------- /vars/gcrGenerateEnvVarDockerImages.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-16 04:35:38 +0100 (Tue, 16 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Generate DOCKER_IMAGES environment variable with full GCR registry prefixes 18 | // ========================================================================== // 19 | 20 | def call(images=[]) { 21 | if (! env.GCR_REGISTRY) { 22 | error('GCR_REGISTRY environment variable not set before calling gcrGenerateEnvVarDockerImages()') 23 | } 24 | if (! env.GCR_PROJECT) { 25 | error('GCR_PROJECT environment variable not set before calling gcrGenerateEnvVarDockerImages()') 26 | } 27 | 28 | return generateEnvVarDockerImages(images, "$GCR_REGISTRY/$GCR_PROJECT") 29 | } 30 | -------------------------------------------------------------------------------- /vars/dockerLoginGitlab.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:46:51 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to GitLab Container Registry 18 | // ========================================================================== // 19 | 20 | // GITLAB_USER and GITLAB_TOKEN must be passed as args or set in the calling environment 21 | 22 | def call (user='', token='') { 23 | user = user ?: env.GITLAB_USER ?: error('dockerLoginGitlab: user not specified and GITLAB_USER not set in the environment') 24 | token = token ?: env.GITLAB_TOKEN ?: error('dockerLoginGitlab: token not specified and GITLAB_TOKEN not set in the environment') 25 | dockerLogin(user, token, 'registry.gitlab.com') 26 | } 27 | -------------------------------------------------------------------------------- /vars/dockerLoginGHCR.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:46:51 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to GitHub Container Registry 18 | // ========================================================================== // 19 | 20 | // GITHUB_USER and GH_TOKEN / GITHUB_TOKEN must be passed as args or set in the calling environment 21 | 22 | def call (user='', token='') { 23 | user = user ?: env.GITHUB_USER ?: error('dockerLoginGHCR: user not specified and GITHUB_USER not set in the environment') 24 | token = token ?: env.GH_TOKEN ?: env.GITHUB_TOKEN ?: error('dockerLoginGHCR: token not specified and GH_TOKEN / GITHUB_TOKEN not set in the environment') 25 | dockerLogin(user, token, 'ghcr.io') 26 | } 27 | -------------------------------------------------------------------------------- /vars/gitOwnerRepo.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-02-17 19:19:21 +0000 (Fri, 17 Feb 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t < O w n e r > / < R e p o > N a m e 18 | // ========================================================================== // 19 | 20 | // Returns the name of the current Git repo in the / format by inferring from it the GIT_URL environment variable 21 | 22 | def call () { 23 | String repoUrl = env.GIT_URL 24 | 25 | // strip any git@github.com: prefix 26 | String ownerRepo = repoUrl.split(':')[-1] 27 | 28 | // take the last two components from "https://github.com//" to leave just "/" 29 | ownerRepo = ownerRepo.split('/')[-2, -1].join('/') 30 | 31 | return ownerRepo 32 | } 33 | -------------------------------------------------------------------------------- /vars/dockerAddTagIfNotExists.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-14 04:54:05 +0100 (Sun, 14 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r A d d T a g I f N o t E x i s t s 18 | // ========================================================================== // 19 | 20 | // checks a given docker container image for a tag suffix and adds it if not given 21 | 22 | def call(image, tag='') { 23 | if (!image) { 24 | error "no docker image passed as first arg to dockerAddTagIfNotExists() function" 25 | } 26 | //if (!tag) { 27 | //error "no docker tag passed as second arg to dockerAddTagIfNotExists() function" 28 | //} 29 | tag = tag ?: dockerInferTag() 30 | if (!image.contains(':')) { 31 | image += ":$tag" 32 | } 33 | return image 34 | } 35 | -------------------------------------------------------------------------------- /vars/dockerPull.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-15 02:08:29 +0100 (Mon, 15 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r P u l l 18 | // ========================================================================== // 19 | 20 | def call(List imageList=[], timeoutMinutes=15) { 21 | label 'Docker Pull' 22 | List images = [] 23 | if (imageList) { 24 | images = imageList 25 | } else { 26 | images = dockerInferImageTagList() 27 | } 28 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 29 | for (image in images) { 30 | withEnv (["IMAGE=$image"]) { 31 | echo "Docker Pull '$IMAGE'" 32 | sh( 33 | label: 'Docker Pull', 34 | script: "docker pull '$IMAGE'" 35 | ) 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vars/dockerPush.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-15 02:08:29 +0100 (Mon, 15 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r P u s h 18 | // ========================================================================== // 19 | 20 | def call(List imageList=[], timeoutMinutes=30) { 21 | label 'Docker Push' 22 | List images = [] 23 | if (imageList) { 24 | images = imageList 25 | } else { 26 | images = dockerInferImageTagList() 27 | } 28 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 29 | for (image in images) { 30 | withEnv (["IMAGE=$image"]) { 31 | echo "Docker Push '$IMAGE'" 32 | sh( 33 | label: 'Docker Push', 34 | script: "docker push '$IMAGE'" 35 | ) 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vars/jenkinsCLICheckEnvVars.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-30 18:00:52 +0100 (Thu, 30 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Check Environment Variables for Jenkins CLI 18 | // ========================================================================== // 19 | 20 | def call () { 21 | sh ( 22 | label: 'Check Environment Variables for Jenkins CLI', 23 | script: '''#!/usr/bin/env bash 24 | set -eux 25 | # doesn't work with _ 26 | for var in JENKINS_URL JENKINS_USER_ID JENKINS_API_TOKEN; do 27 | echo "Checking $var" 28 | if [ -z "${!var:-}" ]; then 29 | echo "$var is not set" 30 | exit 1 31 | fi 32 | done 33 | echo 'All necessary Jenkins CLI environment variables set' 34 | ''' 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /vars/jenkinsJobListAPI.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-06-08 23:36:19 +0100 (Thu, 08 Jun 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // J e n k i n s J o b L i s t 18 | // ========================================================================== // 19 | 20 | // Returns a list of strings of jenkins job names - lighter weight than using the Jenkins CLI but requires in-process script approval by an Administrator 21 | 22 | def call() { 23 | // requires several iterations of In-process Script Approvals from repeatedly failing pipelines at each level of descent into the jenkins.model hierarchy 24 | //List jobs = jenkins.model.Jenkins.instance.items.findAll().collect { it.name } 25 | List jobs = jenkins.model.Jenkins.instance.items.findAll()*.name 26 | 27 | return jobs 28 | } 29 | -------------------------------------------------------------------------------- /vars/gitCurrentBranch.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-31 01:12:44 +0100 (Fri, 31 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t C u r r e n t B r a n c h 18 | // ========================================================================== // 19 | 20 | // Returns a string of the current branch name 21 | // 22 | // Assumes it's executing from inside a git cloned checkout 23 | // 24 | // Requires 'git' to be in the $PATH 25 | 26 | def call () { 27 | currentBranch = sh ( 28 | label: 'Git Current Branch', 29 | returnStdout: true, 30 | script: """ 31 | set -eux 32 | git rev-parse --abbrev-ref HEAD 33 | """ 34 | ).trim() 35 | if ( ! currentBranch ) { 36 | error('Failed to determine Git current branch in function gitCurrentBranch()') 37 | } 38 | return currentBranch 39 | } 40 | -------------------------------------------------------------------------------- /vars/deploy.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D e p l o y 18 | // ========================================================================== // 19 | 20 | def call () { 21 | String label = "Deploy App, Environment: " + "$ENVIRONMENT".capitalize() 22 | echo "Acquiring Deployment Lock: $label" 23 | lock (resource: label, inversePrecedence: true) { 24 | // XXX: prevents calling in a parallel stage otherwise you'll get this error: 25 | // 26 | // "Using a milestone step inside parallel is not allowed" 27 | // 28 | milestone ordinal: null, label: "Milestone: $label" 29 | retry (2) { 30 | timeout (time: 20, unit: 'MINUTES') { 31 | // script in local repo 32 | sh 'deploy.sh' 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vars/gitTagList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-31 01:12:44 +0100 (Fri, 31 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t T a g L i s t 18 | // ========================================================================== // 19 | 20 | // Returns a list of strings of all git tags in the current git log in the current git repo checkout 21 | // 22 | // Assumes it's executing from inside a git cloned checkout 23 | // 24 | // Requires 'git' to be in the $PATH 25 | 26 | def call () { 27 | // could limit this with 'git log -n NN' but only takes 0.5 secs for nearly 30,000 in local git testing 28 | tags = sh ( 29 | label: 'Git Tags', 30 | returnStdout: true, 31 | script: """ 32 | set -eux 33 | git tag --list 34 | """ 35 | ) 36 | tagList = tags.trim().split('\n') 37 | return tagList 38 | } 39 | -------------------------------------------------------------------------------- /vars/loadEnvVars.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-19 00:43:12 +0100 (Fri, 19 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // L o a d E n v i r o n m e n t V a r i a b l e s 18 | // ========================================================================== // 19 | 20 | // Takes a Map argument of environment variables and loads it into the environment to be used later in the pipeline 21 | // 22 | // Written for the Setup() stage of templated pipelines where you need to load environment variables only passed in to some pipelines 23 | // 24 | // Usage: 25 | // 26 | // loadEnvVars(['NO_NOTIFY': 'true']) 27 | 28 | def call(envVars) { 29 | if (! envVars) { 30 | return 31 | } 32 | if (envVars instanceof Map == false) { 33 | error 'envVars passed to loadEnvVars() must be a Map' 34 | } 35 | envVars.each { k, v -> 36 | env[k] = v 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vars/awsAuth.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-03 19:18:16 +0100 (Sun, 03 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // A W S C L I A u t h 18 | // ========================================================================== // 19 | 20 | def call () { 21 | sh ( 22 | label: 'AWS CLI auth', 23 | script: 'aws sts get-caller-identity' 24 | ) 25 | script { 26 | if (!env.AWS_ACCOUNT_ID && isCommandAvailable('jq')) { 27 | env.AWS_ACCOUNT_ID = sh ( 28 | label: 'Generate AWS Account ID environment variable', 29 | returnStdout: true, 30 | script: 'aws sts get-caller-identity | jq -r .Account' 31 | ) 32 | if (!env.AWS_ACCOUNT_ID) { 33 | error('Failed to determine AWS Account ID') 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vars/azureCLILogin.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-03 19:11:10 +0100 (Sun, 03 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // L o g i n t o A z u r e C L I 18 | // ========================================================================== // 19 | 20 | def call (user='', pass='') { 21 | user = user ?: env.get('AZURE_USER', error('azureCLILogin: user not specified and AZURE_USER not set in the environment')) 22 | pass = pass ?: env.get('AZURE_PASSWORD', error('azureCLILogin: pass not specified and AZURE_PASSWORD not set in the environment')) 23 | withEnv(["AZURE_USER=$user", "AZURE_PASSWORD=$pass"]) { 24 | sh ( 25 | label: 'Azure CLI Login', 26 | script: 'az login -u "$AZURE_USER" -p "$AZURE_PASSWORD"' 27 | ) 28 | sh ( 29 | label: 'Azure CLI Show Signed In User', 30 | script: 'az ad signed-in-user show' 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vars/pipenvInstall.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-19 14:32:02 +0100 (Tue, 19 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // P i p e n v I n s t a l l 18 | // ========================================================================== // 19 | 20 | // Sets up pipenv and installs the given package 21 | // 22 | // Must be run in an agent or container with 'pipenv' available such as 'kennethreitz/pipenv:latest' in kubernetes jenkins-agent-pod.yaml 23 | // 24 | // Usage: 25 | // 26 | // container('pipenv') { 27 | // pipenvInstall('checkov') 28 | // sh 'pipenv run checkov ...' 29 | // } 30 | // 31 | 32 | def call (pipPackage) { 33 | withEnv(["PACKAGE=$pipPackage"]) { 34 | sh ( 35 | label: 'Pipenv Install', 36 | script: ''' 37 | set -eux 38 | pipenv install 39 | pipenv run pip install $PACKAGE 40 | ''' 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vars/dockerLoginACR.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:55:43 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to Azure Container Registry 18 | // ========================================================================== // 19 | 20 | // must be called after 'az login' and have Azure CLI in the calling environment 21 | // 22 | // $ACR_REGISTRY_NAME must be passed as the first argument or else set in the calling environment 23 | 24 | def call (registry='') { 25 | registry = registry ?: env.ACR_REGISTRY_NAME ?: error('dockerLoginACR: Azure registry name not specified and ACR_REGISTRY_NAME not set in the environment') 26 | withEnv(["ACR_REGISTRY_NAME=$registry"]) { 27 | script { 28 | // configures docker config with a token 29 | sh ''' 30 | set -eux 31 | az acr login --name "$ACR_REGISRY_NAME" 32 | ''' 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vars/loadCredentials.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-19 00:43:12 +0100 (Fri, 19 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // L o a d E n v i r o n m e n t V a r i a b l e s 18 | // ========================================================================== // 19 | 20 | // Takes a Map argument of environment variables and Jenkins secret credential IDs and loads the secrets into the environment variables to be used later in the pipeline 21 | // 22 | // Written for the Setup() stage of templated pipelines where you need to load credentials only passed in to some pipelines 23 | // 24 | // Usage: 25 | // 26 | // loadCredentials(['MY_SECRET_TOKEN': 'my-jenkins-key-id']) 27 | 28 | def call(creds) { 29 | if (! creds) { 30 | return 31 | } 32 | if (creds instanceof Map == false) { 33 | error 'creds passed to loadCredentials() must be a Map' 34 | } 35 | creds.each { k, v -> 36 | env[k] = credentials(v) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/sonarcloud.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2023-04-14 23:53:43 +0100 (Fri, 14 Apr 2023) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # S o n a r C l o u d 16 | # ============================================================================ # 17 | 18 | --- 19 | name: SonarCloud 20 | 21 | on: # yamllint disable-line rule:truthy 22 | push: 23 | branches: 24 | - master 25 | - main 26 | paths-ignore: 27 | - '**/*.md' 28 | pull_request: 29 | branches: 30 | - master 31 | - main 32 | paths-ignore: 33 | - '**/*.md' 34 | workflow_dispatch: 35 | 36 | permissions: 37 | contents: read 38 | pull-requests: read 39 | 40 | jobs: 41 | SonarCloud: 42 | # github.event.repository context not available in scheduled workflows 43 | #if: github.event.repository.fork == false 44 | if: github.repository_owner == 'HariSekhon' 45 | name: SonarCloud 46 | uses: HariSekhon/GitHub-Actions/.github/workflows/sonarcloud.yaml@master 47 | secrets: 48 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 49 | -------------------------------------------------------------------------------- /vars/downloadDatree.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-22 18:06:58 +0100 (Fri, 22 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d D a t r e e 18 | // ========================================================================== // 19 | 20 | //def call (version = '1.5.1') { 21 | // withEnv(["VERSION=${version}"]) { 22 | def call () { 23 | installPackages( 24 | [ 25 | 'bash', 26 | 'curl', 27 | 'unzip' 28 | ] 29 | ) 30 | String label = "Download Datree on agent '$HOSTNAME'" 31 | echo "Acquiring Lock: $label" 32 | lock (resource: "$label") { 33 | sh ( 34 | label: "$label", 35 | script: ''' 36 | set -eux 37 | 38 | curl https://get.datree.io | 39 | /usr/bin/env bash 40 | ''' 41 | ) 42 | sh ( 43 | label: "Datree Version", 44 | script: ''' 45 | set -eu 46 | datree version 47 | ''' 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vars/jenkinsJobConfigXml.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-06-08 23:36:19 +0100 (Thu, 08 Jun 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // J e n k i n s J o b X M L 18 | // ========================================================================== // 19 | 20 | // Returns a string of the Jenkins Job XML for a given job using the Jenkins CLI 21 | // 22 | // assumes JENKIN_CLI_JAR has been downloaded in a previous stage via downloadJenkinsCLI() function 23 | 24 | def call(jobName) { 25 | withEnv(["JOB_NAME=$jobName"]) { 26 | String jobXml = sh ( 27 | label: "Get Job XML via CLI", 28 | returnStdout: true, 29 | script: ''' 30 | set -eux 31 | java -jar "${JENKINS_CLI_JAR:-$HOME/bin/jenkins-cli.jar}" ${JENKINS_CLI_ARGS:-} get-job "$JOB_NAME" 32 | ''' 33 | ) 34 | if ( ! jobXml ) { 35 | error("Failed to retrieve Jenkins job config xml for job '$jobName'") 36 | } 37 | 38 | return jobXml 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vars/gcpDockerAuth.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 17:58:49 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Configure Docker to Authenticate to GCR 18 | // ========================================================================== // 19 | 20 | // Configures Docker for both Google Container Registry and the newer Google Artifact Registry 21 | 22 | // Requires: 23 | // 24 | // - gcpActivateServiceAccount.groovy to be called first to authenticate GCloud SDK 25 | // - needs GCloud SDK to be installed on the agent - if on Kubernetes make it the default container or else wrap this call in container('gcloud-sdk') { } 26 | // 27 | 28 | // if passing blank for GAR registries, garAuthDocker() will auto-determine all registries 29 | // if passing blank for GCR registries, will use hardcoded default list taken from documentation 30 | def call (garRegistries='', gcrRegistries='') { 31 | garDockerAuth(garRegistries) 32 | gcrDockerAuth(gcrRegistries) 33 | } 34 | -------------------------------------------------------------------------------- /vars/buildScript.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // B u i l d 18 | // ========================================================================== // 19 | 20 | // Do not call this file build.groovy, it will clash with the built-in 'build' which is used by jenkinsfileLibraryUpdatePipeline.groovy 21 | 22 | def call () { 23 | echo "Running Job '${env.JOB_NAME}' Build ${env.BUILD_ID} on ${env.JENKINS_URL}" 24 | echo "Building from branch '${env.GIT_BRANCH}' for '" + "${env.ENVIRONMENT}".capitalize() + "' Environment" 25 | // XXX: prevents calling in a parallel stage otherwise you'll get this error: 26 | // 27 | // "Using a milestone step inside parallel is not allowed" 28 | // 29 | milestone ordinal: null, label: "Milestone: Build" 30 | retry (2) { 31 | timeout (time: 40, unit: 'MINUTES') { 32 | // script in local repo 33 | sh 'build.sh' 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vars/gitHashList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-31 01:12:44 +0100 (Fri, 31 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t H a s h L i s t 18 | // ========================================================================== // 19 | 20 | // Returns a list of strings of all git long hashrefs in the current git log in the current git repo checkout 21 | // 22 | // Assumes it's executing from inside a git cloned checkout 23 | // 24 | // Requires 'git' to be in the $PATH 25 | 26 | def call (shortRef=false) { 27 | format = '%H' 28 | if (shortRef) { 29 | format = '%h' 30 | } 31 | // could limit this with 'git log -n NN' but only takes 0.5 secs for nearly 30,000 in local git testing 32 | hashRefs = sh ( 33 | label: 'Git Log Hashrefs', 34 | returnStdout: true, 35 | script: """ 36 | set -eux 37 | git log --pretty=format:'$format' 38 | """ 39 | ) 40 | hashRefList = hashRefs.trim().split('\n') 41 | return hashRefList 42 | } 43 | -------------------------------------------------------------------------------- /vars/sshKnownHostsAzureDevOps.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-28 16:10:36 +0000 (Fri, 28 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S S H K n o w n H o s t s - A z u r e D e v O p s 18 | // ========================================================================== // 19 | 20 | // Loads Azure DevOps SSH Known Hosts 21 | // 22 | // ssh-keyscan ssh.dev.azure.com 23 | 24 | def call () { 25 | sshKnownHosts(""" 26 | # ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10 27 | ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H 28 | # ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10 29 | # ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10 30 | """, 'Azure DevOps' 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /vars/tfsec.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:10:38 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T F S e c 18 | // ========================================================================== // 19 | 20 | // Terraform code security scanner 21 | 22 | def call (timeoutMinutes=10) { 23 | label 'tfsec' 24 | container('tfsec') { 25 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 26 | //dir ("components/${COMPONENT}") { 27 | ansiColor('xterm') { 28 | // aquasec/tfsec image is based on Alpine, doesn't have bash 29 | //sh '''#!/usr/bin/env bash -euxo pipefail 30 | //sh '''#!/bin/sh -eux 31 | // use --no-color if not using ansicolor plugin 32 | sh 'tfsec --update' 33 | sh 'tfsec --version' 34 | sh' tfsec --run-statistics' // nice summary table 35 | sh 'tfsec --soft-fail' // don't error 36 | sh 'tfsec' // full details and error out if issues found 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.envrc-python: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # vim:ts=4:sts=4:sw=4:et 3 | # 4 | # Author: Hari Sekhon 5 | # Date: Mon Feb 22 17:42:01 2021 +0000 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # License: see accompanying Hari Sekhon LICENSE file 10 | # 11 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | # 13 | # https://www.linkedin.com/in/HariSekhon 14 | # 15 | 16 | # ============================================================================ # 17 | # P y t h o n D i r E n v 18 | # ============================================================================ # 19 | 20 | # .envrc to auto-load the virtualenv inside the 'venv' directory if present 21 | 22 | # https://direnv.net/man/direnv-stdlib.1.html 23 | 24 | set -euo pipefail 25 | [ -n "${DEBUG:-}" ] && set -x 26 | #srcdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 27 | 28 | # this is necessary because newer versions of pip no longer allow you to install PyPI packages in system-packages by default 29 | for venv in "$PWD/venv" "$HOME/venv"; do 30 | if [ -f "$venv/bin/activate" ]; then 31 | echo 32 | echo "Virtualenv directory found in: $venv" 33 | echo 34 | echo "Activating Virtualenv inside the directory: $venv" 35 | 36 | # shellcheck disable=SC1091 37 | source "$venv/bin/activate" 38 | break 39 | fi 40 | done 41 | 42 | # read .env too 43 | #dotenv 44 | -------------------------------------------------------------------------------- /vars/loadScript.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-11 16:23:33 +0000 (Tue, 11 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // L o a d S c r i p t 18 | // ========================================================================== // 19 | 20 | // Copies a unix script to the local workspace and chmod's it to be used in a subsequent 'sh' step, eg: 21 | // 22 | // steps { 23 | // loadScript('script.sh') 24 | // // or 25 | // // loadScript('script.sh', 'path/to/srcdir') 26 | // sh './script.sh ...' 27 | // } 28 | // 29 | // Alternatively, do what I do for my GitHub repos and use a git submodule or clone another repo locally to use its tools 30 | 31 | def call (file, dir = '.') { 32 | withEnv(["FILE=$file"]) { 33 | def scriptContents = libraryResource "$dir/$file" 34 | writeFile file: "$file", 35 | text: scriptContents 36 | //encoding: "Base64" # if the file is Base 64 encoded to decode it before writing (eg. for binaries) 37 | sh 'chmod a+x "./$FILE"' 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vars/stringToList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-15 02:09:36 +0100 (Mon, 15 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S t r i n g T o L i s t 18 | // ========================================================================== // 19 | 20 | // utility function to allow other functions to accept either a single String or a List 21 | // and pass it through this function to end up with a List of Strings either way 22 | // account for 23 | 24 | def call(arg) { 25 | List list = [] 26 | if (arg) { 27 | if (arg instanceof String || 28 | arg instanceof org.codehaus.groovy.runtime.GStringImpl) { 29 | list = [arg] 30 | // ! arg instanceof List does not work and 31 | // arg !instanceof List is only available in Groovy 3 32 | } else if (arg instanceof List == false) { 33 | error "non-list passed as first arg to stringToList() function" 34 | } else { 35 | list = arg 36 | } 37 | } else { 38 | error "no arg passed to stringToList() function" 39 | } 40 | return list 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/fork-sync.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # F o r k S y n c 16 | # ============================================================================ # 17 | 18 | # For a fork of the original repo, activate to keep it up to date via straight GitHub sync to the default branch 19 | 20 | --- 21 | name: Fork Sync 22 | 23 | on: # yamllint disable-line rule:truthy 24 | workflow_dispatch: 25 | inputs: 26 | debug: 27 | type: boolean 28 | required: false 29 | default: false 30 | schedule: 31 | - cron: '0 */3 * * *' 32 | 33 | permissions: 34 | contents: write 35 | 36 | concurrency: 37 | group: ${{ github.workflow }}-${{ github.ref }} 38 | cancel-in-progress: false 39 | 40 | jobs: 41 | fork_sync: 42 | # github.event.repository context not available in scheduled workflows 43 | #if: github.event.repository.fork == true 44 | if: github.repository_owner != 'HariSekhon' 45 | name: Fork Sync 46 | uses: HariSekhon/GitHub-Actions/.github/workflows/fork-sync.yaml@master 47 | with: 48 | debug: ${{ github.event.inputs.debug }} 49 | -------------------------------------------------------------------------------- /vars/gcpRevokeApplicationCredentials.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-09-01 12:50:03 +0100 (Wed, 01 Sep 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G C P R e v o k e A p p l i c a t i o n C r e d e n t i a l s 18 | // ========================================================================== // 19 | 20 | // Removes the application default credentials file 21 | // 22 | // Use this if you're using long-running agents 23 | // 24 | // Not needed on Kubernetes agents which are ephemeral 25 | 26 | def call (timeoutMinutes=1) { 27 | retry (2) { 28 | timeout (time: "$timeoutMinutes", unit: 'MINUTES') { 29 | String label = 'Deleting GCP Application Credential Key' 30 | echo "$label" 31 | sh ( 32 | label: "$label", 33 | script: '''#!/usr/bin/env bash 34 | set -euxo pipefail 35 | # XXX: must match gcpSetupApplicationCredentials.groovy 36 | keyfile="$WORKSPACE_TMP/.gcloud/application-credentials.json.$BUILD_TAG" 37 | rm -fv "$keyfile" 38 | ''' 39 | ) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.checkov.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2022-02-21 16:53:29 +0000 (Mon, 21 Feb 2022) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # License: see accompanying Hari Sekhon LICENSE file 10 | # 11 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | # 13 | # https://www.linkedin.com/in/HariSekhon 14 | # 15 | 16 | # ============================================================================ # 17 | # C h e c k o v c o n f i g 18 | # ============================================================================ # 19 | 20 | # https://github.com/bridgecrewio/checkov#configuration-using-a-config-file 21 | # 22 | # This is not well documented but the fields seem to be the same as: 23 | # 24 | # checkov --help 25 | # 26 | # See master template at: 27 | # 28 | # https://github.com/HariSekhon/Templates/blob/master/.checkov.yaml 29 | 30 | --- 31 | compact: true 32 | directory: 33 | - . 34 | download-external-modules: true # without this gets lots of annoying warning lines such as '2022-02-22 16:14:40,180 [MainThread ] [WARNI] Failed to download module x/y/z:n.n.n' 35 | framework: 36 | - all 37 | no-guide: true 38 | output: cli 39 | quiet: true 40 | repo-id: HariSekhon/Jenkins # what to report to Bridgecrew Cloud - without this gets annoying duplicate repos such as 'harisekhon_cli_repo/jenkins' 41 | skip-suppressions: true 42 | soft-fail: true 43 | -------------------------------------------------------------------------------- /vars/terraformValidate.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a f o r m V a l i d a t e 18 | // ========================================================================== // 19 | 20 | // Validates your Terraform code 21 | // 22 | // Used in terraformPipeline.groovy 23 | 24 | def call (timeoutMinutes=2) { 25 | String label = 'Terraform Validate' 26 | 27 | // forbids older inits from starting 28 | milestone(ordinal: null, label: "Milestone: $label") 29 | 30 | // terraform docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 31 | //container('terraform') { 32 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 33 | ansiColor('xterm') { 34 | dir(env.TERRAFORM_DIR ?: ".") { 35 | echo 'Terraform Validate' 36 | echo "$label" 37 | sh ( 38 | label: "$label", 39 | script: 'terraform validate' 40 | ) 41 | } 42 | } 43 | } 44 | //} 45 | } 46 | -------------------------------------------------------------------------------- /vars/dockerInferImageList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-14 05:04:01 +0100 (Sun, 14 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r I n f e r I m a g e L i s t 18 | // ========================================================================== // 19 | 20 | // Constructs and returns a List of docker images without tags by inferring from environment variables 21 | 22 | def call() { 23 | List images = [] 24 | if (env.DOCKER_IMAGES) { 25 | images = env.DOCKER_IMAGES.split(',') 26 | } else if (env.DOCKER_IMAGES_TAGS) { 27 | for (docker_image in env.DOCKER_IMAGES_TAGS.split(',')) { 28 | images.add(docker.image.split(':')[0]) 29 | } 30 | } else if (env.DOCKER_IMAGE) { 31 | images.add(env.DOCKER_IMAGE) 32 | } else if (env.DOCKER_IMAGE_TAG) { 33 | images.add(env.DOCKER_IMAGE.split(':'[0])) 34 | } else { 35 | error "Failed to infer docker images for list. None of the following environment variables were found: \$DOCKER_IMAGES, \$DOCKER_IMAGES_TAGS, \$DOCKER_IMAGE, \$DOCKER_IMAGE_TAG" 36 | } 37 | return images 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/markdown.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2023-04-14 23:53:43 +0100 (Fri, 14 Apr 2023) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # M a r k D o w n 16 | # ============================================================================ # 17 | 18 | --- 19 | name: Markdown 20 | 21 | on: # yamllint disable-line rule:truthy 22 | push: 23 | branches: 24 | - master 25 | - main 26 | paths: 27 | - '**/*.md' 28 | - .mdlrc 29 | - .mdl.rb 30 | - .markdownlint.rb 31 | - .github/workflows/markdown.yaml 32 | pull_request: 33 | branches: 34 | - master 35 | - main 36 | paths: 37 | - '**/*.md' 38 | - .mdlrc 39 | - .mdl.rb 40 | - .markdownlint.rb 41 | - .github/workflows/markdown.yaml 42 | workflow_dispatch: 43 | 44 | permissions: 45 | contents: read 46 | pull-requests: read 47 | 48 | jobs: 49 | Markdown: 50 | # github.event.repository context not available in scheduled workflows 51 | #if: github.event.repository.fork == false 52 | if: github.repository_owner == 'HariSekhon' 53 | name: Markdown 54 | uses: HariSekhon/GitHub-Actions/.github/workflows/markdown.yaml@master 55 | -------------------------------------------------------------------------------- /vars/downloadYq.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 05:24:55 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d Y q 18 | // ========================================================================== // 19 | 20 | // Downloading it for each run trades inbound bandwidth (free) for not using RAM for bigger Jenkins pods causing more scale and billable Kubernetes 21 | // 22 | // Downloading Yq only takes 2 seconds in testing 23 | // 24 | // The alternative is using the docker image which will be cached but hold RAM for the entire duration of the pipeline, which is very RAM inefficient: 25 | // 26 | // https://github.com/HariSekhon/Kubernetes-configs/blob/master/jenkins/base/jenkins-agent-pod.yaml 27 | 28 | // get release version from: 29 | // 30 | // https://github.com/mikefarah/yq/releases 31 | // 32 | 33 | def call (version='latest') { 34 | downloadGitHubReleaseBinary('mikefarah/yq', 'yq', version) 35 | sh ( 36 | label: "Yq Version", 37 | script: ''' 38 | set -eu 39 | yq --version 40 | ''' 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/url_links.yaml.disabled: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2022-01-26 14:10:12 +0000 (Wed, 26 Jan 2022) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | --- 15 | name: URL Links 16 | 17 | on: 18 | push: 19 | branches: 20 | - master 21 | - main 22 | pull_request: 23 | branches: 24 | - master 25 | - main 26 | workflow_dispatch: 27 | inputs: 28 | debug: 29 | type: boolean 30 | required: false 31 | default: false 32 | schedule: 33 | - cron: '0 0 * * 1' 34 | 35 | permissions: 36 | contents: read 37 | 38 | concurrency: 39 | group: ${{ github.workflow }}-${{ github.ref }} 40 | cancel-in-progress: true 41 | 42 | jobs: 43 | url_links: 44 | # github.event.repository context not available in scheduled workflows 45 | #if: github.event.repository.fork == false 46 | if: github.repository_owner == 'HariSekhon' 47 | name: URL Links 48 | uses: HariSekhon/GitHub-Actions/.github/workflows/url_links.yaml@master 49 | with: 50 | ignore_urls_without_dots: 'true' # any value enables this 51 | url_links_ignored: | 52 | https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv 53 | somerepository.com 54 | mycompany 55 | https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK 56 | -------------------------------------------------------------------------------- /vars/gcrTagDockerImage.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-09 16:58:44 +0000 (Thu, 09 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G r o o v y 18 | // ========================================================================== // 19 | 20 | // Tags a given GCR Docker Image registry path and commit with a new alternative tag 21 | // 22 | // Written for gcrTagGitCommitShort() to be able to create a convenience tag of short git commit 23 | // 24 | // XXX: WARNING: this will overwrite the given newTag - you should first run gcrDockerImageExists() to check if it exists and perhaps skip retagging, eg. see gcrTagGitCommitShort() 25 | 26 | def call (String dockerImageRegistryPathTag, String newTag) { 27 | String dockerImageRegistryPath = dockerImageRegistryPathTag.split(':')[0] 28 | echo "Tagging docker image '$dockerImageRegistryPathTag' with new tag '$newTag'" 29 | sh ( 30 | label: "GCloud add tag", 31 | script: """ 32 | set -eux 33 | gcloud container images add-tag --quiet "$dockerImageRegistryPathTag" "$dockerImageRegistryPath:$newTag" 34 | """ 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /vars/terraformFmt.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a f o r m F o r m a t 18 | // ========================================================================== // 19 | 20 | // Find out if your Terraform code conforms to formatting guidelines 21 | // 22 | // You can set this to optional, see adjacent: 23 | // 24 | // terraformPipeline.groovy 25 | 26 | def call (timeoutMinutes=1) { 27 | String label = 'Terraform Fmt' 28 | 29 | // forbids older inits from starting 30 | milestone(ordinal: null, label: "Milestone: $label") 31 | 32 | // terraform docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 33 | //container('terraform') { 34 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 35 | ansiColor('xterm') { 36 | dir(env.TERRAFORM_DIR ?: ".") { 37 | echo 'Terraform Fmt' 38 | echo "$label" 39 | sh ( 40 | label: "$label", 41 | script: 'terraform fmt -check -diff' 42 | ) 43 | } 44 | } 45 | } 46 | //} 47 | } 48 | -------------------------------------------------------------------------------- /vars/jenkinsJobList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-06-08 23:36:19 +0100 (Thu, 08 Jun 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // J e n k i n s J o b L i s t 18 | // ========================================================================== // 19 | 20 | // Returns a list of strings of jenkins job names 21 | // 22 | // assumes JENKIN_CLI_JAR has been downloaded in a previous stage via downloadJenkinsCLI() function 23 | 24 | def call() { 25 | // requires several iterations of In-process Script Approvals from repeatedly failing pipelines at each level of descent into the jenkins.model hierarchy 26 | //List jobs = jenkins.model.Jenkins.instance.items.findAll().collect { it.name } 27 | // might be able to be more concisely written as a spread expression: 28 | // jenkins.model.Jenkins.instance.items.findAll()*.name 29 | 30 | List jobs = sh ( 31 | label: "List Jobs via CLI", 32 | returnStdout: true, 33 | script: ''' 34 | set -eux 35 | java -jar "${JENKINS_CLI_JAR:-$HOME/bin/jenkins-cli.jar}" ${JENKINS_CLI_ARGS:-} list-jobs 36 | ''' 37 | ).tokenize('\n') 38 | 39 | return jobs 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/kics.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2022-02-01 19:36:08 +0000 (Tue, 01 Feb 2022) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # K i c s 16 | # ============================================================================ # 17 | 18 | --- 19 | name: Kics 20 | 21 | on: # yamllint disable-line rule:truthy 22 | push: 23 | branches: 24 | - master 25 | - main 26 | paths-ignore: 27 | - '**/*.md' 28 | pull_request: 29 | branches: 30 | - master 31 | - main 32 | paths-ignore: 33 | - '**/*.md' 34 | workflow_dispatch: 35 | inputs: 36 | debug: 37 | type: boolean 38 | required: false 39 | default: false 40 | schedule: 41 | - cron: '0 0 * * 1' 42 | 43 | permissions: 44 | actions: read 45 | contents: read 46 | security-events: write 47 | 48 | concurrency: 49 | group: ${{ github.workflow }}-${{ github.ref }} 50 | cancel-in-progress: true 51 | 52 | jobs: 53 | kics: 54 | # github.event.repository context not available in scheduled workflows 55 | #if: github.event.repository.fork == false 56 | if: github.repository_owner == 'HariSekhon' 57 | name: Kics 58 | uses: HariSekhon/GitHub-Actions/.github/workflows/kics.yaml@master 59 | with: 60 | debug: ${{ github.event.inputs.debug }} 61 | -------------------------------------------------------------------------------- /vars/dockerLogin.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:46:51 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // Liceese: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r L o g i n t o D o c k e r H u b 18 | // ========================================================================== // 19 | 20 | // See Also: 21 | // 22 | // gcpAuthDocker.groovy 23 | // gcrAuthDocker.groovy 24 | // garAuthDocker.groovy 25 | 26 | def call (user='', pass='', registry='') { 27 | user = user ?: env.DOCKERHUB_USER ?: error('dockerLogin: username not specified and DOCKERHUB_USER not set in the environment') 28 | password = password ?: env.DOCKERHUB_TOKEN ?: error('dockerLogin: password/token not specified and DOCKERHUB_TOKEN not set in the environment') 29 | echo "Docker Login: $user" 30 | echo "Docker Registry: ${registry ?: 'DockerHub'}" 31 | withEnv(["USER=$user", "PASS=$pass", "REGISTRY=$registry"]) { 32 | // Bourne compatible 33 | //sh ''' 34 | // set -eux 35 | // docker login $REGISTRY -u "$USER" -p "$PASS" 36 | //''' 37 | // requires Bash 38 | sh '''#!/usr/bin/env bash 39 | set -eux 40 | docker login $REGISTRY -u "$USER" --password-stdin <<< "$PASS" 41 | ''' 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vars/liquibaseStatus.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 18:02:06 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // L i q u i b a s e S t a t u s 18 | // ========================================================================== // 19 | 20 | // Requires the following environment variables to be set in pipeline: 21 | // 22 | // JDBC_URL - full jdbc url to the DB 23 | // LIQUIBASE_CREDENTIALS - type usernamePassword to implicitly expand to LIQUIBASE_CREDENTIALS_USR/LIQUIBASE_CREDENTIALS_PSW 24 | // LIQUIBASE_CHANGELOG_FILE - xml file 25 | 26 | def call (timeoutMinutes=20) { 27 | label 'Liquibase Status' 28 | 29 | // XXX: set Liquibase version in the docker image tag in jenkins-agent-pod.yaml 30 | container('liquibase') { 31 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 32 | ansiColor('xterm') { 33 | sh label: 'Liquibase Version', 34 | script: 'liquibase --version' 35 | 36 | sh label: 'Liquibase Status', 37 | script: 'liquibase status --url="$JDBC_URL" --changeLogFile="$LIQUIBASE_CHANGELOG_FILE" --username="$LIQUIBASE_CREDENTIALS_USR" --password="$LIQUIBASE_CREDENTIALS_PSW"' 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /vars/liquibaseUpdate.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 18:02:06 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // L i q u i b a s e U p d a t e 18 | // ========================================================================== // 19 | 20 | // Requires the following environment variables to be set in pipeline: 21 | // 22 | // JDBC_URL - full jdbc url to the DB 23 | // LIQUIBASE_CREDENTIALS - type usernamePassword to implicitly expand to LIQUIBASE_CREDENTIALS_USR/LIQUIBASE_CREDENTIALS_PSW 24 | // LIQUIBASE_CHANGELOG_FILE - xml file 25 | 26 | def call (timeoutMinutes=20) { 27 | label 'Liquibase Update' 28 | 29 | // XXX: set Liquibase version in the docker image tag in jenkins-agent-pod.yaml 30 | container('liquibase') { 31 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 32 | ansiColor('xterm') { 33 | sh label: 'Liquibase Version', 34 | script: 'liquibase --version' 35 | 36 | sh label: 'Liquibase Update', 37 | script: 'liquibase update --url="$JDBC_URL" --changeLogFile="$LIQUIBASE_CHANGELOG_FILE" --username="$LIQUIBASE_CREDENTIALS_USR" --password="$LIQUIBASE_CREDENTIALS_PSW"' 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2021-11-09 15:14:59 +0000 (Tue, 09 Nov 2021) 4 | # 5 | # vim:ts=4:sts=4:sw=4:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # License: see accompanying Hari Sekhon LICENSE file 10 | # 11 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | # 13 | # https://www.linkedin.com/in/HariSekhon 14 | # 15 | 16 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 17 | 18 | # Good in theory, to alert on PR changes to these code paths, but for public repos which may be forked and run .github/workflows/fork-update.yaml, this will result in a lot of spam 19 | 20 | # Tips: 21 | # 22 | # * includes changes under .github/ 23 | # dir/* only matches first level file changes but doesn't recurse 24 | # dir/ recurses 25 | # 26 | # - CODEOWNERS in base branch of PR determines review request 27 | # - paths are case sensitive 28 | # - last match wins, use * at top for overall owner then override with more specific teams 29 | 30 | #* @harisekhon # username or email address 31 | #* @myorg/platform-engineering # team based is the way to go - team must have Write access to the repo regardless of if individuals have access 32 | #* @myorg/devops 33 | #k8s @myorg/devops @myorg/sre-team 34 | #apps/ @myorg/developers 35 | #apps/dir2 # ignores dir2 as no owner/team specified on this line 36 | #src/ @myorg/developers 37 | #docs/ docs@example.com 38 | #.github/workflows @ci-cd-team 39 | -------------------------------------------------------------------------------- /.github/workflows/fork-update-pr.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # F o r k U p d a t e P R 16 | # ============================================================================ # 17 | 18 | # For a fork of the original repo, activate to keep its branches up to date via Pull Requests 19 | # 20 | # To be used in conjunction with the adjacent fork-sync.yaml which keeps the default branch up to date 21 | 22 | --- 23 | name: Fork Update PR 24 | 25 | on: # yamllint disable-line rule:truthy 26 | workflow_dispatch: 27 | inputs: 28 | debug: 29 | type: boolean 30 | required: false 31 | default: false 32 | schedule: 33 | - cron: '0 10 * * 1' 34 | 35 | permissions: 36 | contents: write 37 | pull-requests: write 38 | 39 | concurrency: 40 | group: ${{ github.workflow }}-${{ github.ref }} 41 | cancel-in-progress: false 42 | 43 | jobs: 44 | fork_update_pr: 45 | # github.event.repository context not available in scheduled workflows 46 | #if: github.event.repository.fork == true 47 | if: github.repository_owner != 'HariSekhon' 48 | name: Fork Update PR 49 | uses: HariSekhon/GitHub-Actions/.github/workflows/fork-update-pr.yaml@master 50 | with: 51 | debug: ${{ github.event.inputs.debug }} 52 | -------------------------------------------------------------------------------- /.github/workflows/grype.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2023-05-13 01:07:56 +0100 (Sat, 13 May 2023) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # G r y p e 16 | # ============================================================================ # 17 | 18 | --- 19 | name: Grype 20 | 21 | on: # yamllint disable-line rule:truthy 22 | push: 23 | branches: 24 | - master 25 | - main 26 | paths-ignore: 27 | - '**/*.md' 28 | pull_request: 29 | branches: 30 | - master 31 | - main 32 | paths-ignore: 33 | - '**/*.md' 34 | workflow_dispatch: 35 | inputs: 36 | debug: 37 | type: boolean 38 | required: false 39 | default: false 40 | schedule: 41 | - cron: '0 0 * * 1' 42 | 43 | permissions: 44 | actions: read 45 | contents: read 46 | security-events: write 47 | 48 | concurrency: 49 | group: ${{ github.workflow }}-${{ github.ref }} 50 | cancel-in-progress: true 51 | 52 | jobs: 53 | Grype: 54 | # github.event.repository context not available in scheduled workflows 55 | #if: github.event.repository.fork == false 56 | if: github.repository_owner == 'HariSekhon' 57 | name: Grype 58 | uses: HariSekhon/GitHub-Actions/.github/workflows/grype.yaml@master 59 | with: 60 | debug: ${{ github.event.inputs.debug }} 61 | -------------------------------------------------------------------------------- /.github/workflows/codeowners.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # C o d e O w n e r s 16 | # ============================================================================ # 17 | 18 | --- 19 | name: CodeOwners 20 | 21 | on: # yamllint disable-line rule:truthy 22 | push: 23 | branches: 24 | - master 25 | - main 26 | paths: 27 | - CODEOWNERS 28 | - .github/CODEOWNERS 29 | pull_request: 30 | branches: 31 | - master 32 | - main 33 | paths: 34 | - CODEOWNERS 35 | - .github/CODEOWNERS 36 | workflow_dispatch: 37 | inputs: 38 | debug: 39 | type: boolean 40 | required: false 41 | default: false 42 | schedule: 43 | - cron: '0 0 * * 1' 44 | 45 | permissions: 46 | contents: read 47 | 48 | concurrency: 49 | group: ${{ github.workflow }}-${{ github.ref }} 50 | cancel-in-progress: true 51 | 52 | jobs: 53 | validate: 54 | # github.event.repository context not available in scheduled workflows 55 | #if: github.event.repository.fork == false 56 | if: github.repository_owner == 'HariSekhon' 57 | name: Validate CODEOWNERS 58 | uses: HariSekhon/GitHub-Actions/.github/workflows/codeowners.yaml@master 59 | with: 60 | debug: ${{ github.event.inputs.debug }} 61 | -------------------------------------------------------------------------------- /.github/workflows/validate.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # V a l i d a t i o n 16 | # ============================================================================ # 17 | 18 | # Run all custom validations against files in the repo 19 | 20 | --- 21 | name: Validation 22 | 23 | on: # yamllint disable-line rule:truthy 24 | push: 25 | branches: 26 | - master 27 | - main 28 | paths-ignore: 29 | - '**/*.md' 30 | pull_request: 31 | branches: 32 | - master 33 | - main 34 | paths-ignore: 35 | - '**/*.md' 36 | workflow_dispatch: 37 | inputs: 38 | debug: 39 | type: boolean 40 | required: false 41 | default: false 42 | #schedule: 43 | # - cron: '0 0 * * 1' 44 | 45 | permissions: 46 | contents: read 47 | 48 | concurrency: 49 | group: ${{ github.workflow }}-${{ github.ref }} 50 | cancel-in-progress: true 51 | 52 | jobs: 53 | validate: 54 | # github.event.repository context not available in scheduled workflows 55 | #if: github.event.repository.fork == false 56 | if: github.repository_owner == 'HariSekhon' 57 | name: Validate 58 | uses: HariSekhon/GitHub-Actions/.github/workflows/validate.yaml@master 59 | with: 60 | debug: ${{ github.event.inputs.debug }} 61 | -------------------------------------------------------------------------------- /vars/dockerLoginECR.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:55:43 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to AWS Elastic Container Registry 18 | // ========================================================================== // 19 | 20 | // AWS CLI must be available in the environment and the following environment variables must be set: 21 | // 22 | // AWS_ACCESS_KEY_ID 23 | // AWS_SECRET_ACCESS_KEY 24 | // AWS_DEFAULT_REGION 25 | // AWS_ACCOUNT_ID // should be set by awsAuth() if jq is available 26 | 27 | def call () { 28 | env.AWS_ACCOUNT_ID ?: error('dockerLoginECR: AWS_ACCOUNT_ID not set in the environment') 29 | env.AWS_DEFAULT_REGION ?: error('dockerLoginECR: AWS_DEFAULT_REGION not set in the environment') 30 | ECR_TOKEN = sh ( 31 | label: 'Generating ECR Authentication Token', 32 | returnStdout: true, 33 | script: 'aws ecr get-login-password --region "$AWS_DEFAULT_REGION"' 34 | ) 35 | script { 36 | if (! ECR_TOKEN) { 37 | error('dockerLoginECR: Failed to generate ECR_TOKEN') 38 | } 39 | } 40 | dockerLogin('AWS', ECR_TOKEN, "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com") 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/trivy.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2022-02-02 11:27:37 +0000 (Wed, 02 Feb 2022) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # T r i v y 16 | # ============================================================================ # 17 | 18 | # Scan files in the local repo 19 | 20 | --- 21 | name: Trivy 22 | 23 | on: # yamllint disable-line rule:truthy 24 | push: 25 | branches: 26 | - master 27 | - main 28 | paths-ignore: 29 | - '**/*.md' 30 | pull_request: 31 | branches: 32 | - master 33 | - main 34 | paths-ignore: 35 | - '**/*.md' 36 | workflow_dispatch: 37 | inputs: 38 | debug: 39 | type: boolean 40 | required: false 41 | default: false 42 | schedule: 43 | - cron: '0 0 * * 1' 44 | 45 | permissions: 46 | actions: read 47 | contents: read 48 | security-events: write 49 | 50 | concurrency: 51 | group: ${{ github.workflow }}-${{ github.ref }} 52 | cancel-in-progress: true 53 | 54 | jobs: 55 | trivy: 56 | # github.event.repository context not available in scheduled workflows 57 | #if: github.event.repository.fork == false 58 | if: github.repository_owner == 'HariSekhon' 59 | name: Trivy 60 | uses: HariSekhon/GitHub-Actions/.github/workflows/trivy.yaml@master 61 | with: 62 | debug: ${{ github.event.inputs.debug }} 63 | -------------------------------------------------------------------------------- /.github/workflows/checkov.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # C h e c k o v G i t H u b W o r k f l o w 16 | # ============================================================================ # 17 | 18 | # Static analysis of Terraform code - publishes report to GitHub Security tab 19 | 20 | # https://github.com/bridgecrewio/checkov-action 21 | 22 | --- 23 | name: Checkov 24 | 25 | on: # yamllint disable-line rule:truthy 26 | push: 27 | branches: 28 | - master 29 | - main 30 | paths-ignore: 31 | - '**/*.md' 32 | pull_request: 33 | branches: 34 | - master 35 | - main 36 | paths-ignore: 37 | - '**/*.md' 38 | workflow_dispatch: 39 | inputs: 40 | debug: 41 | type: boolean 42 | required: false 43 | default: false 44 | schedule: 45 | - cron: '0 0 * * 1' 46 | 47 | permissions: 48 | actions: read 49 | contents: read 50 | security-events: write 51 | 52 | jobs: 53 | checkov: 54 | # github.event.repository context not available in scheduled workflows 55 | #if: github.event.repository.fork == false 56 | if: github.repository_owner == 'HariSekhon' 57 | name: Checkov 58 | uses: HariSekhon/GitHub-Actions/.github/workflows/checkov.yaml@master 59 | with: 60 | debug: ${{ github.event.inputs.debug }} 61 | -------------------------------------------------------------------------------- /vars/grypeFS.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-12 17:45:24 +0100 (Fri, 12 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G r y p e S c a n L o c a l F i l e s 18 | // ========================================================================== // 19 | 20 | // Security scanner 21 | 22 | // Pass a starting directory to scan everything underneath it - defaults to '.' for $PWD which in CI/CD is your repo checkout 23 | 24 | // For requirements see adjacent grype.groovy 25 | // 26 | // Usage: 27 | // 28 | // grypeFs() // scan the git clone checkout in the root directory where we start the pipeline 29 | // 30 | // grypeFs('src') // scan only the src code directory 31 | // 32 | // 33 | // Wrap in a 'catchError' to leave it as informational but not break the build - as it's very common for there to be some CVEs etc and you don't usually want it blocking people 34 | // 35 | // catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { 36 | // grypeFS() 37 | // } 38 | // 39 | 40 | def call (dir='.', failOn='high', timeoutMinutes=10) { 41 | label 'Grype' 42 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 43 | withEnv (["DIR=$dir"]) { 44 | grype(["dir:$DIR"], failOn, timeoutMinutes) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vars/sshKnownHostsBitbucket.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-28 16:10:36 +0000 (Fri, 28 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S S H K n o w n H o s t s - B i t b u c k e t 18 | // ========================================================================== // 19 | 20 | // Loads Bitbucket.org SSH Known Hosts 21 | // 22 | // ssh-keyscan bitbucket.org 23 | 24 | def call () { 25 | sshKnownHosts(""" 26 | # bitbucket.org:22 SSH-2.0-conker_3aee7f2e9e d387e8eb814e 27 | # bitbucket.org:22 SSH-2.0-conker_3aee7f2e9e 29a8875ef620 28 | # bitbucket.org:22 SSH-2.0-conker_3aee7f2e9e c6f6c13a1084 29 | # 30 | # https://bitbucket.org/blog/ssh-host-key-changes 31 | # 32 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQeJzhupRu0u0cdegZIa8e86EG2qOCsIsD1Xw0xSeiPDlCr7kq97NLmMbpKTX6Esc30NuoqEEHCuc7yWtwp8dI76EEEB1VqY9QJq6vk+aySyboD5QF61I/1WeTwu+deCbgKMGbUijeXhtfbxSxm6JwGrXrhBdofTsbKRUsrN1WoNgUa8uqN1Vx6WAJw1JHPhglEGGHea6QICwJOAr/6mrui/oB7pkaWKHj3z7d1IC4KWLtY47elvjbaTlkN04Kc/5LFEirorGYVbt15kAUlqGM65pk6ZBxtaO3+30LVlORZkxOh+LKL/BvbZ/iRNhItLqNyieoQj/uh/7Iv4uyH/cV/0b4WDSd3DptigWq84lJubb9t/DnZlrJazxyDCulTmKdOR7vs9gMTo+uoIrPSb8ScTtvw65+odKAlBj59dhnVp9zd7QUojOpXlL62Aw56U4oO+FALuevvMjiWeavKhJqlR7i5n9srYcrNV7ttmDw7kf/97P5zauIhxcjX+xHv4M= bitbucket.org 33 | """, 'Bitbucket' 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /vars/dockerInferImageTagList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-14 05:04:01 +0100 (Sun, 14 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k e r I n f e r I m a g e T a g L i s t 18 | // ========================================================================== // 19 | 20 | // Constructs and returns a List of docker image:tag strings by inferring from environment variables 21 | 22 | def call() { 23 | List images = [] 24 | String tag = dockerInferTag() 25 | if (env.DOCKER_IMAGES_TAGS) { 26 | for (docker_image in env.DOCKER_IMAGES_TAGS.split(',')) { 27 | images.add(dockerAddTagIfNotExists(docker_image, tag)) 28 | } 29 | } else if (env.DOCKER_IMAGES) { 30 | for (docker_image in env.DOCKER_IMAGES.split(',')) { 31 | images.add(dockerAddTagIfNotExists(docker_image, tag)) 32 | } 33 | } else if (env.DOCKER_IMAGE_TAG) { 34 | images.add(dockerAddTagIfNotExists(env.DOCKER_IMAGE, tag)) 35 | } else if (env.DOCKER_IMAGE) { 36 | images.add(dockerAddTagIfNotExists(env.DOCKER_IMAGE, tag)) 37 | } else { 38 | error "Failed to infer docker image:tag items for list. None of the following environment variables were found: \$DOCKER_IMAGES_TAGS, \$DOCKER_IMAGES, \$DOCKER_IMAGE_TAG, \$DOCKER_IMAGE" 39 | } 40 | return images 41 | } 42 | -------------------------------------------------------------------------------- /vars/sshKnownHostsGitLab.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-28 16:10:36 +0000 (Fri, 28 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S S H K n o w n H o s t s - G i t L a b 18 | // ========================================================================== // 19 | 20 | // Loads GitLab.com SSH Known Hosts 21 | // 22 | // ssh-keyscan gitlab.com 23 | 24 | def call () { 25 | sshKnownHosts(""" 26 | # gitlab.com:22 SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 27 | gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 28 | # gitlab.com:22 SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 29 | gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= 30 | # gitlab.com:22 SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 31 | gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf 32 | """, 'GitLab' 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /vars/dockle.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-16 02:46:54 +0100 (Tue, 16 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o c k l e 18 | // ========================================================================== // 19 | 20 | // Dockle container security scanner 21 | 22 | // Usage: 23 | // 24 | // If you're running on a Jenkins agent that already has the dockle binary bundled just call it otherwise download dockle first 25 | // Downloading Dockle only takes 7 seconds in testing 26 | // 27 | // downloadDockle() 28 | // 29 | // dockle('image:tag') 30 | // 31 | // If you want to make it informational but not break the build: 32 | // 33 | // catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { 34 | // dockle('...') 35 | // } 36 | // 37 | // 38 | // Set environment variables to access private registries 39 | // 40 | // https://github.com/goodwithtech/dockle#authorization-for-private-docker-registry 41 | 42 | def call (args='') { 43 | label 'Dockle' 44 | // let caller decide if wrapping this in a container('dockle') or using downloadDockle.groovy to save RAM 45 | //container('dockle') { 46 | ansiColor('xterm') { 47 | sh ( 48 | label: "Dockle", 49 | script: "dockle --exit-code 1 $args" 50 | ) 51 | } 52 | //} 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/semgrep-cloud.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # S e m g r e p C l o u d W o r k f l o w 16 | # ============================================================================ # 17 | 18 | # Logs results to https://semgrep.dev/ 19 | 20 | --- 21 | name: Semgrep Cloud 22 | 23 | on: # yamllint disable-line rule:truthy 24 | push: 25 | branches: 26 | - master 27 | - main 28 | paths-ignore: 29 | - '**/*.md' 30 | pull_request: 31 | branches: 32 | - master 33 | - main 34 | paths-ignore: 35 | - '**/*.md' 36 | workflow_dispatch: 37 | inputs: 38 | debug: 39 | type: boolean 40 | required: false 41 | default: false 42 | schedule: 43 | - cron: '0 0 * * 1' 44 | 45 | permissions: 46 | contents: read 47 | 48 | concurrency: 49 | group: ${{ github.workflow }}-${{ github.ref }} 50 | cancel-in-progress: true 51 | 52 | jobs: 53 | semgrep: 54 | # github.event.repository context not available in scheduled workflows 55 | #if: github.event.repository.fork == false 56 | if: github.repository_owner == 'HariSekhon' 57 | name: Semgrep Cloud 58 | uses: HariSekhon/GitHub-Actions/.github/workflows/semgrep-cloud.yaml@master 59 | secrets: 60 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 61 | with: 62 | debug: ${{ github.event.inputs.debug }} 63 | -------------------------------------------------------------------------------- /.github/workflows/yaml.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # Y A M L 16 | # ============================================================================ # 17 | 18 | # Validate any YAML files found in the repo 19 | 20 | --- 21 | name: YAML 22 | 23 | on: # yamllint disable-line rule:truthy 24 | push: 25 | branches: 26 | - master 27 | - main 28 | paths: 29 | - '**/*.yml' 30 | - '**/*.yaml' 31 | - .github/workflows/yaml.yaml 32 | pull_request: 33 | branches: 34 | - master 35 | - main 36 | paths: 37 | - '**/*.yml' 38 | - '**/*.yaml' 39 | - .github/workflows/yaml.yaml 40 | workflow_dispatch: 41 | inputs: 42 | debug: 43 | type: boolean 44 | required: false 45 | default: false 46 | schedule: 47 | - cron: '0 0 * * 1' 48 | 49 | permissions: 50 | contents: read 51 | 52 | concurrency: 53 | group: ${{ github.workflow }}-${{ github.ref }} 54 | cancel-in-progress: true 55 | 56 | jobs: 57 | check_yaml: 58 | # github.event.repository context not available in scheduled workflows 59 | #if: github.event.repository.fork == false 60 | if: github.repository_owner == 'HariSekhon' 61 | name: Check YAML 62 | uses: HariSekhon/GitHub-Actions/.github/workflows/yaml.yaml@master 63 | with: 64 | debug: ${{ github.event.inputs.debug }} 65 | -------------------------------------------------------------------------------- /vars/downloadClairctl.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-15 05:21:13 +0100 (Mon, 15 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d C l a i r c t l 18 | // ========================================================================== // 19 | 20 | // Downloading Clairctl only takes 4 seconds in testing 21 | 22 | def call (version='4.6.1') { 23 | String label = "Download Clairctl on agent '$HOSTNAME'" 24 | // strip a 'v' prefix if present because we add it to the URL ourselves 25 | if (version[0] == 'v') { 26 | version = version.substring(1) 27 | } 28 | echo "Acquiring Lock: $label" 29 | lock (resource: "$label") { 30 | timeout (time: 2, unit: 'MINUTES') { 31 | withEnv(["VERSION=$version"]) { 32 | echo "$label" 33 | sh ( 34 | label: label, 35 | script: ''' 36 | set -eux 37 | curl -sSL -o /tmp/clairctl.$$ "https://github.com/quay/clair/releases/download/v$VERSION/clairctl-linux-amd64" 38 | chmod +x /tmp/clairctl.$$ 39 | mv -vf /tmp/clairctl.$$ /usr/local/bin/clairctl 40 | ''' 41 | ) 42 | sh ( 43 | label: "Clairctl Version", 44 | script: ''' 45 | set -eu 46 | clairctl --version 47 | ''' 48 | ) 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vars/solrReIndex.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S o l r R e - I n d e x 18 | // ========================================================================== // 19 | 20 | // Run the given script to do a Solr re-index, locking it so that no other script or deployment will clash with it 21 | 22 | // Required Environment Variables to be set in environment{} section of Jenkinsfile, see top level Jenkinsfile template 23 | // 24 | // APP 25 | // ENVIRONMENT 26 | 27 | def call (String scriptPath, int timeoutMinutes=60) { 28 | String app = env.APP.toLowerCase() 29 | String environment = env.ENVIRONMENT.toLowerCase() 30 | String Environment = env.ENVIRONMENT.capitalize() 31 | echo "Solr Re-Indexing App '$APP' '$Environment' from branch '$GIT_BRANCH'" 32 | // XXX: these locks must match whatever locks are used by your deployment functions, in this case gitKustomizeImages() and argoDeploy() 33 | String gitKustomizeLock = "Git Kustomize Image Version - Dir: '$app/$environment'" 34 | String deploymentLock = "ArgoCD Deploy - App: '$app-$environment'" 35 | String indexingLock = "Solr Re-Indexing - App: '$APP', Environment: $Environment" 36 | 37 | scriptLockExecute(scriptPath, [gitKustomizeLock, deploymentLock, indexingLock], timeoutMinutes) 38 | } 39 | -------------------------------------------------------------------------------- /vars/scriptLockExecute.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S c r i p t L o c k + E x e c u t e 18 | // ========================================================================== // 19 | 20 | // Runs a given script with one or more given locks to prevent more than 1 copy of the script ever executing or to prevent a script from running during a deployment, or deployments starting while a script is running 21 | 22 | def call (String scriptPath, List locks, int timeoutMinutes=60) { 23 | // generate a list in this format 24 | //lock (extra: [[resource: 'lock1'], [resource: 'lock2']]) 25 | List extraLocks = [] 26 | for (lock in locks) { 27 | echo "Acquiring Lock: $lock" 28 | extraLocks.add([resource: lock]) 29 | } 30 | lock (extra: extraLocks, inversePrecedence: true) { 31 | // XXX: prevents calling in a parallel stage otherwise you'll get this error: 32 | // 33 | // "Using a milestone step inside parallel is not allowed" 34 | // 35 | milestone label: "Milestone: Running script: $scriptPath" 36 | retry (2) { 37 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 38 | // external script needs to exist in the source repo, not the shared library repo 39 | sh "$scriptPath" 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vars/downloadKICS.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-02-01 18:56:45 +0000 (Tue, 01 Feb 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d K I C S 18 | // ========================================================================== // 19 | 20 | // obsolete - Kics doesn't support downloadable binaries after 1.5.1 21 | def call (version = '1.5.1') { 22 | String label = "Download KICS on agent '$HOSTNAME'" 23 | echo "Acquiring Lock: $label" 24 | lock (resource: "$label") { 25 | withEnv(["VERSION=${version}"]) { 26 | sh ( 27 | label: "$label", 28 | script: ''' 29 | set -eux 30 | 31 | mkdir -p ~/bin 32 | 33 | cd ~/bin 34 | 35 | export PATH="$PATH:$HOME/bin:$HOME/bin/bash-tools" 36 | 37 | if [ -d bash-tools ]; then 38 | # pushd not available in sh 39 | cd bash-tools 40 | git pull 41 | cd .. 42 | else 43 | git clone https://github.com/HariSekhon/DevOps-Bash-tools bash-tools 44 | fi 45 | 46 | bash-tools/install/install_kics.sh 47 | ''' 48 | ) 49 | sh ( 50 | label: "KICS Version", 51 | script: ''' 52 | set -eu 53 | export PATH="$PATH:$HOME/bin/kics":~/bin/kics 54 | kics version 55 | ''' 56 | ) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /vars/sshKnownHostsGitHub.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-28 16:10:36 +0000 (Fri, 28 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S S H K n o w n H o s t s - G i t H u b 18 | // ========================================================================== // 19 | 20 | // Loads GitHub.com SSH Known Hosts 21 | // 22 | // ssh-keyscan github.com 23 | 24 | def call () { 25 | // safer to use known hosts than to just blindly accept them dynamically like this: 26 | // 27 | // ssh -T -oStrictHostKeyChecking=accept-new git@github.com 28 | // 29 | sshKnownHosts(""" 30 | # github.com:22 SSH-2.0-babeld-e47cd09f 31 | github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== 32 | # github.com:22 SSH-2.0-babeld-e47cd09f 33 | github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= 34 | # github.com:22 SSH-2.0-babeld-e47cd09f 35 | github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl 36 | """, 'GitHub' 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: Tue Feb 4 09:53:28 2020 +0000 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https://github.com/HariSekhon/Jenkins 8 | # 9 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 10 | # 11 | # https://www.linkedin.com/in/HariSekhon 12 | # 13 | 14 | # ============================================================================ # 15 | # S e m g r e p G i t H u b W o r k f l o w 16 | # ============================================================================ # 17 | 18 | # Generates code scanning alerts in GitHub's Security tab -> Code scanning alerts 19 | 20 | # https://semgrep.dev/docs/semgrep-ci/sample-ci-configs/#github-actions 21 | 22 | --- 23 | name: Semgrep 24 | 25 | on: # yamllint disable-line rule:truthy 26 | push: 27 | branches: 28 | - master 29 | - main 30 | paths-ignore: 31 | - '**/*.md' 32 | pull_request: 33 | branches: 34 | - master 35 | - main 36 | paths-ignore: 37 | - '**/*.md' 38 | workflow_dispatch: 39 | inputs: 40 | debug: 41 | type: boolean 42 | required: false 43 | default: false 44 | schedule: 45 | - cron: '0 0 * * 1' 46 | 47 | permissions: 48 | actions: read 49 | contents: read 50 | security-events: write 51 | 52 | concurrency: 53 | group: ${{ github.workflow }}-${{ github.ref }} 54 | cancel-in-progress: true 55 | 56 | jobs: 57 | semgrep: 58 | # github.event.repository context not available in scheduled workflows 59 | #if: github.event.repository.fork == false 60 | if: github.repository_owner == 'HariSekhon' 61 | name: Semgrep GitHub Security Tab 62 | uses: HariSekhon/GitHub-Actions/.github/workflows/semgrep.yaml@master 63 | with: 64 | debug: ${{ github.event.inputs.debug }} 65 | -------------------------------------------------------------------------------- /vars/gitBranchList.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-31 01:12:44 +0100 (Fri, 31 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t B r a n c h L i s t 18 | // ========================================================================== // 19 | 20 | // Returns a list of strings of all git branches in the current git repo checkout 21 | // 22 | // By default returns only remote branches as this is usually what you want 23 | // 24 | // Assumes it's executing from inside a git cloned checkout 25 | // 26 | // Pass in a first boolean arg of 'true' to return local branches as well as remotes 27 | // 28 | // Requires 'git' to be in the $PATH 29 | 30 | def call (all=false) { 31 | String opt 32 | if (all) { 33 | opt = '-a' 34 | } else { 35 | opt = '-r' 36 | } 37 | branches = sh ( 38 | label: 'Git Branches', 39 | returnStdout: true, 40 | script: """ 41 | set -eux 42 | git branch --list $opt 43 | """ 44 | ) 45 | if ( ! branches ) { 46 | error('No branches returns from git command in function gitBranchList()') 47 | } 48 | branchList = branches. 49 | split(). 50 | collect { 51 | it.split('/')[-1] 52 | }.findAll( 53 | { 54 | it != 'HEAD' && 55 | it != '->' 56 | } 57 | ).unique() 58 | return branchList 59 | } 60 | -------------------------------------------------------------------------------- /vars/jenkinsJobJenkinsfile.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-06-08 23:36:19 +0100 (Thu, 08 Jun 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // J e n k i n s J o b J e n k i n s f i l e 18 | // ========================================================================== // 19 | 20 | // Returns the Jenkinsfile path for a given Jenkins job from its XML config as returned by jenkinsJobConfigXml(jobName) 21 | 22 | def call(jobXml) { 23 | 24 | if ( ! jobXml ) { 25 | error('no job xml passed to function jenkinsJobJenkinsfile()') 26 | } 27 | 28 | // https://groovy-lang.org/processing-xml.html 29 | 30 | //def xmlroot = new XmlSlurper().parseText(jobXml) 31 | // gets java.lang.NullPointerException 32 | //assert xmlroot instanceof groovy.xml.slurpersupport.GPathResult 33 | 34 | //def xmlroot = new XmlParser().parseText(jobXml) 35 | 36 | //assert xmlroot instanceof groovy.util.Node 37 | 38 | // works in groovysh but not in Jenkins: 39 | // 40 | // org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such field found: field groovy.util.Node ** 41 | // 42 | //repo = xmlroot.'**'.find { it.name() == 'scriptPath' }.value()[0].trim() 43 | 44 | // quick and dirty 45 | repo = jobXml. 46 | split('\n'). 47 | find { it.contains('') }. 48 | replace('', ''). 49 | replace('', ''). 50 | trim() 51 | 52 | return repo 53 | } 54 | -------------------------------------------------------------------------------- /vars/jenkinsJobBranch.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-06-09 04:30:50 +0100 (Fri, 09 Jun 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // J e n k i n s J o b B r a n c h 18 | // ========================================================================== // 19 | 20 | // Returns the branch for a given Jenkins job from its XML config as returned by jenkinsJobConfigXml(jobName) 21 | 22 | def call(jobXml) { 23 | 24 | if ( ! jobXml ) { 25 | error('no job xml passed to function jenkinsJobJenkinsfile()') 26 | } 27 | 28 | // https://groovy-lang.org/processing-xml.html 29 | 30 | //def xmlroot = new XmlSlurper().parseText(jobXml) 31 | // gets java.lang.NullPointerException 32 | //assert xmlroot instanceof groovy.xml.slurpersupport.GPathResult 33 | 34 | //def xmlroot = new XmlParser().parseText(jobXml) 35 | 36 | //assert xmlroot instanceof groovy.util.Node 37 | 38 | // works in groovysh but not in Jenkins: 39 | // 40 | // org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such field found: field groovy.util.Node ** 41 | // 42 | //branch = xmlroot.'**'.find { it.name() == 'name' }.value()[0].split('/')[-1].trim() 43 | 44 | // quick and dirty 45 | repo = jobXml. 46 | split('\n'). 47 | find { it.contains('') }. 48 | replace('', ''). 49 | replace('', ''). 50 | split('/')[-1]. 51 | trim() 52 | 53 | return repo 54 | } 55 | -------------------------------------------------------------------------------- /vars/gitMergePipeline.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t M e r g e B r a n c h e P i p e l i n e 18 | // ========================================================================== // 19 | 20 | // Usage in Jenkinsfile: 21 | // 22 | // @Library('github.com/harisekhon/jenkins@master') _ 23 | // gitMergePipeline('staging', 'dev') 24 | // 25 | 26 | def call (fromBranch, toBranch) { 27 | 28 | pipeline { 29 | 30 | agent any 31 | 32 | options { 33 | disableConcurrentBuilds() 34 | } 35 | 36 | // backup to catch GitHub -> Jenkins webhook failures 37 | triggers { 38 | pollSCM('H/10 * * * *') 39 | } 40 | 41 | stages { 42 | 43 | stage('Environment') { 44 | steps { 45 | printEnv() 46 | } 47 | } 48 | 49 | stage('Git Merge') { 50 | steps { 51 | gitMerge("$fromBranch", "$toBranch") 52 | } 53 | } 54 | 55 | // git push needs to be done in the same step gitMerge to benefit properly from the locking 56 | //stage('Git Push') { 57 | // steps { 58 | // sh ( 59 | // label: 'Git Push', 60 | // script: 'git push origin --all' 61 | // ) 62 | // } 63 | //} 64 | } 65 | 66 | post { 67 | failure { 68 | Notify() 69 | } 70 | fixed { 71 | Notify() 72 | } 73 | } 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /vars/installPackages.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-27 13:23:20 +0100 (Mon, 27 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // I n s t a l l O S P a c k a g e s 18 | // ========================================================================== // 19 | 20 | // Adapted from the more advanced DevOps Bash tools repo's install_packages.sh and supporting scripts 21 | 22 | def call (packages=[]) { 23 | String label = "Install Packages on agent '$HOSTNAME'" 24 | echo "Acquiring Lock: $label" 25 | lock (resource: "$label") { 26 | timeout (time: 5, unit: 'MINUTES') { 27 | withEnv(["PACKAGES=${packages.join(' ')}"]) { 28 | sh ''' 29 | set -eux 30 | 31 | export DEBIAN_FRONTEND=noninteractive 32 | 33 | sudo="" 34 | # adapted from DevOps Bash tools lib/utils.sh am_root() function 35 | if ! [ "${EUID:-${UID:-$(id -u)}}" -eq 0 ]; then 36 | sudo=sudo 37 | fi 38 | 39 | if command -v apt-get >/dev/null; then 40 | $sudo apt-get update 41 | $sudo apt-get install -y $PACKAGES 42 | elif command -v apk >/dev/null; then 43 | $sudo apk update 44 | $sudo apk add $PACKAGES 45 | elif command -v yum >/dev/null; then 46 | $sudo yum install -y $PACKAGES 47 | else 48 | echo "ERROR: No recognized package manager found to install packages with" 49 | exit 1 50 | fi 51 | ''' 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vars/downloadJenkinsCLI.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-30 18:03:25 +0100 (Thu, 30 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Download Jenkins CLI from current Jenkins Master 18 | // ========================================================================== // 19 | 20 | // Downloads Terraform binary to $HOME/bin if run as a user, or /usr/local/bin if run as root 21 | 22 | // Adapted from DevOps Bash Tools jenkins_cli.sh, install_binary.sh, install_packages.sh and lib/utils.sh 23 | 24 | def call () { 25 | String label = "Download Jenkins CLI on agent '$HOSTNAME'" 26 | echo "Acquiring Lock: $label" 27 | lock (resource: "$label") { 28 | timeout (time: 5, unit: 'MINUTES') { 29 | // TODO: add destination arg to follow ${JENKINS_CLI_JAR:-$HOME/bin/jenkins-cli.jar} 30 | installBinary(url: "$JENKINS_URL/jnlpJars/jenkins-cli.jar") 31 | sh ( 32 | label: "Jenkins CLI Version", 33 | // clear JENKINS_URL and JENKINS_USER_ID to avoid this error: 34 | // "The JENKINS_USER_ID and JENKINS_API_TOKEN env vars should be both set or left empty." 35 | script: ''' 36 | set -eu 37 | #unset JENKINS_URL JENKINS_SERVER_COOKIE JENKINS_USER_ID JENKINS_NODE_COOKIE 38 | # need to keep JENKINS_CLI_ARGS="-webSocket" to prevent hang and fail 39 | unset $(env | grep '^JENKINS_' | grep -v '^JENKINS_CLI_ARGS=' | sed 's/=.*//') 40 | java -jar ~/bin/jenkins-cli.jar ${JENKINS_CLI_ARGS:-} version || : 41 | ''' 42 | ) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vars/gcrTagGitCommitShort.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-09 16:07:58 +0000 (Thu, 09 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G C R T a g G i t S h o r t C o m m i t 18 | // ========================================================================== // 19 | 20 | // Tags the current GIT_COMMIT tagged Docker image in Google Container Registry with the Git Short Commit which is a bit more user friendly when using not explicitly versioned CI/CD deployments 21 | // 22 | // Call with a string split like for an env var: 23 | // 24 | // gcrTagGitCommitShort(env.DOCKER_IMAGES.split(',') as List) 25 | // 26 | // Requires GCloud SDK CLI to be installed and authenticated 27 | 28 | def call (List dockerImageRegistryPaths=[]) { 29 | if ( ! env.GIT_COMMIT_SHORT ) { 30 | gitCommitShort() 31 | } 32 | dockerImageRegistryPaths = dockerImageRegistryPaths ?: dockerInferImageList() 33 | if (dockerImageRegistryPaths instanceof List == false) { 34 | error "non-list passed as arg to gcrTagGitCommitShort()" 35 | } 36 | for (String dockerImageRegistryPath in dockerImageRegistryPaths) { 37 | if (gcrDockerImageExists(dockerImageRegistryPath, env.GIT_COMMIT_SHORT)) { 38 | continue 39 | } 40 | if (!gcrDockerImageExists(dockerImageRegistryPath, env.GIT_COMMIT)) { 41 | error "Docker image '$dockerImageRegistryPath' full SHA '$GIT_COMMIT' tag does not exist, cannot tag with short SHA!" 42 | } 43 | gcrTagDockerImage("$dockerImageRegistryPath:$GIT_COMMIT", env.GIT_COMMIT_SHORT) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vars/gitCommitShort.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-02-22 14:53:55 +0000 (Wed, 22 Feb 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Generate GIT_COMMIT_SHORT 18 | // ========================================================================== // 19 | 20 | // Generates the GIT_COMMIT_SHORT environment variable from GIT_COMMIT and returns it 21 | // 22 | // If GIT_COMMIT is not set, attempts to also create that first from the Git Log 23 | // 24 | // Useful for auto-versioning dev builds to deploy automatically without generating too many release versions via githubCreateRelease.groovy for every git push 25 | // 26 | // https://www.jenkins.io/doc/pipeline/examples/#gitcommit 27 | 28 | def call () { 29 | if ( ! env.GIT_COMMIT ) { 30 | echo "GIT_COMMIT environment variable not found, attempting to populate from git log" 31 | env.GIT_COMMIT = sh ( 32 | returnStdout: true, 33 | script: "git log -n 1 --pretty=format:'%H'" 34 | ).trim() 35 | if ( ! env.GIT_COMMIT ) { 36 | error "Failed to determine GIT_COMMIT" 37 | } 38 | } 39 | int commitLength = env.GIT_COMMIT.length() 40 | if (commitLength != 40) { 41 | error "GIT_COMMIT environment variable is of unexpected length, expected 40 characters, got '$commitLength' characters in '$GIT_COMMIT'" 42 | } 43 | // avoid this call if GIT_COMMIT is already set 44 | //env.GIT_COMMIT_SHORT = sh (returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() 45 | env.GIT_COMMIT_SHORT = env.GIT_COMMIT.substring(0, 7) 46 | return env.GIT_COMMIT_SHORT 47 | } 48 | -------------------------------------------------------------------------------- /vars/terragruntApply.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a g r u n t A p p l y 18 | // ========================================================================== // 19 | 20 | def call (timeoutMinutes=30) { 21 | String terraformDir = env.TERRAFORM_DIR ?: '.' 22 | String unique = "Dir: $terraformDir" 23 | String label = "Terragrunt Apply - $unique" 24 | // must differentiate lock to share the same lock between Terraform Plan and Terraform Apply 25 | String lockString = "Terraform - $unique" 26 | echo "Acquiring Terragrunt Apply Lock: $lockString" 27 | lock (resource: lockString, inversePrecedence: true) { // use same lock between Terraform / Terragrunt for safety 28 | // forbids older applys from starting 29 | milestone(ordinal: null, label: "Milestone: $label") 30 | 31 | // terragrunt docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 32 | //container('terragrunt') { 33 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 34 | //dir ("components/${COMPONENT}") { 35 | ansiColor('xterm') { 36 | // for test environments, add a param to trigger -destroy switch 37 | dir("$terraformDir") { 38 | echo "$label" 39 | sh ( 40 | label: "$label", 41 | script: 'terragrunt apply plan.zip --terragrunt-non-interactive -input=false -auto-approve' 42 | ) 43 | } 44 | } 45 | } 46 | //} 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vars/gcrDockerImageExists.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-09 16:51:25 +0000 (Thu, 09 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G C R D o c k e r I m a g e E x i s t s 18 | // ========================================================================== // 19 | 20 | // Checks if the given Docker Image GCR registry path and commit already exists 21 | // 22 | // Requires GCloud SDK CLI to be installed and authenticated 23 | 24 | def call (String dockerImageRegistryPath, String dockerImageTag) { 25 | echo "Checking docker image '$dockerImageRegistryPath' tag '$dockerImageTag' exists" 26 | if (dockerImageRegistryPath.contains(':')) { 27 | dockerImageRegistryPath = dockerImageRegistryPath.split(':')[0] 28 | } 29 | String stdout = sh ( 30 | // GCloud SDK returns zero whether found or not, so check the stdout for content 31 | returnStdout: true, 32 | label: "GCloud list tags", 33 | script: """ 34 | set -eux 35 | gcloud container images list-tags '$dockerImageRegistryPath' --filter='tags ~ ^$dockerImageTag\$' --format=text 36 | """ 37 | // XXX: do not do --filter='tags:$dockerImageTag' - this will match a partial substring such that an image 38 | // with only a long hashref tag will be matched by a check for the short hashref, defeating adjacent gcrTagGitCommitShort.groovy 39 | ) 40 | if (stdout) { 41 | echo "Docker image '$dockerImageRegistryPath:$dockerImageTag' exists" 42 | return true 43 | } else { 44 | echo "Docker image '$dockerImageRegistryPath:$dockerImageTag' does not exist" 45 | return false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vars/cloudflarePurgeCache.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // C l o u d f l a r e P u r g e C a c h e 18 | // ========================================================================== // 19 | 20 | // Requires environment variables to be defined in environment{} section of Jenkinsfile: 21 | // 22 | // CLOUDFLARE_EMAIL 23 | // CLOUDFLARE_ZONE_ID 24 | // CLOUDFLARE_API_KEY // see master ../Jenkinsfile for how to load this from a Jenkins secret 25 | 26 | def call () { 27 | String label = "Cloudflare Purge Cache - '" + "${env.ENVIRONMENT}".capitalize() + "' Environment" 28 | echo "Acquiring Cloudflare Lock: $label" 29 | lock (resource: label, inversePrecedence: true) { 30 | milestone ordinal: null, label: "Milestone: $label" 31 | retry (2) { 32 | timeout (time: 1, unit: 'MINUTES') { 33 | echo "$label" 34 | sh ( 35 | label: "$label", 36 | script: '''#!/usr/bin/env bash 37 | set -euxo pipefail 38 | output="$( 39 | curl -sS -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache" \ 40 | -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \ 41 | -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \ 42 | -H "Content-Type: application/json" \ 43 | --data '{"purge_everything":true}' 44 | )" 45 | #echo "$output" 46 | grep -q '"success": true' <<< "$output" 47 | ''' 48 | ) 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vars/terragruntRefreshState.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-07 18:48:47 +0000 (Fri, 07 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a g r u n t R e f r e s h 18 | // ========================================================================== // 19 | 20 | // For large estates to run a separate refresh-only job periodically to keep the state file up to date 21 | // 22 | // $APP and $ENVIRONMENT must be set in pipeline to ensure separate locking 23 | 24 | def call (timeoutMinutes=59) { 25 | String terraformDir = env.TERRAFORM_DIR ?: '.' 26 | String unique = "Dir: $terraformDir" 27 | String label = "Terragrunt Refresh State - $unique" 28 | // must differentiate lock to share the same lock between Terraform Plan and Terraform Apply 29 | String lockString = "Terraform - $unique" 30 | lock (resource: lockString, inversePrecedence: true) { 31 | // forbids older runs from starting 32 | milestone(ordinal: null, label: "Milestone: $label") 33 | 34 | // terragrunt docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 35 | //container('terragrunt') { 36 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 37 | ansiColor('xterm') { 38 | dir(env.TERRAFORM_DIR ?: ".") { 39 | // for test environments, add a param to trigger -destroy switch 40 | echo "$label" 41 | sh ( 42 | label: "$label", 43 | script: 'terragrunt apply -refresh-only --terragrunt-non-interactive -input=false' 44 | ) 45 | } 46 | } 47 | } 48 | //} 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vars/downloadDockle.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-16 02:41:44 +0100 (Tue, 16 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d D o c k l e 18 | // ========================================================================== // 19 | 20 | // Downloading Kustomize only takes a couple seconds in testing 21 | 22 | // get release version from: 23 | // 24 | // https://github.com/goodwithtech/dockle/releases 25 | // 26 | 27 | def call (version='0.4.11') { 28 | String label = "Download Dockle on agent '$HOSTNAME'" 29 | // strip a 'v' prefix if present because we add it to the URL ourselves 30 | if (version[0] == 'v') { 31 | version = version.substring(1) 32 | } 33 | echo "Acquiring Lock: $label" 34 | lock (resource: "$label") { 35 | timeout (time: 2, unit: 'MINUTES') { 36 | withEnv(["VERSION=$version"]) { 37 | echo "$label" 38 | sh ( 39 | label: "$label, version '$version'", 40 | script: ''' 41 | set -eux 42 | 43 | echo "Downloading Dockle version $VERSION" 44 | 45 | curl -sSL -o "/tmp/dockle.$$.tgz" "https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz" 46 | 47 | tar zxvf "/tmp/dockle.$$.tgz" dockle -O > "/tmp/dockle.$$" 48 | chmod +x "/tmp/dockle.$$" 49 | 50 | mv -fv "/tmp/dockle.$$" /usr/local/bin/dockle 51 | ''' 52 | ) 53 | sh ( 54 | label: "Dockle Version", 55 | script: ''' 56 | set -eu 57 | dockle --version 58 | ''' 59 | ) 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vars/terraformRefreshState.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-07 18:48:47 +0000 (Fri, 07 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a f o r m R e f r e s h S t a t e s 18 | // ========================================================================== // 19 | 20 | // For large estates to run a separate refresh-only job periodically to keep the state file up to date 21 | // 22 | // $APP and $ENVIRONMENT must be set in pipeline to ensure separate locking 23 | 24 | def call (timeoutMinutes=59) { 25 | String terraformDir = env.TERRAFORM_DIR ?: '.' 26 | String unique = "Dir: $terraformDir" 27 | String label = "Terraform Refresh State - $unique" 28 | // must differentiate lock to share the same lock between Terraform Plan and Terraform Apply 29 | String lockString = "Terraform - $unique" 30 | echo "Acquiring Terraform Refresh Lock: $lockString" 31 | lock (resource: lockString, inversePrecedence: true) { 32 | // forbids older runs from starting 33 | milestone(ordinal: null, label: "Milestone: $label") 34 | 35 | // terraform docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 36 | //container('terraform') { 37 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 38 | //dir ("components/${COMPONENT}") { 39 | ansiColor('xterm') { 40 | // for test environments, add a param to trigger -destroy switch 41 | dir(env.TERRAFORM_DIR ?: ".") { 42 | echo "$label" 43 | sh ( 44 | label: "$label", 45 | script: 'terraform apply -refresh-only -input=false' 46 | ) 47 | } 48 | } 49 | } 50 | //} 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vars/gcrDockerImagesExistWait.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-03-31 00:24:09 +0100 (Fri, 31 Mar 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G C R D o c k e r I m a g e s E x i s t W a i t 18 | // ========================================================================== // 19 | 20 | // Waits until all the given GCR docker image:tag paths exist for the specifed length of time in minutes 21 | // 22 | // Useful if you've got CloudBuild triggering asynchronously and Jenkins is waiting for them before continuing with a deployment 23 | // 24 | // Requires GCloud SDK CLI to be installed and authenticated 25 | 26 | def call (List dockerImageRegistryPaths=[], String dockerTag='', int waitMinutes=10) { 27 | dockerImageRegistryPaths = dockerImageRegistryPaths ?: dockerInferImageTagList() 28 | timeout (time: waitMinutes, unit: 'MINUTES') { 29 | echo "Waiting for $waitMinutes minutes for GCR docker images to become available" 30 | waitUntil { 31 | for (String dockerImageRegistryPath in dockerImageRegistryPaths) { 32 | String tag = dockerTag 33 | if (dockerImageRegistryPath.contains(':')) { 34 | tag = dockerImageRegistryPath.split(':')[-1] 35 | } 36 | if (!tag) { 37 | error("gcrDockerImagesExistWait() passed docker image registry path without a tag suffix, which will usually be true after even 1 build: $dockerImageRegistryPath") 38 | } 39 | if (!gcrDockerImageExists(dockerImageRegistryPath, tag)) { 40 | sleep( 41 | time: 10, 42 | unit: 'SECONDS' // default is SECONDS 43 | ) 44 | return false 45 | } 46 | } 47 | return true 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vars/dockerLoginGCR.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:55:43 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to Google Container Registry 18 | // ========================================================================== // 19 | 20 | // pass GCP_SERVICE_ACCOUNT key and GCR_REGISTRY as the first and second args or it'll look for them in the environment 21 | 22 | // must be called after gcpActivateServiceAccount.groovy 23 | // must have GCloud SDK in the calling environment or will fall back to attempting a direct Docker login 24 | 25 | def call (key='', registry='') { 26 | key = key ?: env.GCP_SERVICEACCOUNT_KEY ?: error('dockerLoginGCR: key not specified and GCP_SERVICEACCOUNT_KEY not set in the environment') 27 | registry = registry ?: env.GCR_REGISTRY ?: error('dockerLoginGCR: registry not specified and GCR_REGISTRY not set in the environment') 28 | withEnv(["GCP_SERVICEACCOUNT_KEY=$key", "GCR_REGISTRY=$registry"]) { 29 | script { 30 | if (isCommandAvailable('gcloud')) { 31 | echo 'Using GCloud SDK to configure Docker' 32 | // configures docker config with a token 33 | sh 'gcloud auth configure-docker "$GCR_REGISTRY"' 34 | } else { 35 | echo 'GCloud SDK is not installed, attempting to login with docker directly' 36 | if (!env.GCR_REGISTRY) { 37 | error('GCR_REGISTRY environment variable not set!') 38 | } 39 | if (!env.GCP_SERVICEACCOUNT_KEY) { 40 | error('GCP_SERVICEACCOUNT_KEY environment variable not set!') 41 | } 42 | dockerLogin('_json_key', env.GCP_SERVICEACCOUNT_KEY.bytes.decodeBase64().toString(), env.GCR_REGISTRY) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /vars/downloadTerragrunt.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 13:12:02 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d T e r r a g r u n t 18 | // ========================================================================== // 19 | 20 | // Downloads Terragrunt binary to $HOME/bin 21 | 22 | // Won't overwrite an existing ~/bin/terragrunt unless the 'overwrite: true' arg is given 23 | 24 | // Designed for Kubernetes ephemeral agents rather than old style long running agents, in which case it'd need to be modified with a unique destination per version 25 | 26 | // Adapted from DevOps Bash Tools install/install_terragrunt.sh 27 | 28 | // get release version from: 29 | // 30 | // https://github.com/gruntwork-io/terragrunt/releases 31 | // 32 | 33 | def call (version) { 34 | String label = "Download Terragrunt on agent '$HOSTNAME'" 35 | if (!version) { 36 | error "version arg not given to downloadTerragrunt()" 37 | } 38 | // strip a 'v' prefix if present because we add it to the URL ourselves 39 | if (version[0] == 'v') { 40 | version = version.substring(1) 41 | } 42 | echo "Acquiring Lock: $label" 43 | lock (resource: "$label") { 44 | timeout (time: 5, unit: 'MINUTES') { 45 | withEnv(["VERSION=$version"]) { 46 | installBinary( 47 | binary: 'terragrunt', 48 | url: "https://github.com/gruntwork-io/terragrunt/releases/download/v$VERSION/terragrunt_{os}_{arch}" 49 | ) 50 | } 51 | sh ( 52 | label: "Terragrunt Version", 53 | script: ''' 54 | set -eu 55 | export PATH="$PATH:$HOME/bin":~/bin 56 | terragrunt --version 57 | ''' 58 | ) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /vars/gitSetup.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-28 16:10:36 +0000 (Fri, 28 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t S e t u p 18 | // ========================================================================== // 19 | 20 | // Sets up Git username and email for comitting, you may want to call one of the sshKnownHosts* functions first if using dynamic agents and Git over SSH 21 | // 22 | // Recommended to set an Environment variable of GIT_EMAIL=your-team@your-company.com - consider doing this at the global Jenkins level: 23 | // 24 | // Manage Jenkins -> Configure System -> Global properties -> Environment Variables -> Add -> GIT_EMAIL 25 | 26 | def call () { 27 | String label = "Setting up local Git repo for Jenkins" 28 | echo "$label" 29 | sh ( 30 | label: "$label", 31 | script: ''' 32 | set -eux 33 | 34 | GIT_USERNAME="${GIT_USERNAME:-${GIT_USER:-Jenkins}}" 35 | GIT_EMAIL="${GIT_EMAIL:-jenkins@noreply}" 36 | 37 | #if [ -z "${GIT_EMAIL:-}" ]; then 38 | # echo "GIT_EMAIL is not defined, please set this in Jenkinsfile environment{} section" 39 | # exit 1 40 | #fi 41 | 42 | # needed for 'git commit' 43 | git config user.name "$GIT_USERNAME" 44 | git config user.email "$GIT_EMAIL" 45 | 46 | if [ -n "${DEBUG:-}" ]; then 47 | ssh-add -l || : 48 | fi 49 | 50 | # use sshKnownHosts() functions instead to make the real tracked host keys available in K8s agents 51 | #export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" 52 | 53 | # better defined in Jenkinsfile environment{} section 54 | #export GIT_TRACE=1 55 | #export GIT_TRACE_SETUP=1 56 | ''' 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /vars/mapUserEmails.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // M a p U s e r E m a i l 18 | // ========================================================================== // 19 | 20 | // Processes a List of ['Username '] 21 | // to a Map of [username:email] 22 | // 23 | // see gitLogBrokenCommitters.groovy for example usage 24 | 25 | // Limitation: usernames that have used more than one email addresses will overwrite with the last email in the list 26 | 27 | // XXX: matcher is non-serializable and will result in this exception in Jenkins due to it wanting to be able to save state to disk for durability/resume: 28 | // 29 | // Caused: java.io.NotSerializableException: java.util.regex.Matcher 30 | // 31 | // the @NonCPS annotation tells Jenkins not to try to save the local variables of this function 32 | // XXX: side effect, seems to generate 'at Unknown.Unknown(Unknown)' for any issue in Java stack traces in the calling function eg. slackBrokenCommitters() 33 | 34 | // https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/ 35 | @NonCPS 36 | def call (List userEmailList) { 37 | // returns a Map in ['user': 'email'] format 38 | Map userEmails = [:] 39 | // matcher needs local def, otherwise @NonCPS annotation doesn't exclude it 40 | def matcher 41 | userEmailList.each { 42 | if ((matcher = it =~ /^(.+)<(.+)>$/)) { 43 | username = matcher.group(1).trim() 44 | email = matcher.group(2).trim() 45 | username = username ?: email 46 | userEmails.put(username, email) 47 | } else { 48 | if (it) { 49 | echo("WARNING: failed to parse username: $it") 50 | } 51 | } 52 | } 53 | return userEmails 54 | } 55 | -------------------------------------------------------------------------------- /vars/dockerLoginGAR.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 10:55:43 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Docker Login to Google Artifact Registry 18 | // ========================================================================== // 19 | 20 | // pass GCP_SERVICE_ACCOUNT key and GAR_REGISTRY as the first and second args or it'll look for them in the environment 21 | 22 | // must be called after gcpActivateServiceAccount.groovy 23 | // must have GCloud SDK in the calling environment or will fall back to attempting a direct Docker login 24 | // 25 | // GAR registries list can be obtained via: 26 | // 27 | // gcloud artifacts locations list 28 | 29 | def call (key='', registry='') { 30 | key = key ?: env.GCP_SERVICEACCOUNT_KEY ?: error('dockerLoginGAR: key not specified and GCP_SERVICEACCOUNT_KEY not set in the environment') 31 | registry = registry ?: env.GAR_REGISTRY ?: error('dockerLoginGAR: registry not specified and GAR_REGISTRY not set in the environment') 32 | withEnv(["GCP_SERVICEACCOUNT_KEY=$key", "GAR_REGISTRY=$registry"]) { 33 | script { 34 | if (isCommandAvailable('gcloud')) { 35 | echo 'Using GCloud SDK to configure Docker' 36 | // configures docker config with a token 37 | sh 'gcloud auth configure-docker "$GAR_REGISTRY"' 38 | } else { 39 | echo 'GCloud SDK is not installed, attempting to login with docker directly' 40 | if (!env.GAR_REGISTRY) { 41 | error('GAR_REGISTRY environment variable not set!') 42 | } 43 | if (!env.GCP_SERVICEACCOUNT_KEY) { 44 | error('GCP_SERVICEACCOUNT_KEY environment variable not set!') 45 | } 46 | dockerLogin('_json_key', env.GCP_SERVICEACCOUNT_KEY.bytes.decodeBase64().toString(), env.GAR_REGISTRY) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vars/downloadTerraform.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-21 13:12:02 +0100 (Tue, 21 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d T e r r a f o r m 18 | // ========================================================================== // 19 | 20 | // Downloads Terraform binary to $HOME/bin 21 | 22 | // Won't overwrite an existing ~/bin/terraform unless the 'overwrite: true' arg is given 23 | 24 | // Designed for Kubernetes ephemeral agents rather than old style long running agents, in which case it'd need to be modified with a unique destination per version, although I'd recommend using tfenv instead in that case 25 | 26 | // Adapted from DevOps Bash Tools install/install_terraform.sh, install_binary.sh, install_packages.sh and lib/utils.sh 27 | 28 | // you may need to call this first to ensure the prerequisite commands curl and unzip are available: 29 | // 30 | // installPackages(['curl', 'unzip']) 31 | 32 | // get release version from: 33 | // 34 | // https://github.com/hashicorp/terraform/releases 35 | // 36 | 37 | def call (version) { 38 | String label = "Download Terraform on agent '$HOSTNAME'" 39 | if (!version) { 40 | error "version arg not given to downloadTerraform()" 41 | } 42 | echo "Acquiring Lock: $label" 43 | lock (resource: "$label") { 44 | timeout (time: 5, unit: 'MINUTES') { 45 | withEnv(["VERSION=$version"]) { 46 | installBinary( 47 | binary: 'terraform', 48 | url: "https://releases.hashicorp.com/terraform/$VERSION/terraform_${VERSION}_{os}_{arch}.zip" 49 | ) 50 | } 51 | sh ( 52 | label: "Terraform Version", 53 | script: ''' 54 | set -eu 55 | export PATH="$PATH:$HOME/bin":~/bin 56 | terraform version 57 | ''' 58 | ) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # vim:ts=4:sts=4:sw=4:et 2 | # 3 | # Author: Hari Sekhon 4 | # Date: 2015-10-31 19:04:34 +0000 (Sat, 31 Oct 2015) 5 | # 6 | # https://github.com/HariSekhon/Jenkins 7 | # 8 | # License: see accompanying Hari Sekhon LICENSE file 9 | # 10 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback 11 | # to help improve or steer this or other code I publish 12 | # 13 | # https://www.linkedin.com/in/HariSekhon 14 | # 15 | 16 | # http://EditorConfig.org 17 | 18 | # stop recursing upwards for other .editorconfig files 19 | root = true 20 | 21 | # Unix-style newlines with a newline ending every file 22 | [*] 23 | indent_size = 4 24 | indent_style = space 25 | end_of_line = lf 26 | trim_trailing_whitespace = true 27 | insert_final_newline = true 28 | 29 | [*.go] 30 | indent_size = 4 31 | indent_style = tab 32 | end_of_line = lf 33 | trim_trailing_whitespace = true 34 | insert_final_newline = true 35 | 36 | [Makefile] 37 | indent_size = 4 38 | indent_style = tab 39 | end_of_line = lf 40 | trim_trailing_whitespace = true 41 | insert_final_newline = true 42 | 43 | [{*.md,*.hcl,*.tf,*.tfvars}] 44 | indent_size = 2 45 | indent_style = space 46 | end_of_line = lf 47 | trim_trailing_whitespace = true 48 | insert_final_newline = true 49 | 50 | [*.yml,*.yaml] 51 | indent_size = 2 52 | indent_style = space 53 | end_of_line = lf 54 | trim_trailing_whitespace = true 55 | insert_final_newline = true 56 | 57 | [.*] 58 | indent_size = 4 59 | indent_style = space 60 | end_of_line = lf 61 | trim_trailing_whitespace = true 62 | insert_final_newline = true 63 | 64 | # ============================================================================ # 65 | # Older Stuff, don't think I use this any more 66 | # ============================================================================ # 67 | 68 | # Matches multiple files with brace expansion notation 69 | # Set default charset 70 | #[*.{js,py}] 71 | #charset = utf-8 72 | 73 | # Indentation override for all JS under lib directory 74 | #[lib/**.js] 75 | #indent_style = space 76 | #indent_size = 2 77 | 78 | # Matches the exact files either package.json or .travis.yml 79 | #[{package.json,.travis.yml}] 80 | #indent_style = space 81 | #indent_size = 2 82 | 83 | #[*.xml] 84 | #indent_style = space 85 | #indent_size = 2 86 | -------------------------------------------------------------------------------- /vars/gitLogBrokenCommitters.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Find Git Log Committers to Notify they Broke the Build 18 | // ========================================================================== // 19 | 20 | // abstracted from previous pipelines gitMergePipeline, terraformPipe, jenkinsBackupJobConfigsPipeline 21 | 22 | // returns Map of committers since last successful build, in format ['username': 'email'] 23 | // 24 | // Limitation: users that have just changed email addresses will only use the lexiographically later email address 25 | 26 | // Example Usage: 27 | // 28 | // failure { 29 | // script { 30 | // Map committers = gitLogBrokenCommitters() 31 | // // send notification to these users 32 | // // see slackBrokenCommitters.groovy and slackNotify.groovy for examples 33 | // } 34 | // } 35 | 36 | def call () { 37 | // gets a List in ['username'] format 38 | List logCommittersList = sh ( 39 | label: 'Get Git Log Committers Since Last Successful Build', 40 | returnStdout: true, 41 | script: ''' 42 | set -eux 43 | if [ -z "${GIT_PREVIOUS_SUCCESSFUL_COMMIT:-}" ]; then 44 | exit 0 45 | fi 46 | git log --format='%an <%ae>' "${GIT_PREVIOUS_SUCCESSFUL_COMMIT}..${GIT_COMMIT}" | 47 | grep -Fv -e Jenkins \ 48 | -e '[bot]' | 49 | sort -fu 50 | ''' 51 | ).trim().split('\n').collect { it.trim() } 52 | //echo "Inferred Git committers List since last successful build via git log to be: $logCommittersList" 53 | // gets a Map in ['user': 'email'] format 54 | Map logCommitters = mapUserEmails(logCommittersList) 55 | echo "Inferred Git committers since last successful build via git log to be: $logCommitters" 56 | return logCommitters 57 | } 58 | -------------------------------------------------------------------------------- /vars/jenkinsJobRepo.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-06-08 23:36:19 +0100 (Thu, 08 Jun 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // J e n k i n s J o b R e p o 18 | // ========================================================================== // 19 | 20 | // Returns the SCM repo url for a given Jenkins job from its XML config as returned by jenkinsJobConfigXml(jobName) 21 | 22 | // You will get these errors the first couple times and must go to $JENKINS_URL/scriptApproval/ to allow XML parsing each time: 23 | // 24 | // Scripts not permitted to use new groovy.util.XmlParser. Administrators can decide whether to approve or reject this signature. 25 | // 26 | // org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use method groovy.util.XmlParser parseText java.lang.String 27 | 28 | def call(jobXml) { 29 | 30 | if ( ! jobXml ) { 31 | error('no job xml passed to function jenkinsJobJenkinsfile()') 32 | } 33 | 34 | // https://groovy-lang.org/processing-xml.html 35 | 36 | //def xmlroot = new XmlSlurper().parseText(jobXml) 37 | // gets java.lang.NullPointerException 38 | //assert xmlroot instanceof groovy.xml.slurpersupport.GPathResult 39 | 40 | //def xmlroot = new XmlParser().parseText(jobXml) 41 | 42 | //assert xmlroot instanceof groovy.util.Node 43 | 44 | // works in groovysh but not in Jenkins: 45 | // 46 | // org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such field found: field groovy.util.Node ** 47 | // 48 | //repo = xmlroot.'**'.find { it.name() == 'url' }.value()[0].trim() 49 | 50 | // quick and dirty 51 | repo = jobXml. 52 | split('\n'). 53 | find { it.contains('') }. 54 | replace('', ''). 55 | replace('', ''). 56 | trim() 57 | 58 | return repo 59 | } 60 | -------------------------------------------------------------------------------- /vars/downloadSonarScanner.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-17 00:56:30 +0100 (Wed, 17 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d S o n a r S c a n n e r 18 | // ========================================================================== // 19 | 20 | // CLI Scanner for SonarQube 21 | // 22 | // Takes 2 seconds to download Sonar Scanner 23 | // 24 | // Takes 13 seconds to download unzip due to apt package cache updates 25 | // 26 | // XXX: superceded by the Jenkins SonarQube Scanner plugin 27 | // 28 | // https://plugins.jenkins.io/sonar/ 29 | 30 | def call (version='4.8.0.2856') { 31 | String label = "Download Sonar Scanner on agent '$HOSTNAME'" 32 | echo "Acquiring Lock: $label" 33 | lock (resource: "$label") { 34 | timeout (time: 3, unit: 'MINUTES') { 35 | withEnv(["VERSION=$version"]) { 36 | installPackages(['unzip']) 37 | echo "$label" 38 | sh ( 39 | label: "$label, version '$version'", 40 | script: ''' 41 | set -eux 42 | 43 | echo "Downloading Sonar Scanner version $VERSION" 44 | 45 | curl -sSL -o "/tmp/sonar-scanner.$$.zip" "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$VERSION-linux.zip" 46 | 47 | cd /usr/local/ 48 | 49 | unzip "/tmp/sonar-scanner.$$.zip" 50 | 51 | #latest_sonar_dir="$(ls -td sonar-scanner-* | head -n1)" 52 | 53 | ln -sfvT "sonar-scanner-$VERSION-linux" sonar-scanner 54 | ''' 55 | ) 56 | echo 'Adding sonar-scanner bin to $PATH' 57 | env.PATH += ':/usr/local/sonar-scanner/bin' 58 | sh ( 59 | label: "Sonar Scanner Version", 60 | script: ''' 61 | set -eu 62 | sonar-scanner --version 63 | ''' 64 | ) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vars/downloadGitHubReleaseBinary.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 05:24:55 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d G i t H u b R e l e a s e B i n a r y 18 | // ========================================================================== // 19 | 20 | // Downloading it for each run trades inbound bandwidth (free) for not using RAM for bigger Jenkins pods causing more scale and billable Kubernetes 21 | // 22 | // Downloading only takes 2 seconds in testing 23 | // 24 | // The alternative is using the docker image which will be cached but hold RAM for the entire duration of the pipeline, which is very RAM inefficient: 25 | // 26 | // https://github.com/HariSekhon/Kubernetes-configs/blob/master/jenkins/base/jenkins-agent-pod.yaml 27 | 28 | def call (repo, binary, version='latest') { 29 | if ( ! repo ) { 30 | error("no github 'owner/repo' passed for first arg to downloadGitHubReleaseBinary()") 31 | } 32 | if ( ! binary ) { 33 | error('no binary passed for second arg to downloadGitHubReleaseBinary()') 34 | } 35 | // ! version instanceof String does not work and 36 | // version !instanceof String is only available in Groovy 3 37 | if (version instanceof String == false) { 38 | error "non-string version passed to downloadGitHubReleaseBinary() function" 39 | } 40 | if (version.contains("'")) { 41 | error "invalid version given to downloadGitHubReleaseBinary(): $version" 42 | } 43 | String label = "Download '$binary' on agent '$HOSTNAME'" 44 | echo "Acquiring Lock: $label" 45 | lock (resource: "$label") { 46 | timeout (time: 3, unit: 'MINUTES') { 47 | echo "$label" 48 | if (version == 'latest') { 49 | installBinary(url: "https://github.com/$repo/releases/latest/download/$binary-{os}-{arch}") 50 | } else { 51 | installBinary(url: "https://github.com/$repo/releases/download/$version/$binary-{os}-{arch}") 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vars/sonarScanner.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:19:11 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // S o n a r S c a n n e r 18 | // ========================================================================== // 19 | 20 | // https://docs.sonarqube.org/latest/analyzing-source-code/scanners/jenkins-extension-sonarqube/ 21 | // 22 | // Requires Jenkins Sonar plugin: 23 | // 24 | // https://plugins.jenkins.io/sonar/ 25 | 26 | // CLI Scanner for SonarQube 27 | // 28 | // Repo should have a 'sonar-project.properties' file at its root for the project specific settings 29 | // 30 | // Jenkins should have its Global Config for the SonarQube section set at: 31 | // 32 | // $JENKINS_URL/configure 33 | // 34 | // to provide pipeline with: 35 | // 36 | // SONAR_TOKEN credential - https://sonar.domain.com/account/security 37 | // 38 | // SONAR_HOST_URL = https://sonar.domain.com (via Kubernetes ingress, see https://github.com/HariSekhon/Kubernetes-configs 39 | // 40 | 41 | // 'config' is the server config instance configured at $JENKINS_URL/configure "SonarQube servers" section 42 | // 43 | // 'toolName' is the global tool name configured with version at $JENKINS_URL/configureTools/ "SonarQube Scanner" section 44 | // 45 | def call (config='SonarQube Server on Kubernetes', toolName='SonarQube Scanner', timeoutMinutes=30) { 46 | label 'Sonar Scanner' 47 | // let caller decide if wrapping this in a container('grype') or using downloadGrype.groovy to save RAM 48 | //container('sonar-scanner') { 49 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 50 | ansiColor('xterm') { 51 | withSonarQubeEnv(config) { 52 | def scannerHome = tool toolName 53 | echo "Sonar Scanner using SonarQube server at '$SONAR_HOST_URL'" 54 | sh ( 55 | label: "Sonar Scanner", 56 | script: "${scannerHome}/bin/sonar-scanner" 57 | ) 58 | } 59 | } 60 | } 61 | //} 62 | } 63 | -------------------------------------------------------------------------------- /vars/downloadCodeQL.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-07-26 17:59:59 +0100 (Tue, 26 Jul 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d G i t H u b C o d e Q L 18 | // ========================================================================== // 19 | 20 | // adapted from DevOps Bash tools script install/install_github_codeql.sh 21 | 22 | def call () { 23 | String label = "Download CodeQL on agent '$HOSTNAME'" 24 | echo "Acquiring Lock: $label" 25 | lock (resource: "$label") { 26 | sh ( 27 | label: "$label", 28 | script: ''' 29 | set -eux 30 | 31 | if [ -d ~/bin/codeql ]; then 32 | echo "CodeQL is already installed" 33 | exit 0 34 | fi 35 | 36 | os="$(uname -s | tr '[:upper:]' '[:lower:]')" 37 | 38 | if [ "$os" = darwin ]; then 39 | os=osx 40 | fi 41 | 42 | # 64 - there are no i386 or other arch available 43 | tarball="codeql-bundle-${os}64.tar.gz" 44 | 45 | #tmp="$(mktemp -d)" 46 | # trap signals copied from DevOps Bash tools lib/utils.sh 47 | #trap 'rm -fr "$tmp"' INT QUIT TRAP ABRT TERM EXIT 48 | 49 | # better for caching partial downloads 50 | tmp=/tmp 51 | 52 | cd "$tmp" 53 | 54 | curl -sSLf -o "$tarball" "https://github.com/github/codeql-action/releases/latest/download/$tarball" 55 | echo 56 | 57 | rm -fr -- ./codeql 58 | # the -- breaks the tar command which attempts to take it literally on GCloud SDK container 59 | #tar xvzf -- ./"$tarball" 60 | tar xvzf ./"$tarball" 61 | echo 62 | 63 | unalias rm >/dev/null 2>/dev/null || : 64 | unalias mv >/dev/null 2>/dev/null || : 65 | 66 | mv -fv -- codeql/ ~/bin/ 67 | rm -fv -- "$tarball" 68 | ''' 69 | ) 70 | sh ( 71 | label: "CodeQL Version", 72 | script: ''' 73 | set -eu 74 | ~/bin/codeql/codeql version 75 | ''' 76 | ) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /vars/gitMerge.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t M e r g e B r a n c h e s 18 | // ========================================================================== // 19 | 20 | // XXX: define 'github-ssh-key' credential (SSH private key) in Jenkins -> Manage Jenkins -> Credentials as SSH username with private key 21 | // 22 | // See Also: 23 | // 24 | // jenkins_cred_set_ssh_key.sh 25 | // or 26 | // jenkins_cred_cli_set_ssh_key.sh 27 | // 28 | // scripts in DevOps Bash tools repo which can createthis quickly from a local private key using API or CLI 29 | // 30 | // https://github.com/HariSekhon/DevOps-Bash-tools/ 31 | 32 | def call (fromBranch, toBranch, credential = 'github-ssh-key') { 33 | String label = "Git Merge from branch '$fromBranch' to branch '$toBranch'" 34 | echo "Acquiring Git Merge Lock: $label" 35 | lock (resource: label, inversePrecedence: true) { 36 | milestone ordinal: null, label: "Milestone: $label" 37 | timeout (time: 5, unit: 'MINUTES') { 38 | sshagent (credentials: [credential]) { 39 | retry (2) { 40 | withEnv(["FROM_BRANCH=$fromBranch", "TO_BRANCH=$toBranch"]) { 41 | gitSetup() 42 | echo "$label" 43 | sh ( 44 | label: "$label", 45 | script: ''' 46 | set -eux 47 | 48 | git status 49 | 50 | git fetch --all 51 | 52 | git checkout "$TO_BRANCH" --force 53 | git pull --no-edit --no-rebase 54 | git merge "origin/$FROM_BRANCH" --no-edit 55 | 56 | # XXX: push is done here and not a separate stage (which would be nicer visually in a Blue Ocean pipeline) 57 | # because we need the lock to encompass the entire operation for safety 58 | git push 59 | ''' 60 | ) 61 | } 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vars/printAuth.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-06-20 16:59:19 +0100 (Mon, 20 Jun 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // P r i n t A u t h S t a t u s 18 | // ========================================================================== // 19 | 20 | // Shows the current auth status, who you're logged in as for the major Cloud platforms 21 | // 22 | // Usage in Jenkinsfile: 23 | // 24 | // // import this library directly from github: 25 | // 26 | // @Library('github.com/harisekhon/jenkins@master') _ 27 | // 28 | // // run to login to any platforms for which we have standard expected environment variables available 29 | // 30 | // printAuth() 31 | 32 | def call () { 33 | timeout (time: 2, unit: 'MINUTES') { 34 | echo 'Showing logged in status for any platforms we have CLIs available for' 35 | 36 | sh ( 37 | label: 'Auth Status', 38 | script: ''' 39 | set -eu 40 | 41 | whoami 42 | echo 43 | 44 | if command -v aws >/dev/null 2>&1; then 45 | echo "AWS:" 46 | aws sts get-caller-identity || : 47 | echo 48 | fi 49 | 50 | if command -v gcloud >/dev/null 2>&1; then 51 | echo "GCP:" 52 | gcloud auth list || : 53 | echo 54 | fi 55 | 56 | if command -v az >/dev/null 2>&1; then 57 | echo "Azure:" 58 | az ad signed-in-user show || : 59 | echo 60 | fi 61 | 62 | if command -v gh >/dev/null 2>&1; then 63 | gh auth status || : 64 | #echo # above command prints an extra newline anyway 65 | fi 66 | 67 | if command -v docker >/dev/null 2>&1; then 68 | if [ -f ~/.docker/config.json ]; then 69 | if command -v jq >/dev/null 2>&1; then 70 | echo "Docker registries logged in to:" 71 | echo 72 | jq -r '.auths | keys[]' ~/.docker/config.json 73 | echo 74 | fi 75 | fi 76 | fi 77 | 78 | ''' 79 | ) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vars/garDockerAuth.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 17:58:49 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Configure Docker to Authenticate to Google Artifact Registry 18 | // ========================================================================== // 19 | 20 | // Requires: 21 | // 22 | // - gcpActivateServiceAccount.groovy to be called first to authenticate GCloud SDK 23 | // - needs GCloud SDK to be installed on the agent - if on Kubernetes make it the default container or else wrap this call in container('gcloud-sdk') { } 24 | // 25 | // XXX: GCloud SDK fails without throwing non-zero exit code for invalid registries with: 26 | // 27 | // WARNING: blah is not a supported registry 28 | 29 | def call (registries='') { 30 | if (!registries) { 31 | echo "No GAR registries specified, auto-populating complete GAR registry list" 32 | registries = sh ( 33 | label: 'GCloud SDK fetch GAR registry locations', 34 | returnStdout: true, 35 | script: """ 36 | set -eux 37 | if [ -n "\${GAR_PROJECT:-}" ]; then 38 | export CLOUDSDK_CORE_PROJECT="\$GAR_PROJECT" 39 | fi 40 | gcloud artifacts locations list --format='get(name)' | tr '\\n' ',' | sed 's/,\$//' 41 | """ 42 | ) 43 | if (!registries) { 44 | error "Failed to get list of GAR registry locations" 45 | } 46 | registries = registries.split(',').collect { "${it}-docker.pkg.dev" }.join(',') 47 | } 48 | if (registries.contains("'")) { 49 | error "invalid registries given to garAuthDocker(): $registries" 50 | } 51 | sh ( 52 | label: 'GCloud SDK Configure Docker Authentication for Google Artifact Registry', 53 | script: """ 54 | set -eux 55 | if [ -n "\${GAR_PROJECT:-}" ]; then 56 | export CLOUDSDK_CORE_PROJECT="\$GCR_PROJECT" 57 | fi 58 | # XXX: fails without throwing non-zero exit code for invalid registries with 59 | # WARNING: blah is not a supported registry 60 | gcloud auth configure-docker --quiet '$registries' 61 | """ 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /vars/downloadGrype.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 05:24:55 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d G r y p e 18 | // ========================================================================== // 19 | 20 | // XXX: Jenkins plugin doesn't document how grypeScanner function can use all the Grype settings such as --scope all-layers --fail-on high 21 | // 22 | // https://plugins.jenkins.io/grypescanner/ 23 | 24 | // This also gives greater control to download a specific Grype version from GitHub: 25 | // 26 | // https://github.com/anchore/grype/releases 27 | 28 | // Grype docker image doesn't have any command to keep the container alive as per: 29 | // 30 | // https://github.com/anchore/grype/issues/1287 31 | // 32 | // and it also saves RAM and billable cloud scaling to not have that container alive the whole time 33 | // 34 | // Downloading Grype only takes 4 seconds in testing 35 | 36 | def call (version='latest') { 37 | String label = "Download Grype on agent '$HOSTNAME'" 38 | // ! version instanceof String does not work and 39 | // version !instanceof String is only available in Groovy 3 40 | if (version instanceof String == false) { 41 | error "non-string version passed to downloadGrype() function" 42 | } 43 | if (version.contains("'")) { 44 | error "invalid version given to downloadGrype(): $version" 45 | } 46 | echo "Acquiring Lock: $label" 47 | lock (resource: "$label") { 48 | timeout (time: 3, unit: 'MINUTES') { 49 | withEnv (["VERSION=$version"]) { 50 | echo "$label" 51 | sh ( 52 | label: "$label, version '$VERSION'", 53 | script: """ 54 | set -eux 55 | curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin '$VERSION' 56 | """ 57 | ) 58 | sh ( 59 | label: "Grype Version", 60 | script: ''' 61 | set -eu 62 | grype version 63 | ''' 64 | ) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Hari Sekhon 3 | # Date: 2024-08-08 17:34:56 +0300 (Thu, 08 Aug 2024) 4 | # 5 | # vim:ts=2:sts=2:sw=2:et 6 | # 7 | # https///github.com/HariSekhon/Jenkins 8 | # 9 | # License: see accompanying Hari Sekhon LICENSE file 10 | # 11 | # If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | # 13 | # https://www.linkedin.com/in/HariSekhon 14 | # 15 | 16 | # ============================================================================ # 17 | # P r e - C o m m i t 18 | # ============================================================================ # 19 | 20 | --- 21 | fail_fast: false 22 | #exclude: *.tmp$ 23 | 24 | repos: 25 | 26 | # will accept anything that 'git clone' understands 27 | # this means you can set this to a local git repo to develop your own hook repos interactively 28 | - repo: https://github.com/pre-commit/pre-commit-hooks 29 | rev: v4.6.0 30 | hooks: 31 | - id: check-yaml 32 | # Common errors 33 | #- id: end-of-file-fixer # ruins .gitignore Icon\r 34 | - id: trailing-whitespace 35 | args: [--markdown-linebreak-ext=md] 36 | # Git style 37 | - id: check-added-large-files 38 | - id: check-merge-conflict 39 | - id: check-vcs-permalinks 40 | #- id: forbid-new-submodules 41 | # Cross platform 42 | - id: check-case-conflict 43 | - id: mixed-line-ending 44 | args: [--fix=lf] 45 | # Security 46 | - id: detect-aws-credentials 47 | args: ['--allow-missing-credentials'] 48 | 49 | # rewrites python files with useless changes like changing single quotes to double quotes 50 | #- repo: https://github.com/psf/black 51 | # rev: 24.8.0 52 | # hooks: 53 | # - id: black 54 | 55 | # Git secrets Leaks 56 | - repo: https://github.com/awslabs/git-secrets.git 57 | # the release tags for 1.2.0, 1.2.1 and 1.3.0 are broken with this error: 58 | # 59 | # /Users/hari/.cache/pre-commit/repo......./.pre-commit-hooks.yaml is not a file 60 | # 61 | rev: 5357e18 62 | hooks: 63 | - id: git-secrets 64 | 65 | - repo: https://github.com/markdownlint/markdownlint 66 | rev: v0.12.0 67 | hooks: 68 | - id: markdownlint 69 | name: Markdownlint 70 | description: Run markdownlint on your Markdown files 71 | entry: mdl 72 | args: [-s, .mdl.rb] 73 | language: ruby 74 | files: \.(md|mdown|markdown)$ 75 | -------------------------------------------------------------------------------- /vars/terragruntInit.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a g r u n t I n i t 18 | // ========================================================================== // 19 | 20 | def call (timeoutMinutes=10) { 21 | String label = 'Terragrunt Init' 22 | 23 | // forbids older inits from starting 24 | milestone(ordinal: null, label: "Milestone: $label") 25 | 26 | // terragrunt docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 27 | //container('terragrunt') { 28 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 29 | //dir ("components/${COMPONENT}") { 30 | ansiColor('xterm') { 31 | 32 | // let's check we have the login creds we think we should (checking up on the login() function you should have called earlier) 33 | printAuth() 34 | 35 | dir(env.TERRAFORM_DIR ?: ".") { 36 | 37 | // terraform workspace is not supported if using Terraform Cloud 38 | // TF_WORKSPACE overrides 'terraform workspace select' 39 | 40 | // alpine/terragrunt docker image doesn't have bash 41 | //sh '''#/usr/bin/env bash -euxo pipefail 42 | 43 | echo 'Terragrunt Workspace Select' 44 | sh label: 'Workspace Select', 45 | script: ''' 46 | set -eux 47 | if [ -n "${TF_WORKSPACE:-}" ]; then 48 | terragrunt workspace new "$TF_WORKSPACE" || echo "Workspace '$TF_WORKSPACE' already exists or using Terraform Cloud as a backend" 49 | #terragrunt workspace select "$TF_WORKSPACE" # TF_WORKSPACE takes precedence over this select 50 | fi 51 | ''' 52 | 53 | echo "$label" 54 | sh label: "$label", 55 | script: ''' 56 | terragrunt init --terragrunt-non-interactive -input=false 57 | ''' 58 | // # -backend-config "bucket=$ACCOUNT-$PROJECT-terraform" -backend-config "key=${ENV}-${PRODUCT}/${COMPONENT}/state.tf" 59 | } 60 | } 61 | } 62 | //} 63 | } 64 | -------------------------------------------------------------------------------- /vars/gcrDockerAuth.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 17:58:49 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // Configure Docker to Authenticate to Google Container Registry 18 | // ========================================================================== // 19 | 20 | // Requires: 21 | // 22 | // - gcpActivateServiceAccount.groovy to be called first to authenticate GCloud SDK 23 | // - needs GCloud SDK to be installed on the agent - if on Kubernetes make it the default container or else wrap this call in container('gcloud-sdk') { } 24 | // 25 | // XXX: GCloud SDK fails without throwing non-zero exit code for invalid registries with: 26 | // 27 | // WARNING: blah is not a supported registry 28 | 29 | // registry list is from here: https://cloud.google.com/container-registry/docs/overview#registries 30 | def call (registries='') { 31 | if (!registries) { 32 | registries = 'gcr.io,eu.gcr.io,us.gcr.io,asia.gcr.io' 33 | echo "No GCR registries specified, using default list of all documented regional registries: $registries" 34 | //error "cannot pass non-blank registries to gcrAuthDocker()" 35 | // 36 | // Can't find a GCloud SDK command similar to GAR to get a list of registries 37 | //echo "No GAR registries given, auto-populating complete GAR registry list" 38 | //registries = sh ( 39 | // label: 'GCloud SDK fetch GAR registries', 40 | // returnStdout: true, 41 | // script: "cloud artifacts locations list --format='get(name)' | tr '\\n' ',' | sed 's/,$//'" 42 | //) 43 | } 44 | if (registries.contains("'")) { 45 | error "invalid registries given to gcrAuthDocker(): $registries" 46 | } 47 | sh ( 48 | label: 'GCloud SDK Configure Docker Authentication for Google Container Registry', 49 | script: """ 50 | set -eux 51 | if [ -n "\${GCR_PROJECT:-}" ]; then 52 | export CLOUDSDK_CORE_PROJECT="\$GCR_PROJECT" 53 | fi 54 | # XXX: fails without throwing non-zero exit code for invalid registries with 55 | # WARNING: blah is not a supported registry 56 | gcloud auth configure-docker --quiet '$registries' 57 | """ 58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /vars/githubCreateRelease.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-02-17 19:16:24 +0000 (Fri, 17 Feb 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t H u b C r e a t e R e l e a s e 18 | // ========================================================================== // 19 | 20 | // Creates a GitHub Release Tag of the given argument 21 | // 22 | // If no release tag is given, tries to auto-determine the next increment from the current latest release tag using adjacent function githubNextRelease.groovy - see there for more details 23 | // 24 | // Requires GITHUB_TOKEN credential to be defined in the environment 25 | 26 | // https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release 27 | 28 | def call (Map args = [ repo: '', release: '', targetRef: '' ]) { 29 | 30 | // if repo not given, assume own repo 31 | String ownerRepo = args.repo ?: gitOwnerRepo() 32 | 33 | String release = args.release ?: githubNextRelease() 34 | 35 | String targetRef = args.targetRef ?: env.GIT_COMMIT 36 | 37 | if (!targetRef?.trim()) { 38 | error 'No targetRef passed and could not find GIT_COMMIT environment variable' 39 | } 40 | 41 | echo "Creating GitHub repo '$ownerRepo' release: $release" 42 | 43 | sh ( 44 | label: 'Create GitHub Release', 45 | script: """ 46 | set -eu 47 | # set -x is useful for debugging but could expose your GITHUB_TOKEN in the logs, although Jenkins should redact it 48 | set -x 49 | #set -o pipefail 2>/dev/null || : 50 | curl -sSf \ 51 | -X POST \ 52 | -H "Accept: application/vnd.github+json" \ 53 | -H "Authorization: Bearer \$GITHUB_TOKEN"\ 54 | -H "X-GitHub-Api-Version: 2022-11-28" \ 55 | https://api.github.com/repos/$ownerRepo/releases \ 56 | -d '{ 57 | "tag_name":"$release", 58 | "target_commitish": "$targetRef", 59 | "name": "$release", 60 | "body": "Jenkins auto-incremented release", 61 | "draft": false, 62 | "prerelease": false, 63 | "generate_release_notes": false 64 | }' 65 | """ 66 | ) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /vars/gcpSetupApplicationCredentials.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-09-01 12:50:03 +0100 (Wed, 01 Sep 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G C P S e t u p A p p l i c a t i o n C r e d e n t i a l s 18 | // ========================================================================== // 19 | 20 | // Requires: 21 | // 22 | // - environment {} section at top level of Jenkinsfile with: 23 | // - base64 encoded GCP_SERVICEACCOUNT_KEY environment variable 24 | // or wrapped in something like 25 | // - withCredentials([string(credentialsId: 'gcp-serviceaccount-key', variable: 'GCP_SERVICEACCOUNT_KEY')]) { 26 | // - GOOGLE_APPLICATION_CREDENTIALS environment variable set to a path to store the key - see top level Jenkinsfile template for a good example path 27 | 28 | def call (timeoutMinutes=1) { 29 | retry (2) { 30 | timeout (time: "$timeoutMinutes", unit: 'MINUTES') { 31 | String label = 'Generating GCP Application Credential Key' 32 | echo "$label" 33 | sh ( 34 | label: "$label", 35 | // needs to be bash to use <<< to avoid exposing the GCP_SERVICEACCOUNT_KEY in shell tracing 36 | //script: '''#!/bin/sh 37 | // but many docker containers like Trivy don't have Bash :'-( 38 | // # XXX: don't set -x as it'll expose the Service Account key credential 39 | // # base64 --decode is portable across Linux and Mac, but unfortunately busybox as found in Trivy container only supports -d 40 | // #base64 --decode <<< "$GCP_SERVICEACCOUNT_KEY" > "$keyfile" 41 | script: '''#!/bin/sh 42 | set -eu 43 | if [ -z "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]; then 44 | echo '$GOOGLE_APPLICATION_CREDENTIALS is not set' 45 | exit 1 46 | fi 47 | if [ -z "${GCP_SERVICEACCOUNT_KEY:-}" ]; then 48 | echo '$GCP_SERVICEACCOUNT_KEY is not set' 49 | exit 1 50 | fi 51 | 52 | keyfile="$GOOGLE_APPLICATION_CREDENTIALS" 53 | 54 | mkdir -p -v "$(dirname "$keyfile")" 55 | 56 | echo "Writing Google Application Credentials key file to '$keyfile'" 57 | 58 | echo "$GCP_SERVICEACCOUNT_KEY"| base64 -d > "$keyfile" 59 | ''' 60 | ) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vars/approval.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-04-30 15:25:01 +0100 (Fri, 30 Apr 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // A p p r o v a l 18 | // ========================================================================== // 19 | 20 | // Prompts for human click approval before progressing - protect your production environments from deployments 21 | 22 | // Usage: 23 | // 24 | // // submitter can be in group email or name format 25 | // approval(submitter: 'platform-engineering@mycompany.com,ApproversGroup', timeout: 10) 26 | // 27 | // or better to DRY between pipelines use a global variable: 28 | // 29 | // approval(submitter: "$APPROVERS", timeout: 10) 30 | // 31 | // set to 2 hours instead of default 60 minutes - these values are those supported by the standard Jenkins timeout() function: 32 | // 33 | // approval(submitter: "$APPROVERS", timeout: 2, timeoutUnits: 'HOURS') 34 | // 35 | // then configure $APPROVERS environment variable at the global Jenkins level: 36 | // 37 | // Manage Jenkins -> Configure System -> Global properties -> Environment Variables -> Add -> APPROVERS 38 | // 39 | // submitter = comma separated list of users/groups by name or email address that are permitted to authorize 40 | // ok = what the ok button should say, defaults to 'Proceed' if empty/unspecified 41 | 42 | def call (Map args = [submitter:'', timeout:60, timeoutUnits: 'MINUTES', ok:'']) { 43 | // XXX: prevents calling in a parallel stage otherwise you'll get this error: 44 | // 45 | // "Using a milestone step inside parallel is not allowed" 46 | // 47 | milestone ordinal: null, label: "Milestone: Approval" 48 | int time = args.timeout ?: 60 49 | String timeoutUnits = args.timeoutUnits ?: 'MINUTES' 50 | timeout (time: time, unit: timeoutUnits) { 51 | input ( 52 | message: """Are you sure you want to release this build? 53 | 54 | This prompt will time out in ${time} ${timeoutUnits.toLowerCase()}""", 55 | ok: args.ok, 56 | // only allow people in these 2 groups to approve before proceeeding eg. to production deployment - this list can now be provided as an argument 57 | //submitter: "platform-engineering@mydomain.co.uk,ApproversGroup" 58 | submitter: args.submitter 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /vars/gcpActivateServiceAccount.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-09-01 12:50:03 +0100 (Wed, 01 Sep 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G C P A c t i v a t e S e r v i c e A c c o u n t 18 | // ========================================================================== // 19 | 20 | // Requires base64 encoded GCP_SERVICEACCOUNT_KEY environment variable to be set in environment{} section of Jenkinsfile, see top level Jenkinsfile template 21 | 22 | def call (key='', timeoutMinutes=2) { 23 | if (key) { 24 | env.GCP_SERVICEACCOUNT_KEY = credentials(key) 25 | } else { 26 | if (!env.GCP_SERVICEACCOUNT_KEY) { 27 | error('gcpActivateServiceAccount: key not specified and GCP_SERVICEACCOUNT_KEY not set in the environment') 28 | } 29 | } 30 | retry (2) { 31 | timeout (time: "$timeoutMinutes", unit: 'MINUTES') { 32 | String label = 'Activating GCP Service Account credential' 33 | script { 34 | // if called on concurrent persistent agents instead of single-use Kubernetes ephemeral agents, isolate the config to this pipeline 35 | echo "Isolating GCloud SDK config auth to this pipeline" 36 | env.CLOUDSDK_CONFIG = "$HOME/.gcloud/auth/$BUILD_TAG" 37 | echo "Setting GCloud SDK to non-interactive" 38 | env.CLOUDSDK_CORE_DISABLE_PROMPTS = 1 39 | } 40 | echo "$label" 41 | sh ( 42 | label: "$label", 43 | // needs to be bash to use <<< to avoid exposing the GCP_SERVICEACCOUNT_KEY in shell tracing 44 | //script: '''#!/usr/bin/env bash 45 | // but many docker containers like Trivy don't have Bash :'-( 46 | // # XXX: don't set -x as it'll expose the Service Account key credential 47 | // # base64 --decode is portable across Linux and Mac, but unfortunately busybox as found in Trivy container only supports -d 48 | // #gcloud auth activate-service-account --key-file=<(base64 --decode <<< "$GCP_SERVICEACCOUNT_KEY") 49 | script: '''#!/bin/sh 50 | set -eu 51 | echo "$GCP_SERVICEACCOUNT_KEY" | base64 -d > /tmp/gcp_serviceaccount_key.json 52 | gcloud auth activate-service-account --key-file=/tmp/gcp_serviceaccount_key.json 53 | gcloud auth list 54 | ''' 55 | ) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /vars/downloadTrivy.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-05-11 05:24:55 +0100 (Thu, 11 May 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d T r i v y 18 | // ========================================================================== // 19 | 20 | // Downloading it for each run trades inbound bandwidth (free) for not using RAM for bigger Jenkins pods causing more scale and billable Kubernetes 21 | // 22 | // Downloading Trivy only takes 6 seconds in testing 23 | // 24 | // The alternative is using the docker image which will be cached but hold RAM for the entire duration of the pipeline, which is very RAM inefficient: 25 | // 26 | // https://github.com/HariSekhon/Kubernetes-configs/blob/master/jenkins/base/jenkins-agent-pod.yaml 27 | 28 | // get release version from: 29 | // 30 | // https://github.com/aquasecurity/trivy/releases 31 | // 32 | 33 | def call (version='latest') { 34 | String label = "Download Trivy on agent '$HOSTNAME'" 35 | // ! version instanceof String does not work and 36 | // version !instanceof String is only available in Groovy 3 37 | if (version instanceof String == false) { 38 | error "non-string version passed to downloadTrivy() function" 39 | } 40 | if (version.contains("'")) { 41 | error "invalid version given to downloadTrivy(): $version" 42 | } 43 | echo "Acquiring Lock: $label" 44 | lock (resource: "$label") { 45 | timeout (time: 3, unit: 'MINUTES') { 46 | withEnv(["VERSION=$version"]) { 47 | echo "$label" 48 | sh ( 49 | label: "$label, version '$VERSION'", 50 | script: """ 51 | set -eux 52 | curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin '$VERSION' 53 | """ 54 | ) 55 | sh ( 56 | label: "Trivy Version", 57 | script: ''' 58 | set -eu 59 | trivy version 60 | ''' 61 | ) 62 | label = "Download Trivy HTML Report Template" 63 | echo "$label" 64 | sh ( 65 | label: "$label", 66 | script: "curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/html.tpl > trivy-html.tpl" 67 | ) 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /vars/terragruntPlan.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a g r u n t P l a n 18 | // ========================================================================== // 19 | 20 | def call (timeoutMinutes=10) { 21 | String terraformDir = env.TERRAFORM_DIR ?: '.' 22 | String unique = "Dir: $terraformDir" 23 | String label = "Terragrunt Plan - $unique" 24 | // must differentiate lock to share the same lock between Terraform Plan and Terraform Apply 25 | String lockString = "Terraform - $unique" 26 | echo "Acquiring Terragrunt Plan Lock: $lockString" 27 | lock (resource: lockString, inversePrecedence: true) { 28 | // forbids older plans from starting 29 | milestone(ordinal: null, label: "Milestone: $label") 30 | 31 | // terragrunt docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 32 | //container('terragrunt') { 33 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 34 | //dir ("components/${COMPONENT}") { 35 | ansiColor('xterm') { 36 | dir("$terraformDir") { 37 | // alpine/terragrunt docker image doesn't have bash 38 | //sh '''#/usr/bin/env bash -euxo pipefail 39 | //sh '''#/bin/sh -eux 40 | echo 'Terragrunt Workspace List' 41 | sh ( 42 | label: 'Workspace List', 43 | script: 'terragrunt workspace list || :' // # 'workspaces not supported' if using Terraform Cloud as a backend 44 | ) 45 | echo "$label" 46 | sh ( 47 | label: "$label", 48 | script: 'terragrunt plan --terragrunt-non-interactive -out=plan.zip -input=false' // # -var-file=base.tfvars -var-file="$ENV.tfvars" 49 | ) 50 | script { 51 | logList = currentBuild.rawBuild.getLog(100) 52 | logString = logList.join('\n') 53 | if (logString.contains('Your infrastructure matches the configuration')) { 54 | env.TERRAFORM_CHANGES = false 55 | } else { 56 | env.TERRAFORM_CHANGES = true 57 | } 58 | } 59 | } 60 | } 61 | } 62 | //} 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /vars/githubNextRelease.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2023-02-17 18:27:43 +0000 (Fri, 17 Feb 2023) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // G i t H u b N e x t R e l e a s e 18 | // ========================================================================== // 19 | 20 | // Returns the next release version by querying current GitHub releases and incrementing by one 21 | // 22 | // Expects release version format such as semver 1.2.3 or YYYY.NN eg. 2023.1 23 | // 24 | // XXX: does not currently support alphabetic naming convention prefixes/suffixes like v1.0 or kustomize/v5.0.0 25 | // 26 | // Requires GITHUB_TOKEN credential to be defined in the environment 27 | 28 | // https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-the-latest-release 29 | 30 | def call (String repo='') { 31 | 32 | // if repo not given, assume own repo 33 | String ownerRepo = repo ?: gitOwnerRepo() 34 | 35 | echo "Querying GitHub API for latest release tag of repo: $ownerRepo" 36 | 37 | String jsonOutput = sh ( 38 | returnStdout: true, 39 | label: 'Query GitHub Latest Release', 40 | script: """ 41 | set -eu 42 | # set -x is useful for debugging but could expose your GITHUB_TOKEN in the logs, although Jenkins should redact it 43 | set -x 44 | #set -o pipefail 2>/dev/null || : 45 | curl -sS -H "Authorization: Bearer \$GITHUB_TOKEN" "https://api.github.com/repos/$ownerRepo/releases/latest" 46 | """ 47 | ) 48 | 49 | def json = new groovy.json.JsonSlurper().parseText(jsonOutput) 50 | assert json instanceof Map 51 | 52 | String currentVersion = json.tag_name 53 | 54 | // XXX: GitHub json.tag_name is a string and so this won't carry through any alphabetic naming conventions prefixes/suffixes such a v1.0 or kustomize/v5.0.0 - something to be improved 55 | //List versionComponents = currentVersion.findAll( /\d+/ ).collect { it.toInteger() } 56 | List versionComponents = currentVersion.findAll( /\d+/ )*.toInteger() 57 | 58 | echo "Latest release is: ${versionComponents.join('.')}" 59 | 60 | // increment the last number by one 61 | versionComponents[-1] = versionComponents[-1] + 1 62 | 63 | newVersion = versionComponents.join('.') 64 | 65 | echo "New release is: $newVersion" 66 | 67 | return newVersion 68 | 69 | } 70 | -------------------------------------------------------------------------------- /vars/terraformInit.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a f o r m I n i t 18 | // ========================================================================== // 19 | 20 | def call (timeoutMinutes=10) { 21 | String label = 'Terraform Init' 22 | 23 | // forbids older inits from starting 24 | milestone(ordinal: null, label: "Milestone: $label") 25 | 26 | // terraform docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 27 | //container('terraform') { 28 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 29 | //dir ("components/${COMPONENT}") { 30 | ansiColor('xterm') { 31 | 32 | // let's check we have the login creds we think we should (checking up on the login() function you should have called earlier) 33 | printAuth() 34 | 35 | // org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use staticMethod java.lang.System getenv java.lang.String 36 | //dir(System.getenv("TERRAFORM_DIR") ?: ".") { 37 | dir(env.TERRAFORM_DIR ?: ".") { 38 | 39 | // terraform workspace is not supported if using Terraform Cloud 40 | // TF_WORKSPACE overrides 'terraform workspace select' 41 | 42 | // terraform docker image doesn't have bash 43 | //sh '''#/usr/bin/env bash -euxo pipefail 44 | 45 | echo 'Terraform Workspace Select' 46 | sh label: 'Workspace Select', 47 | script: ''' 48 | set -eux 49 | if [ -n "${TF_WORKSPACE:-}" ]; then 50 | terraform workspace new "$TF_WORKSPACE" || echo "Workspace '$TF_WORKSPACE' already exists or using Terraform Cloud as a backend" 51 | #terraform workspace select "$TF_WORKSPACE" # TF_WORKSPACE takes precedence over this select 52 | fi 53 | ''' 54 | 55 | echo "$label" 56 | sh label: "$label", 57 | script: ''' 58 | terraform init -input=false 59 | ''' 60 | // -backend-config "bucket=$ACCOUNT-$PROJECT-terraform" -backend-config "key=${ENV}-${PRODUCT}/${COMPONENT}/state.tf" 61 | } 62 | } 63 | } 64 | //} 65 | } 66 | -------------------------------------------------------------------------------- /vars/terraformApply.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2022-01-06 17:35:16 +0000 (Thu, 06 Jan 2022) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // T e r r a f o r m A p p l y 18 | // ========================================================================== // 19 | 20 | def call (timeoutMinutes=60) { 21 | String terraformDir = env.TERRAFORM_DIR ?: '.' 22 | String unique = "Dir: $terraformDir" 23 | String label = "Terraform Apply - $unique" 24 | // must differentiate lock to share the same lock between Terraform Plan and Terraform Apply 25 | String lockString = "Terraform - $unique" 26 | echo "Acquiring Terraform Apply Lock: $lockString" 27 | lock (resource: lockString, inversePrecedence: true) { 28 | // forbids older applys from starting 29 | milestone(ordinal: null, label: "Milestone: $label") 30 | 31 | // terraform docker image is pretty useless, doesn't have the tools to authenticate to cloud providers 32 | //container('terraform') { 33 | timeout (time: timeoutMinutes, unit: 'MINUTES') { 34 | //dir ("components/${COMPONENT}") { 35 | ansiColor('xterm') { 36 | // for test environments, add a param to trigger -destroy switch 37 | dir("$terraformDir") { 38 | echo "$label" 39 | sh ( 40 | label: "$label", 41 | // cannot add ${args} here when using saved plan, otherwise will get an error like this: 42 | // 43 | //[2022-07-11T15:45:39.085Z] + terraform apply -input=false -auto-approve -var-file ../tf_vars/datadog.tfvars plan.zip 44 | //[2022-07-11T15:45:39.085Z] ╷ 45 | //[2022-07-11T15:45:39.085Z] │ Error: Can't set variables when applying a saved plan 46 | //[2022-07-11T15:45:39.085Z] │ 47 | //[2022-07-11T15:45:39.085Z] │ The -var and -var-file options cannot be used when applying a saved plan 48 | //[2022-07-11T15:45:39.085Z] │ file, because a saved plan includes the variable values that were set when 49 | //[2022-07-11T15:45:39.085Z] │ it was created. 50 | //[2022-07-11T15:45:39.085Z] ╵ 51 | //script returned exit code 1 52 | script: "terraform apply -input=false -auto-approve plan.zip" 53 | ) 54 | } 55 | } 56 | } 57 | //} 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /vars/downloadKustomize.groovy: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Hari Sekhon 3 | // Date: 2021-09-01 11:57:48 +0100 (Wed, 01 Sep 2021) 4 | // 5 | // vim:ts=2:sts=2:sw=2:et 6 | // 7 | // https://github.com/HariSekhon/Jenkins 8 | // 9 | // License: see accompanying Hari Sekhon LICENSE file 10 | // 11 | // If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish 12 | // 13 | // https://www.linkedin.com/in/HariSekhon 14 | // 15 | 16 | // ========================================================================== // 17 | // D o w n l o a d K u s t o m i z e 18 | // ========================================================================== // 19 | 20 | // Downloading Kustomize only takes 1 second in testing 21 | // 22 | // This is better (easier and more version flexible across different pipelines) than maintaining a docker image harisekhon/git-kustomize docker image. 23 | // The docker image cache is of negligible benefit in this case 24 | // 25 | // See top-level Jenkinsfile, adjacent gitKustomizeImage.groovy and jenkins-agent-pod.yaml in: 26 | // 27 | // https://github.com/HariSekhon/Kubernetes-configs 28 | 29 | // get release version from: 30 | // 31 | // https://github.com/kubernetes-sigs/kustomize/releases 32 | // 33 | 34 | // Kustomize version needs to be fairly recent to solve 'unknown field "includeCRDs"' when combining Kustomize + Helm with includeCRDs option as seen in *-kustomization.yaml in https://github.com/HariSekhon/Kubernetes-configs 35 | def call (version='4.5.7') { 36 | String label = "Download Kustomize on agent '$HOSTNAME'" 37 | // strip a 'v' prefix if present because we add it to the URL ourselves 38 | if (version[0] == 'v') { 39 | version = version.substring(1) 40 | } 41 | echo "Acquiring Lock: $label" 42 | lock (resource: "$label") { 43 | timeout (time: 2, unit: 'MINUTES') { 44 | withEnv(["VERSION=$version"]) { 45 | echo "$label" 46 | sh ( 47 | label: "$label, version '$version'", 48 | script: ''' 49 | set -eux 50 | 51 | echo "Downloading Kustomize version $VERSION" 52 | 53 | curl -sSL -o "/tmp/kustomize.$$.tgz" "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${VERSION}/kustomize_v${VERSION}_linux_amd64.tar.gz" 54 | 55 | tar zxvf "/tmp/kustomize.$$.tgz" kustomize -O > "/tmp/kustomize.$$" 56 | chmod +x "/tmp/kustomize.$$" 57 | 58 | mv -fv "/tmp/kustomize.$$" /usr/local/bin/kustomize 59 | ''' 60 | ) 61 | sh ( 62 | label: "Kustomize Version", 63 | script: ''' 64 | set -eu 65 | kustomize version 66 | ''' 67 | ) 68 | } 69 | } 70 | } 71 | } 72 | --------------------------------------------------------------------------------