├── generate-docs.sh ├── log-parser-rules.txt ├── settings.gradle ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── init.d │ └── init.gradle ├── INSTALL.md ├── src ├── main │ └── groovy │ │ └── jenkins │ │ └── automation │ │ ├── utils │ │ ├── Environment.groovy │ │ ├── EnvironmentUtils.groovy │ │ ├── XmlParserHelper.groovy │ │ ├── ScmUtils.groovy │ │ ├── GhUtils.groovy │ │ ├── PluginUtils.groovy │ │ ├── CheckmarxUtils.groovy │ │ └── CommonUtils.groovy │ │ ├── builders │ │ ├── FrontEndTestJobBuilder.groovy │ │ ├── BaseJobBuilder.groovy │ │ ├── SiteMonitorJobBuilder.groovy │ │ ├── CheckmarxSecurityJobBuilder.groovy │ │ ├── SauceConnectJobBuilder.groovy │ │ ├── SalesforceAntJobBuilder.groovy │ │ ├── PipelineJobBuilder.groovy │ │ ├── JsJobBuilder.groovy │ │ ├── BddSecurityJobBuilder.groovy │ │ └── MultibranchPipelineJobBuilder.groovy │ │ └── rest │ │ ├── RestApiScriptRunner.groovy │ │ └── RestApiJobManagement.groovy └── test │ └── groovy │ ├── EnvironmentUtilsTests.groovy │ └── ExampleJobValidSyntaxTests.groovy ├── .travis.yml ├── CHANGELOG.md ├── example-jobs ├── sauce_connect_job.groovy ├── new_relic_notifier_job.groovy ├── sqs_notifier_job.groovy ├── environment_test_job.groovy ├── mb_pipeline_job_git.groovy ├── salesforce_ant_job.groovy ├── mb_pipeline_job_github_enterprise.groovy ├── site_monitor_job.groovy ├── js_build_job.groovy ├── checkmarx_security_job.groovy ├── ghe_job.groovy ├── mother_seed_job.groovy ├── pipeline_job.groovy ├── common_utils.groovy └── base_jobs.groovy ├── jobs └── jac.groovy ├── .gitignore ├── CONTRIBUTING.md ├── TERMS.md ├── gradlew.bat ├── README.md ├── opensource-checklist.md ├── gradlew ├── CODE_OF_CONDUCT.md ├── LICENSE └── docs └── examples.md /generate-docs.sh: -------------------------------------------------------------------------------- 1 | ./gradlew groovydoc -------------------------------------------------------------------------------- /log-parser-rules.txt: -------------------------------------------------------------------------------- 1 | warning /(?i)is deprecated/ 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jenkins-automation' 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cfpb/jenkins-automation/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation instructions 2 | 3 | Detailed instructions on how to install, configure, and get the project running. 4 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/Environment.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | /** 4 | * Created by muchniki on 12/4/15. 5 | */ 6 | enum Environment { 7 | dev, 8 | prod, 9 | stage 10 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Nov 18 18:50:40 EST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: java 3 | jdk: 4 | - openjdk8 5 | before_cache: 6 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 7 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 8 | cache: 9 | directories: 10 | - $HOME/.gradle/caches/ 11 | - $HOME/.gradle/wrapper/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | All notable changes to this project will be documented in this file. 2 | We follow the [Semantic Versioning 2.0.0](http://semver.org/) format. 3 | 4 | 5 | ## x.y.z - YYYY-MM-DD 6 | 7 | ### Added 8 | - Lorem ipsum dolor sit amet 9 | 10 | ### Deprecated 11 | - Nothing. 12 | 13 | ### Removed 14 | - Nothing. 15 | 16 | ### Fixed 17 | - Nothing. 18 | -------------------------------------------------------------------------------- /example-jobs/sauce_connect_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.SauceConnectJobBuilder 2 | 3 | def projectName ='foo' 4 | new SauceConnectJobBuilder( 5 | name: "${projectName}-browser-tests", 6 | description: "Sample sauce connect job", 7 | emails: ['email1@server1.com', 'email2@server2.com'], 8 | sauceCredentialId: '1234', 9 | additionalOptions: "-v" 10 | ).build(this); -------------------------------------------------------------------------------- /example-jobs/new_relic_notifier_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.BaseJobBuilder 2 | import jenkins.automation.utils.PluginUtils 3 | 4 | def job = new BaseJobBuilder( 5 | name: "sample-base-job-with-new-relic", 6 | description: "A job with some additional plugin added" 7 | ).build(this).with { 8 | PluginUtils.addNewRelicSupport(delegate, "nr-api-key", "new", "foo", "some_log", "deploy", '1') 9 | } 10 | 11 | -------------------------------------------------------------------------------- /example-jobs/sqs_notifier_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.BaseJobBuilder 2 | 3 | import jenkins.automation.utils.PluginUtils 4 | 5 | def job=new BaseJobBuilder( 6 | name: "sample-base-job-with-sqs-support", 7 | description: "A job with some additional plugin added" 8 | ).build(this).with { 9 | PluginUtils.addSQSNotification(delegate, "" ,"https://localhost/jenkins","foobar",customSQSMessageValue="Say something sharp" ) 10 | } -------------------------------------------------------------------------------- /example-jobs/environment_test_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.utils.EnvironmentUtils 2 | 3 | // in our Jenkinses, we have a global variable named "JAC_ENVIRONMENT", 4 | // with values of "DEV", "STAGE", or "PROD" 5 | def env = EnvironmentUtils.getInstance("${JAC_ENVIRONMENT}") 6 | println "Environment is " + env.getEnv() 7 | 8 | if(env.isDev()) { 9 | //do something 10 | } 11 | 12 | job('test') { 13 | steps { 14 | shell "echo ${env.getEnv()}" 15 | } 16 | } -------------------------------------------------------------------------------- /gradle/init.d/init.gradle: -------------------------------------------------------------------------------- 1 | def copyConfigs = { 2 | def osa_dir = new File('osa_dependencies') 3 | if ( !osa_dir.exists() ) { 4 | osa_dir.mkdirs() 5 | } 6 | from project.configurations.compile 7 | into 'jac_dependencies' 8 | } 9 | 10 | allprojects { project -> 11 | afterEvaluate { 12 | project.tasks.create( 13 | name: 'copyDeps', 14 | type: Copy, 15 | copyConfigs, 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example-jobs/mb_pipeline_job_git.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.MultibranchPipelineJobBuilder 2 | 3 | 4 | new MultibranchPipelineJobBuilder( 5 | name: "multi-branch-pipeline-git", 6 | description: "Sample Multibranch Pipeline using git as the branch source", 7 | branchSource: MultibranchPipelineJobBuilder.BranchSourceType.GIT, 8 | gitCredentials: "009c8c9d-3cf5-4b2a-89f3-286977cabddf", 9 | gitRemote: "https://github.com/OrlandoSoto/orlando-shared-libraries", 10 | ).build(this) 11 | -------------------------------------------------------------------------------- /example-jobs/salesforce_ant_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.SalesforceAntJobBuilder 2 | 3 | List developers = ['jane@example.com', 'joe@example.com'] 4 | 5 | def repo = "https://github.com/cfpb/salesforce-automation" 6 | 7 | new SalesforceAntJobBuilder( 8 | name: "example-salesforce-ant-job", 9 | description: 'An example using a job builder for a Salesforce Ant JobBuilder build jobs project.', 10 | repoUrl: repo, 11 | emails: developers, 12 | antTasks: ["retrieveUnpackaged", "publish"], 13 | antInstallerName:"ant-latest" 14 | ).build(this) 15 | 16 | -------------------------------------------------------------------------------- /example-jobs/mb_pipeline_job_github_enterprise.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.MultibranchPipelineJobBuilder 2 | 3 | 4 | new MultibranchPipelineJobBuilder( 5 | name: "multi-branch-pipeline-github-enterprise", 6 | description: "Sample Multibranch Pipeline using the GitHub API as the branch source", 7 | branchSource: MultibranchPipelineJobBuilder.BranchSourceType.GITHUB, 8 | gitCredentials: "8fbdbaa0-d5ff-4acb-9e8f-27e49b77048a", 9 | ghOwner: "org-name", 10 | ghRepo: "repo-name", 11 | ghApiEndpoint: "https://github.example.com/api/v3", 12 | oldNumToKeep: 1 13 | ).build(this) 14 | -------------------------------------------------------------------------------- /src/test/groovy/EnvironmentUtilsTests.groovy: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by muchniki on 12/13/15. 3 | */ 4 | import jenkins.automation.utils.EnvironmentUtils 5 | import spock.lang.Specification 6 | 7 | class EnvironmentUtilsTests extends Specification { 8 | 9 | void 'Should Correctly Return Env'() { 10 | 11 | given: 12 | def ENVIRONMENT = 'dev' 13 | 14 | when: 15 | def myEnv = EnvironmentUtils.getInstance(ENVIRONMENT) 16 | 17 | then: 18 | assert myEnv.isDev() 19 | assert !myEnv.isProd() 20 | assert !myEnv.isStage() 21 | assert myEnv.getEnv() as String == ENVIRONMENT; 22 | } 23 | } -------------------------------------------------------------------------------- /example-jobs/site_monitor_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.SiteMonitorJobBuilder 2 | 3 | 4 | new SiteMonitorJobBuilder( 5 | name: "my-site-pulse-check", 6 | description: "Sample url pulse check job", 7 | cronSchedule: "@daily", 8 | urls: ["https://google.com","https://yourethemannowdog.ytmnd.com/"] 9 | ).build(this); 10 | 11 | 12 | new SiteMonitorJobBuilder( 13 | name: "my-site-pulse-check-without-schedule", 14 | description: "Sample url pulse check job without a schedule", 15 | cronSchedule: "", 16 | urls: ["https://google.com","https://yourethemannowdog.ytmnd.com/"] 17 | ).build(this); 18 | 19 | -------------------------------------------------------------------------------- /example-jobs/js_build_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.FrontEndTestJobBuilder 2 | 3 | 4 | String basePath = 'JsJobSamples' 5 | List developers = ['jane@example.com', 'joe@example.com'] 6 | 7 | def repos = [ 8 | [name: 'jenkins-automation', url: "https://github.com/cfpb/jenkins-automation@2.0"], 9 | [name: 'collab', url: "https://github.com/cfpb/jenkins-automation"] 10 | ] 11 | folder(basePath) { 12 | description 'This example shows how to create jobs using Job builders.' 13 | } 14 | 15 | 16 | new FrontEndTestJobBuilder( 17 | name: "$basePath/BuilderVsBuilders", 18 | description: 'An example using a job builder for a Javascript build jobs project.', 19 | repos: repos, 20 | emails: developers, 21 | use_versions: true 22 | // some more testparams 23 | ).build(this) 24 | 25 | -------------------------------------------------------------------------------- /example-jobs/checkmarx_security_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.CheckmarxSecurityJobBuilder 2 | 3 | 4 | def projectName = "foo" 5 | def groupId = "ac43cb0d-034d-4b1e-9bf5-e7c1c46f71d2" 6 | 7 | 8 | new CheckmarxSecurityJobBuilder( 9 | name: "${projectName}-checkmarx", 10 | description: "Sample checkmarx security job", 11 | scanRepo: [ 12 | [url: "https://github.com/OrlandoSoto/ckan-browser-tests"] 13 | ], 14 | checkmarxConfig: [ 15 | projectName: "${projectName}-checkmarx", 16 | groupId: "${groupId}", 17 | comment: "Generated by Checkmarx job builder JAC", 18 | vulnerabilityThresholdEnabled: true, 19 | highThreshold: 1, 20 | mediumThreshold: 2, 21 | lowThreshold: 3, 22 | ] 23 | ).build(this); 24 | -------------------------------------------------------------------------------- /example-jobs/ghe_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.BaseJobBuilder 2 | import jenkins.automation.utils.GhUtils 3 | 4 | new BaseJobBuilder( 5 | name: 'GH_PR_builder', 6 | description: 'Does GH PR building', 7 | ).build(this).with { 8 | GhUtils.ghPrWatcher( 9 | delegate, 10 | [ 11 | ghProject: 'cfpb/reponame', 12 | ghHostname: 'github.org.tld', 13 | ghAuthId: '', 14 | ghPrHooks: false, 15 | ghPrCron: '*/2 * * * *', 16 | ghPrOrgsList: 'CFPB', 17 | ghPrStatusContext: 'Test your example job', 18 | ghPrResultMessage: [ 19 | 'SUCCESS': 'Tests completed normally', 20 | 'ERROR': 'Tests errored', 21 | 'FAILURE': 'Tests failed', 22 | ] 23 | ] 24 | ) 25 | 26 | steps { 27 | shell(''' 28 | echo Testing 29 | '''.stripIndent() 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jobs/jac.groovy: -------------------------------------------------------------------------------- 1 | job('generate docs') { 2 | scm { 3 | git { 4 | remote { 5 | name('origin') 6 | branch('master') 7 | url("https://github.com/cfpb/jenkins-automation") 8 | credentials('009c8c9d-3cf5-4b2a-89f3-286977cabddf') 9 | } 10 | extensions { 11 | cleanBeforeCheckout() 12 | wipeOutWorkspace() 13 | } 14 | } 15 | } 16 | 17 | steps { 18 | 19 | gradle { 20 | tasks('groovydoc') 21 | useWrapper() 22 | } 23 | shell(""" 24 | git add -A 25 | git commit -m'Jenkins autogenerate docs' 26 | """) 27 | } 28 | triggers { 29 | scm 'H/5 * * * *' 30 | } 31 | publishers { 32 | git { 33 | pushOnlyIfSuccess() 34 | pushMerge(false) 35 | forcePush(true) 36 | branch('origin', 'gh-pages') 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/groovy/ExampleJobValidSyntaxTests.groovy: -------------------------------------------------------------------------------- 1 | import groovy.io.FileType 2 | import javaposse.jobdsl.dsl.* 3 | import spock.lang.* 4 | 5 | class ExampleJobValidSyntaxTests extends Specification { 6 | 7 | void 'test dev and prod jobs do not fail'() { 8 | 9 | given: 10 | JobManagement jm = new MemoryJobManagement() 11 | DslScriptLoader scriptLoader = new DslScriptLoader(jm) 12 | 13 | def params = jm.getParameters() 14 | 15 | List files = [] 16 | 17 | new File('example-jobs').eachFileRecurse(FileType.FILES) { 18 | if (it.name.endsWith('.groovy')) { 19 | files << it 20 | } 21 | } 22 | 23 | when: 24 | ['dev', 'stage', 'prod'].each { environment -> 25 | params['JAC_ENVIRONMENT'] = environment 26 | 27 | println "\nTesting jobs for with environment = $environment \n" 28 | 29 | files.each { file -> 30 | println file 31 | scriptLoader.runScript(file.text) 32 | } 33 | } 34 | 35 | then: 36 | 37 | assert true 38 | noExceptionThrown() 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .gradle/ 3 | build/ 4 | sandbox/ 5 | *.iml 6 | 7 | # Compiled source # 8 | ################### 9 | *.com 10 | *.class 11 | *.dll 12 | *.exe 13 | *.o 14 | *.so 15 | _site/ 16 | 17 | # Packages # 18 | ############ 19 | # it's better to unpack these files and commit the raw source 20 | # git has its own built in compression methods 21 | *.7z 22 | *.dmg 23 | *.gz 24 | *.iso 25 | #*.jar 26 | *.rar 27 | *.tar 28 | *.zip 29 | 30 | # Logs and databases # 31 | ###################### 32 | *.log 33 | *.sql 34 | *.sqlite 35 | 36 | # OS generated files # 37 | ###################### 38 | .DS_Store 39 | .DS_Store? 40 | .Spotlight-V100 41 | .Trashes 42 | Icon? 43 | ehthumbs.db 44 | Thumbs.db 45 | 46 | # Vim swap files # 47 | ################## 48 | *.swp 49 | 50 | # Python # 51 | ################# 52 | *.pyc 53 | *.egg-info/ 54 | __pycache__/ 55 | *.py[cod] 56 | .env 57 | 58 | # Django # 59 | ################# 60 | *.egg-info 61 | .installed.cfg 62 | 63 | # Unit test / coverage reports 64 | ################# 65 | htmlcov/ 66 | .tox/ 67 | .coverage 68 | .cache 69 | nosetests.xml 70 | coverage.xml 71 | 72 | # Front-End # 73 | ############# 74 | node_modules/ 75 | bower_components/ 76 | .grunt/ 77 | src/vendor/ 78 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/FrontEndTestJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | 6 | class FrontEndTestJobBuilder { 7 | 8 | String name 9 | String description 10 | String gitBranch = 'master' 11 | String pollScmSchedule = '@daily' 12 | String tasks 13 | String junitResults = '**/build/test-results/*.xml' 14 | String artifacts = 'dist/' 15 | List emails 16 | Boolean use_versions 17 | def repos; 18 | 19 | Job build(DslFactory factory) { 20 | 21 | Job jsJob = new JsJobBuilder( 22 | name: this.name, 23 | description: this.description, 24 | repos: this.repos, 25 | emails: this.emails, 26 | use_versions: true 27 | ).build(factory) 28 | 29 | print jsJob 30 | 31 | jsJob.steps { 32 | 33 | shell( 34 | ''' 35 | cd $DIR_UNDER_TEST 36 | ./frontendtest.sh 37 | 38 | ''' 39 | ) 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/EnvironmentUtils.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | 4 | /** 5 | * Utility Class used to determine the environment at runtime 6 | * see example usage 7 | */ 8 | 9 | 10 | class EnvironmentUtils { 11 | 12 | /** 13 | * Enum for Jenkins environments 14 | * relies on ENVIRONMENT env variable in Jenkins 15 | */ 16 | 17 | private static final EnvironmentUtils instance = new EnvironmentUtils() 18 | String env 19 | 20 | private EnvironmentUtils() { 21 | // do nothing 22 | } 23 | 24 | public static EnvironmentUtils getInstance(String env) { 25 | instance.env = env 26 | return instance 27 | } 28 | 29 | 30 | boolean isDev() { 31 | return getEnv() as Environment== Environment.dev 32 | } 33 | 34 | boolean isProd() { 35 | return getEnv() as Environment == Environment.prod 36 | } 37 | 38 | boolean isStage() { 39 | return getEnv() as Environment == Environment.stage 40 | } 41 | 42 | 43 | String getEnv() { 44 | try { 45 | return env.toLowerCase() as Environment 46 | } catch (all) { 47 | return all.getLocalizedMessage() 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/rest/RestApiScriptRunner.groovy: -------------------------------------------------------------------------------- 1 | // Credit: https://github.com/sheehan/job-dsl-gradle-example 2 | 3 | package jenkins.automation.rest 4 | 5 | import javaposse.jobdsl.dsl.DslScriptLoader 6 | 7 | String pattern = System.getProperty('pattern') 8 | String baseUrl = System.getProperty('baseUrl') 9 | String username = System.getProperty('username') 10 | String password = System.getProperty('password') // password or token 11 | 12 | if (!pattern || !baseUrl) { 13 | println 'usage: -Dpattern= -DbaseUrl= [-Dusername=] [-Dpassword=]' 14 | System.exit 1 15 | } 16 | 17 | RestApiJobManagement jm = new RestApiJobManagement(baseUrl) 18 | if (username && password) { 19 | println "Setting credentials to ${username}:${password}" 20 | jm.setCredentials username, password 21 | } 22 | 23 | //simulate our required Jenkins Environment Variables 24 | params = jm.getParameters() 25 | params['JAC_ENVIRONMENT'] = System.getProperty('JAC_ENVIRONMENT') ?: 'dev' 26 | params['JAC_HOST'] = System.getProperty('JAC_HOST') ?: 'aws' 27 | params['JENKINS_URL'] = baseUrl 28 | 29 | 30 | new FileNameFinder().getFileNames('.', pattern).each { String fileName -> 31 | println "\nprocessing file: $fileName" 32 | File file = new File(fileName) 33 | new DslScriptLoader(jm).runScript(file.text) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/BaseJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | import javaposse.jobdsl.dsl.* 6 | import jenkins.automation.utils.CommonUtils 7 | 8 | /** 9 | * The very first and basic building block 10 | * 11 | *

12 | * creates a job with colorized input, 13 | * log rotator, email notifications and build claiming 14 | *

15 | * @param name used to name the job 16 | * @param description job description 17 | * @param emails list of developer to get notifications 18 | *

19 | * 20 | * @see Base job builder example 22 | 23 | *

24 | */ 25 | class BaseJobBuilder { 26 | String name 27 | String description 28 | List emails 29 | 30 | Job build(DslFactory factory) { 31 | factory.job(name) { 32 | it.description this.description 33 | CommonUtils.addDefaults(delegate) 34 | publishers { 35 | if (emails) { 36 | publishers { 37 | CommonUtils.addExtendedEmail(delegate, emails) 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guidance on how to contribute 2 | 3 | > All contributions to this project will be released under the CC0 public domain 4 | > dedication. By submitting a pull request or filing a bug, issue, or 5 | > feature request, you are agreeing to comply with this waiver of copyright interest. 6 | > Details can be found in our [TERMS](TERMS.md) and [LICENCE](LICENSE). 7 | 8 | 9 | There are two primary ways to help: 10 | - Using the issue tracker, and 11 | - Changing the code-base. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | Use the issue tracker to suggest feature requests, report bugs, and ask questions. 17 | This is also a great way to connect with the developers of the project as well 18 | as others who are interested in this solution. 19 | 20 | Use the issue tracker to find ways to contribute. Find a bug or a feature, mention in 21 | the issue that you will take on that effort, then follow the _Changing the code-base_ 22 | guidance below. 23 | 24 | 25 | ## Changing the code-base 26 | 27 | Generally speaking, you should fork this repository, make changes in your 28 | own fork, and then submit a pull-request. All new code should have associated unit 29 | tests that validate implemented features and the presence or lack of defects. 30 | Additionally, the code should follow any stylistic and architectural guidelines 31 | prescribed by the project. In the absence of such guidelines, mimic the styles 32 | and patterns in the existing code-base. 33 | 34 | When adding new builders groovydoc comments are required for all builders and [example of usage](docs/exmples.md) -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/SiteMonitorJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | import jenkins.automation.builders.BaseJobBuilder 6 | 7 | /** 8 | * @author Mark Esher - you need to add docs and an example 9 | */ 10 | class SiteMonitorJobBuilder { 11 | 12 | String name 13 | String description 14 | List emails 15 | String cronSchedule = 'H/15 * * * *' 16 | 17 | List urls 18 | 19 | Job build(DslFactory factory) { 20 | 21 | def baseJob = new BaseJobBuilder( 22 | name: this.name, 23 | description: this.description, 24 | emails: this.emails, 25 | ).build(factory) 26 | 27 | 28 | baseJob.with { 29 | triggers { 30 | if(cronSchedule) { 31 | cron(cronSchedule) 32 | } 33 | } 34 | 35 | configure { project -> 36 | project / publishers << 'hudson.plugins.sitemonitor.SiteMonitorRecorder' { 37 | mSites { 38 | urls.each { url -> 39 | 'hudson.plugins.sitemonitor.model.Site' { 40 | mUrl url 41 | admitInsecureSslCerts false 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | } 49 | 50 | return baseJob 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/CheckmarxSecurityJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | 4 | import java.util.Map 5 | import javaposse.jobdsl.dsl.DslFactory 6 | import javaposse.jobdsl.dsl.Job 7 | import jenkins.automation.utils.CheckmarxUtils 8 | import jenkins.automation.utils.ScmUtils 9 | 10 | 11 | /** 12 | * Checkmarx Security builder creates a default Checkmarx security build configuration 13 | * 14 | * @param name job name 15 | * @param description job description 16 | * @param emails list of recipients to get notifications 17 | * @param scanRepo a collection of Github repos to scan with Checkmarx 18 | * @param checkmarxConfig a Map to configure the Checkmarx project; see `CheckmarxUtils.checkmarxScan` for a list of valid options 19 | * @see Checkmarx job Example 20 | * 21 | */ 22 | 23 | class CheckmarxSecurityJobBuilder { 24 | 25 | String name 26 | String description 27 | List emails 28 | List scanRepo = [] 29 | Map checkmarxConfig 30 | 31 | Job build(DslFactory factory) { 32 | 33 | def baseJob = new BaseJobBuilder( 34 | name: this.name, 35 | description: this.description, 36 | emails: this.emails 37 | ).build(factory) 38 | 39 | baseJob.with { 40 | multiscm { 41 | ScmUtils.project_repos(delegate, this.scanRepo, false) 42 | } 43 | CheckmarxUtils.checkmarxScan(delegate, this.checkmarxConfig) 44 | } 45 | 46 | return baseJob 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example-jobs/mother_seed_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.utils.ScmUtils 2 | 3 | import java.lang.reflect.Array 4 | 5 | 6 | //def reposToAutomate = RepositoryYamlParser.parseRepositories('some_yaml_file') 7 | def reposToAutomate = [ 8 | [name: "OAH", url: "https://github.com/muchniki/oah-jenkins-automation"], 9 | [name: "PFC", url: "https://github.com/paying-for-college/jenkins"], 10 | [name: "Qu", url: "https://github.com//qu-jenkins-automation"] 11 | 12 | ] 13 | 14 | 15 | 16 | 17 | reposToAutomate.each { project -> 18 | def reposToInclude = [[name: "automation", url: 'https://github.com/imuchnik/jenkins-automation']] //repo containing utils and builders 19 | reposToInclude<< project 20 | listView(project.name) { 21 | columns { 22 | status() 23 | weather() 24 | name() 25 | lastSuccess() 26 | lastFailure() 27 | lastDuration() 28 | buildButton() 29 | } 30 | filterBuildQueue() 31 | filterExecutors() 32 | jobs { 33 | regex(/(?i)(${project.name}.*)/) 34 | } 35 | } 36 | job(project.name + 'SeedJob') { 37 | 38 | multiscm { 39 | ScmUtils.project_repos(delegate, reposToInclude, false) 40 | } 41 | 42 | triggers { 43 | scm 'H/5 * * * *' 44 | } 45 | steps { 46 | 47 | dsl { 48 | external "jobs/**/*.groovy" 49 | // you can also give a pattern here like jobs/**/*Jobs.groovy', which would run 50 | //scripts in jobs directory that end with /*Jobs.groovy 51 | additionalClasspath "automation/src/main/groovy \r\n ${project.name}/src/main/groovy \r\n" 52 | 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/XmlParserHelper.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | import groovy.util.slurpersupport.GPathResult 3 | 4 | /** 5 | * Utility class to aid testing and XML handling 6 | */ 7 | class XmlParserHelper { 8 | 9 | private processNode( Map map, node) { 10 | if ( !map[node.name()] ){ 11 | map[node.name()] = map.getClass().newInstance() 12 | } 13 | Map nodeMap = map[node.name()] 14 | 15 | node.children().each { it -> 16 | if (it.children().size() == 0) { 17 | processLeaf( nodeMap, it) 18 | } else { 19 | processNode( nodeMap, it) 20 | } 21 | } 22 | nodeMap 23 | } 24 | 25 | private processLeaf(Map map, node) { 26 | if ( map[node.name()] == null) { 27 | map[node.name()] = node.text() 28 | } else { 29 | if ( ! (map[node.name()] instanceof List) ) { 30 | map[node.name()] = [ map[node.name()] ] 31 | } 32 | map[node.name()] << node.text() 33 | } 34 | 35 | map[node.name()] 36 | } 37 | /** 38 | * Converts XML string into a HashMap 39 | * very useful in xml string comparison 40 | * @param xmlString XML string to be parsed 41 | * @return Hashmap Hashmap of maps using node names as keys 42 | */ 43 | def parse(String xmlString) { 44 | final GPathResult xml = new XmlSlurper().parseText(xmlString) 45 | final Map map = [ : ] 46 | 47 | xml.children().each { 48 | if ( it.children().size() == 0 ){ 49 | processLeaf map, it 50 | } else { 51 | processNode map, it 52 | } 53 | } 54 | 55 | map 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/SauceConnectJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | 6 | 7 | /** 8 | * Sauce Connect Job builder creates a default Sauce Connect OnDemand plugin configuration 9 | 10 | * 11 | * @param name job name 12 | * @param description job description 13 | * @param webDriverBrowser (Optional) browser to use with Sauce Connect 14 | * @param sauceCredentialId SauceCredential to use for the sauce plugin. Note that when using Gradle build this parameter does not get populated. 15 | * However, this parameter is correctly populated when built inside a Jenkins environment. 16 | * @param additionalOptions (Optional) additional option to use e.g '-v' 17 | * 18 | */ 19 | class SauceConnectJobBuilder { 20 | 21 | String name 22 | String description 23 | List emails 24 | String webDriverBrowser = 'Linuxchrome44' 25 | String sauceCredentialId 26 | String additionalOptions 27 | 28 | /** 29 | * The main job-dsl script that build job configuration xml 30 | * @param DslFactory 31 | * @return Job 32 | */ 33 | Job build(DslFactory factory) { 34 | def baseJob = new BaseJobBuilder( 35 | name: this.name, 36 | description: this.description, 37 | emails: this.emails 38 | ).build(factory) 39 | 40 | baseJob.with { 41 | wrappers { 42 | sauceOnDemand { 43 | enableSauceConnect(true) 44 | webDriverBrowsers(webDriverBrowser) 45 | verboseLogging(true) 46 | useGeneratedTunnelIdentifier(true) 47 | credentials(sauceCredentialId) 48 | additionalOptions ? options (additionalOptions) : null 49 | } 50 | } 51 | } 52 | return baseJob 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/SalesforceAntJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | import jenkins.automation.builders.BaseJobBuilder 6 | 7 | /** 8 | * This is a basic builder for Salesforce deployments using Force.com 9 | * migration tool 10 | * @param name Job Name 11 | * @param description Job description 12 | * @param emails List of emails for build notifications 13 | * @param antTasks List of ant target to execute 14 | * @param repoUrl Git repository to clone 15 | * @param antInstallerName Name of ant installer on the Jenkins server 16 | * 17 | *

18 | * 19 | * @see Salesforce job builder example 21 | 22 | *

23 | */ 24 | class SalesforceAntJobBuilder { 25 | 26 | String name 27 | String description 28 | List emails 29 | def antTasks =[] 30 | String repoUrl 31 | String antInstallerName 32 | 33 | Job build(DslFactory factory) { 34 | 35 | def job = new BaseJobBuilder( 36 | name: this.name, 37 | description: this.description, 38 | emails: this.emails 39 | ).build(factory); 40 | 41 | job.with { 42 | 43 | if(this.repoUrl){ 44 | scm { 45 | git { 46 | remote { 47 | url(this.repoUrl) 48 | } 49 | } 50 | } 51 | } 52 | 53 | steps { 54 | ant { 55 | targets(this.antTasks) 56 | if(this.antInstallerName){ 57 | antInstallation(this.antInstallerName) 58 | } 59 | } 60 | } 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /example-jobs/pipeline_job.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.PipelineJobBuilder 2 | import jenkins.automation.utils.CommonUtils 3 | 4 | 5 | def pipelineScript = """ 6 | pipeline { 7 | agent { label 'master' } 8 | stages { 9 | stage('hello') { 10 | steps { 11 | sh 'echo "Hello World"' 12 | } 13 | } 14 | } 15 | } 16 | """ 17 | 18 | new PipelineJobBuilder( 19 | name: 'Hello Pipeline With Script', 20 | description: 'This is a simple pipeline job', 21 | pipelineScript: pipelineScript, 22 | sandboxFlag: false 23 | ).build(this).with { 24 | logRotator { 25 | numToKeep(365) 26 | } 27 | } 28 | 29 | 30 | new PipelineJobBuilder( 31 | name: 'Hello non-concurrent Pipeline job with builder', 32 | description: 'This is a simple pipeline job that disables concurrent builds', 33 | pipelineScript: pipelineScript, 34 | sandboxFlag: false 35 | ).build(this).with { 36 | logRotator { 37 | numToKeep(365) 38 | } 39 | CommonUtils.disableConcurrentBuilds(delegate) 40 | } 41 | 42 | def simplePipeline = pipelineJob('Hello non-concurrent Pipeline job') { 43 | description('This is a simple pipeline job using straight job-dsl instead of a builder that disables concurrent builds') 44 | CommonUtils.disableConcurrentBuilds(delegate) 45 | definition { 46 | cps { 47 | script(pipelineScript) 48 | } 49 | } 50 | } 51 | 52 | new PipelineJobBuilder( 53 | name: 'Pipeline builder with stages', 54 | description: 'this is a simple pipeline job', 55 | stages: [[ 56 | stageName : 'First stage', 57 | jobName : 'Job 1', 58 | parameters: "[[\$class: 'StringParameterValue', name: 'foo', value: 'bar']]" 59 | ], 60 | [ 61 | stageName: 'Second stage', 62 | jobName : 'Job 2', 63 | ]] 64 | ).build(this).with { 65 | logRotator { 66 | numToKeep(365) 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /example-jobs/common_utils.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.utils.CommonUtils 2 | 3 | def emails = 'foo@example.com, bar@example.com' 4 | def triggers = ['SUCCESS'] 5 | def sendToDevelopers = true 6 | def sendToRequester = false 7 | def includeCulprits = false 8 | def sendToRecipient = false 9 | 10 | job("example") { 11 | publishers { 12 | CommonUtils.addExtendedEmail(delegate, emails = 'foo@example.com, bar@example.com') 13 | } 14 | } 15 | 16 | // override accepts emails as a list. Compatible with builders 17 | job('example email list') { 18 | publishers { 19 | CommonUtils.addExtendedEmail(delegate, emails = ['foo@example.com', 'bar@example.com']) 20 | } 21 | } 22 | 23 | // Override default email triggers. 24 | job('example email with default triggers') { 25 | publishers { 26 | CommonUtils.addExtendedEmail(delegate, emails = ["foo@example.com"], triggers = ['statusChanged']) 27 | } 28 | } 29 | 30 | // Override default email pre-send script, by providing a groovy code 31 | job('example email with presend script') { 32 | publishers { 33 | CommonUtils.addExtendedEmail(delegate, emails, triggers, sendToDevelopers, sendToRequester, includeCulprits, sendToRecipient, "cancel = true") 34 | } 35 | } 36 | 37 | // Override default email pre-send script by providing path to the script file 38 | job('example script by providing path to the script file') { 39 | publishers { 40 | CommonUtils.addExtendedEmail(delegate,, emails, triggers, sendToDevelopers, sendToRequester, includeCulprits, sendToRecipient, "\${SCRIPT, template='path/to/script.groovy'}") 41 | } 42 | } 43 | // Override using configuration map, a little nicer way to invoke the function with named arguments 44 | job('example using configuration map') { 45 | publishers { 46 | CommonUtils.addExtendedEmail(emails: 'foo@example.com', triggers: ['statusChanged'], attachmentPattern: ".csv", delegate,) 47 | } 48 | } 49 | // Override the default content and customize subject 50 | job('example using Override the default content and customize subject') { 51 | publishers { 52 | CommonUtils.addExtendedEmail(emails: 'foo@example.com', content: "foo", subject: "bar", delegate) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /TERMS.md: -------------------------------------------------------------------------------- 1 | As a work of the United States Government, this package (excluding any 2 | exceptions listed below) is in the public domain within the United States. 3 | Additionally, we waive copyright and related rights in the work worldwide 4 | through the [CC0 1.0 Universal public domain dedication][CC0]. 5 | 6 | Software source code previously released under an open source license and then 7 | modified by CFPB staff or its contractors is considered a "joint work" 8 | (see 17 USC § 101); it is partially copyrighted, partially public domain, 9 | and as a whole is protected by the copyrights of the non-government authors and 10 | must be released according to the terms of the original open-source license. 11 | Segments written by CFPB staff, and by contractors who are developing software 12 | on behalf of CFPB are also in the public domain, and copyright and related 13 | rights for that work are waived through the CC0 1.0 Universal dedication. 14 | 15 | For further details, please see the CFPB [Source Code Policy][policy]. 16 | 17 | 18 | ## CC0 1.0 Universal Summary 19 | 20 | This is a human-readable summary of the [Legal Code (read the full text)][CC0]. 21 | 22 | ### No Copyright 23 | 24 | The person who associated a work with this deed has dedicated the work to 25 | the public domain by waiving all of his or her rights to the work worldwide 26 | under copyright law, including all related and neighboring rights, to the 27 | extent allowed by law. 28 | 29 | You can copy, modify, distribute and perform the work, even for commercial 30 | purposes, all without asking permission. See Other Information below. 31 | 32 | ### Other Information 33 | 34 | In no way are the patent or trademark rights of any person affected by CC0, 35 | nor are the rights that other persons may have in the work or in how the 36 | work is used, such as publicity or privacy rights. 37 | 38 | Unless expressly stated otherwise, the person who associated a work with 39 | this deed makes no warranties about the work, and disclaims liability for 40 | all uses of the work, to the fullest extent permitted by applicable law. 41 | When using or citing the work, you should not imply endorsement by the 42 | author or the affirmer. 43 | 44 | [policy]: https://github.com/cfpb/source-code-policy/ 45 | [CC0]: http://creativecommons.org/publicdomain/zero/1.0/legalcode 46 | 47 | 48 | ## Exceptions 49 | 50 | _Source code or other assets that are excluded from the TERMS should be listed 51 | here. These may include dependencies that may be licensed differently or are 52 | not in the public domain._ 53 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/PipelineJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | import jenkins.automation.utils.CommonUtils 6 | 7 | /** 8 | * The basic PipelineJob builder 9 | * 10 | * @param stages list of maps describing the stages; see example linked below for the shape of the map 11 | * @param name job name 12 | * @param sandboxFlag, boolean to enable/disable sandbox, default true. 13 | * @param description job description 14 | * @param pipelineScript optional string containing the raw pipeline script (use instead of `stages`) 15 | * @see Pipeline job builder example 16 | */ 17 | 18 | class PipelineJobBuilder { 19 | List stages 20 | String name 21 | String description 22 | String pipelineScript = "" 23 | Boolean sandboxFlag = true 24 | 25 | /** 26 | * @param DSL factory class, provided by Jenkins when executed from build context 27 | * @return pipeline job 28 | */ 29 | 30 | Job build(DslFactory factory) { 31 | factory.pipelineJob(name) { 32 | it.description this.description 33 | logRotator { 34 | numToKeep(30) 35 | } 36 | def warningText = '' 37 | if (stages && pipelineScript) { 38 | warningText += """ 39 | ansiColor('xterm') { 40 | echo '\\033[31m! `pipelineScript` parameter has no effect because ' + 41 | '`stages` was also supplied; choose one or the other\\033[0m' 42 | } 43 | """ 44 | } 45 | 46 | if (stages) { 47 | def stagesStr = stages.collect { 48 | """ 49 | stage("${it.stageName}") { 50 | build job: "${it.jobName}"${ 51 | it.parameters ? ', parameters: ' + it.parameters : '' 52 | } 53 | } 54 | """ 55 | }.join("\n") 56 | pipelineScript = """ 57 | node { 58 | ${warningText} 59 | ${stagesStr} 60 | } 61 | """.stripIndent() 62 | } 63 | 64 | definition { 65 | cps { 66 | sandbox(sandboxFlag) 67 | script(pipelineScript) 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/JsJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | import jenkins.automation.utils.ScmUtils 6 | 7 | /** 8 | * @param name Job name 9 | * @param description Job description 10 | * @param pollScmSchedule Optional Configure Jenkins to poll changes in SCM. Cron style schedule string. 11 | * @param artifacts Optional Closure defaulted to closure below : 12 | { 13 | pattern("dist/") 14 | fingerprint() 15 | defaultExcludes() 16 | 17 | } 18 | 19 | * @param emails List or String of notification email addresses 20 | * @param node_version Version of nodeJs installation to use. Relies on nodejs plugin and corresponds to the installation 21 | * defined in Jenkins. 22 | * @param repos List of repos to watch 23 | * @param use_versions flag to check out the repo at a specific tag. Applies only to MultiScm block. 24 | * The tag is parsed out from url property appended after '@' sign. 25 | 26 | *

27 | * 28 | * @see JS job builder example 30 | 31 | *

32 | */ 33 | 34 | class JsJobBuilder { 35 | String name 36 | String node_version="5.5.0" 37 | String description 38 | String gitBranch = 'master' 39 | String pollScmSchedule = '@daily' 40 | String tasks 41 | String junitResults = '**/build/test-results/*.xml' 42 | def artifacts = { 43 | pattern("dist/") 44 | fingerprint() 45 | defaultExcludes() 46 | } 47 | def emails 48 | Boolean use_versions 49 | 50 | def repos = []; 51 | 52 | Job build(DslFactory factory) { 53 | 54 | def baseJob = new BaseJobBuilder( 55 | name: this.name, 56 | description: this.description, 57 | emails: this.emails 58 | ).build(factory) 59 | 60 | 61 | baseJob.with { 62 | wrappers { 63 | nodejs(node_version) 64 | } 65 | 66 | multiscm { 67 | ScmUtils.project_repos(delegate, this.repos, use_versions) 68 | } 69 | 70 | if (pollScmSchedule) { 71 | triggers { 72 | scm pollScmSchedule 73 | } 74 | } 75 | 76 | if (artifacts) { 77 | publishers { 78 | archiveArtifacts artifacts 79 | } 80 | } 81 | 82 | } 83 | 84 | baseJob 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /example-jobs/base_jobs.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.automation.builders.BaseJobBuilder 2 | import jenkins.automation.utils.CommonUtils 3 | import jenkins.automation.utils.ScmUtils 4 | 5 | //Note that BaseJobBuilder is simply a wrapper around `job` that also adds some additional configuration such as log rotation and broken build claiming 6 | new BaseJobBuilder( 7 | name: "sample-base-job", 8 | description: "This is a simple job" 9 | ).build(this) 10 | 11 | new BaseJobBuilder( 12 | name: "sample-base-job-with-additional-config", 13 | description: "A job with some additional configurations added" 14 | ).build(this).with { 15 | //how to use CommonUtils 16 | CommonUtils.addInjectGlobalPasswords(delegate) 17 | } 18 | 19 | new BaseJobBuilder( 20 | name: "sample-base-job-with-log-parsing", 21 | description: "A job with log parsing added" 22 | ).build(this).with { 23 | //how to use CommonUtils; pass a custom filename to override the default 24 | CommonUtils.addLogParserPublisher(delegate, "/var/lib/jenkins/some_rules_file.txt") 25 | } 26 | 27 | new BaseJobBuilder( 28 | name: "sample-base-job-with-virtualenv", 29 | description: "A job that creates and activates a python 2.7 virtualenv" 30 | ).build(this).with { 31 | steps { 32 | shell( CommonUtils.python27Virtualenv + """ 33 | # pip install ansible 34 | ls -la 35 | env 36 | echo "Hello world" 37 | """.stripIndent() 38 | ) 39 | } 40 | } 41 | 42 | new BaseJobBuilder( 43 | name: "sample-job-with-credentials-variable-bindings", 44 | description: "This is a job that uses our credentialsBinding helpers" 45 | ).build(this).with { 46 | //you can use one or the other or together if you need to 47 | CommonUtils.addUsernamePasswordCredentials(delegate, "some-credentials-id", "SOME-USERNAME-VAR", "SOME-PASSWORD-VAR") 48 | CommonUtils.addAmazonWebServicesCredentials(delegate, "some-aws-credentials-id", "SOME-ACCESS-KEY-VAR", "SOME-SECRET-KEY-VAR") 49 | } 50 | 51 | new BaseJobBuilder( 52 | name: "sample-base-job-with-performance-publisher", 53 | description: "A job with a performance publisher. It does not include the actual bits that run the load tests" 54 | ).build(this).with { 55 | steps { 56 | shell("echo 'Run jmeter tests here'") 57 | } 58 | CommonUtils.addPerformancePublisher(delegate,failedThresholdPositive=10, failedThresholdNegative=10, unstableThresholdPositive=5, unstableThresholdNegative=5) 59 | } 60 | 61 | def repos = [ 62 | [url: "https://github.com/cfpb/jenkins-automation@2.0"], 63 | [url: "https://github.com/cfpb/collab"], 64 | [url: "https://github.com/cfpb/hubot-aws-cfpb", branch: "main"] 65 | ] 66 | new BaseJobBuilder( 67 | name: "sample-base-job-with-multiscm", 68 | description: "A sample with multiple source control repositories", 69 | ).build(this).with { 70 | multiscm { 71 | ScmUtils.project_repos(delegate, repos, true) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to jenkins-automation 2 | 3 | [![Build Status](https://travis-ci.org/cfpb/jenkins-automation.svg?branch=master)](https://travis-ci.org/cfpb/jenkins-automation) 4 | 5 | Repos automated by Jenkins DSL 6 | 7 | Jenkins automation is a library of helper functions to make continuous integration 8 | and deployment fast, easy and fun. 9 | 10 | We use [Job DSL](https://github.com/jenkinsci/job-dsl-plugin/wiki) to automate. 11 | We have further enhanced the functionality with a set of builders. You can see what they can do and how to use them in our 12 | [API documentation](http://cfpb.github.io/jenkins-automation/). 13 | 14 | Our collection of builders is still growing and we would love your contributions. Please see [How to contribute](CONTRIBUTING.md). 15 | 16 | ### Good place to start learning about job-dsl API: 17 | 18 | http://jenkinsci.github.io/job-dsl-plugin/ 19 | 20 | ### Once you are little more comfortable, try the sandbox: 21 | 22 | http://job-dsl.herokuapp.com/ 23 | 24 | #### If you would like to get started on it for your project a good place to start is our [starter repo](https://github.com/cfpb/jenkins-as-code-starter-project). 25 | 26 | ## Guiding principles 27 | 28 | 1. Make valuable 29 | 2. Make it easy 30 | 3. Make it fast 31 | 4. Make it pretty 32 | 33 | and always [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it), 34 | [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 35 | and [KISS](https://en.wikipedia.org/wiki/KISS_principle) 36 | 37 | ### Configuration added to all jobs 38 | 39 | Any jobs created by using these `Builders` get the following configuration added automatically: 40 | 41 | - Colorized Output (requires AnsiColor plugin) 42 | - Timestamps in the build log (requires Timestamper plugin) 43 | - Broken build claiming (requires Claim plugin) 44 | - Build Failure Analyzer (requires Build Failure Analyzer plugin) 45 | - Max 30 builds retained 46 | 47 | If the `emails` property is set on a job, the job automatically gets configured with an Extended Email block, as well (requires Email Extension plugin) 48 | 49 | ### Commonly used Jenkins Plugins that we support 50 | 51 | - https://wiki.jenkins-ci.org/display/JENKINS/Job+DSL+Plugin 52 | - https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin 53 | - http://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin 54 | - http://wiki.jenkins-ci.org/display/JENKINS/Multiple+SCMs+Plugin 55 | - http://wiki.jenkins-ci.org/display/JENKINS/NodeJS+Plugin 56 | - http://wiki.jenkins-ci.org/display/JENKINS/Clone+Workspace+SCM+Plugin 57 | 58 | 59 | ### Gradle init scripts 60 | 61 | Within the path 62 | 63 | ``` 64 | gradle/init.d/init.gradle 65 | ``` 66 | 67 | There is a file that contains tasks that may be imported into other 68 | gradle builds by using the additional flag `-I` during normal gradle 69 | execution. Currently this file contains a task designed to copy the 70 | jar files of all dependencies of a project into a directory `jac_dependencies`. 71 | This tool may be useful when integrating with tools like 72 | [OWASP](https://www.owasp.org/index.php/OWASP_Dependency_Check) 73 | 74 | 75 | Common execution might be 76 | 77 | ``` 78 | ./gradlew -I gradle/init.d/init.gradle copyDeps 79 | ``` 80 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/BddSecurityJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.Job 5 | 6 | 7 | /** 8 | * Bdd Security builder creates a default BDD security build configuration 9 | 10 | * 11 | * @param name job name 12 | * @param description job description 13 | * @param baseUrl url of the application to scan 14 | * @param bddSecurityRepo repo where BDD Security framework resides 15 | * @param chromedriverPath path to the Chromedriver binary 16 | * 17 | * @see BDD job Example 18 | * 19 | */ 20 | class BddSecurityJobBuilder { 21 | 22 | String name 23 | String description 24 | String baseUrl 25 | List emails 26 | String bddSecurityRepo 27 | String chromedriverPath 28 | 29 | /** 30 | * The main job-dsl script that build job configuration xml 31 | * @param DslFactory 32 | * @return Job 33 | */ 34 | Job build(DslFactory factory) { 35 | def baseJob = new BaseJobBuilder( 36 | name: this.name, 37 | description: this.description, 38 | emails: this.emails 39 | ).build(factory) 40 | 41 | baseJob.with { 42 | scm { 43 | git { 44 | remote { 45 | url(bddSecurityRepo) 46 | } 47 | branch('*/master') 48 | extensions { 49 | cloneOptions { 50 | shallow() 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | baseJob.with { 58 | steps { 59 | shell("""umask 002 60 | /usr/bin/Xvfb :\$BUILD_NUMBER -ac -screen 0 1024x768x24 & 61 | sleep 10 62 | export DISPLAY=:\$BUILD_NUMBER 63 | 64 | echo \${WORKSPACE} 65 | 66 | cd \${WORKSPACE} 67 | sed -i 's/.*<\\/zapPath>/zap\\/zap.sh<\\/zapPath>/g' config.xml 68 | sed -i 's/<\\/baseUrl>/${baseUrl}<\\/baseUrl>/g' config.xml 69 | 70 | sed -i 's/Chrome<\\/defaultDriver>/g' config.xml 71 | 72 | ./gradlew clean test""") 73 | } 74 | } 75 | 76 | baseJob.with { 77 | 78 | configure { project -> 79 | project / publishers / 'com.github.bogdanlivadariu.jenkins.reporting.cucumber.CucumberTestReportPublisher' { 80 | 'fileIncludePattern'('build/reports/cucumber/all_tests.json') 81 | } 82 | } 83 | 84 | configure { project -> 85 | project / publishers / 'hudson.tasks.junit.JUnitResultArchiver' { 86 | 'testResults'('build/reports/junit/all_tests.xml') 87 | 'healthScaleFactor'('1.0') 88 | } 89 | } 90 | } 91 | 92 | return baseJob 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /opensource-checklist.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: base 3 | title: "Open Source Checklist" 4 | --- 5 | 6 | # Open Source Check List 7 | 8 | Prior to releasing a project to GitHub.com, walk through these items and ensure they are addressed. 9 | 10 | - **Has PII been removed?** 11 | - Use [Clouseau](https://github.com/virtix/clouseau) for scanning source code. 12 | - For an Open Source Release, attach the Clouseau output. 13 | - If there are images, visually inspect each image to ensure there is no CFPB-specific information. 14 | 15 | - **Have security vulnerabilities been remediated?** 16 | - Use the [OWASP Top 10](https://www.owasp.org/index.php/Top_10_2013) 17 | - [National Vulnerability Database](http://nvd.nist.gov/) 18 | - [SANS Swat Checklist](http://www.securingthehuman.org/developer/swat) 19 | 20 | - **Are we including any other open source products? If so, is there any conflict with our public domain release?** 21 | 22 | - **Is our `TERMS.md` included?** 23 | 24 | - **Is a `CHANGELOG.md` present and does it contain structured, consistently formatted recent history?** 25 | - See and 26 | - Some Inspiration: 27 | 28 | - **Are instructions for contributing included (`CONTRIBUTING.md`)?** 29 | 30 | - **Are installation instructions clearly written in the `README` _and_ tested on a clean machine?** 31 | 32 | - **Are all dependencies described in the `README`, `requirements.txt`, and/or `buildout.cfg`?** 33 | 34 | - **Are the API docs generated?** 35 | 36 | - **Are there unit tests?** 37 | 38 | - **If appplicable and possible, is it set up in TravisCI?** 39 | 40 | - **Have multiple people reviewed the code?** 41 | 42 | - **Is there a screenshot in the `README`, if applicable?** 43 | 44 | 45 | ## Copy this version to paste into a GitHub issue with live checkboxes: 46 | 47 | ~~~ 48 | - [ ] **Has PII been removed?** 49 | - Use [Clouseau](https://github.com/virtix/clouseau) for scanning source code. 50 | - If there are images, visually inspect each image to ensure there is no CFPB-specific information. 51 | - [ ] **Have security vulnerabilities been remediated?** 52 | - [ ] **Are we including any other open source products? If so, is there any conflict with our public domain release?** 53 | - [ ] **Is our `TERMS.md` included?** 54 | - [ ] **Is a `CHANGELOG.md` present and does it contain structured, consistently formatted recent history?** 55 | - [ ] **Are instructions for contributing included (`CONTRIBUTING.md`)?** 56 | - [ ] **Are installation instructions clearly written in the `README` _and_ tested on a clean machine?** 57 | - [ ] **Are all dependencies described in the `README`, `requirements.txt`, and/or `buildout.cfg`?** 58 | - [ ] **Are the API docs generated?** 59 | - [ ] **Are there unit tests?** 60 | - [ ] **If applicable and possible, is it set up in TravisCI?** 61 | - [ ] **Have multiple people reviewed the code?** 62 | - [ ] **Is there a screenshot in the `README`, if applicable?** 63 | ~~~ 64 | 65 | ---- 66 | 67 | 68 | ## Take a look at the following projects as good models to follow: 69 | 70 | - [https://github.com/cfpb/qu](https://github.com/cfpb/qu) 71 | - [https://github.com/cfpb/idea-box](https://github.com/cfpb/idea-box) 72 | - [https://github.com/cfpb/hmda-tool](https://github.com/cfpb/hmda-tools) 73 | - [https://github.com/cfpb/django-cache-tools](https://github.com/cfpb/django-cache-tools) 74 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/ScmUtils.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | /** 4 | * Utility class to provide nicer, terser DSL for common tasks 5 | */ 6 | class ScmUtils { 7 | 8 | /*** 9 | * 10 | * Utility method to create a multiscm block from a list of repos. 11 | * @see example 12 | * @param context A reference to the job object being modified 13 | * @param repos List of repo maps. Each repo object must include url property and 14 | * optionally sub_directory(checkout subdirectory), shallow(shallow clone), branch property, and 15 | * disable_submodule properties. 16 | * @param use_versions Flag to check out the repo at a specific tag. 17 | * The tag is parsed out from url property appended after {@literal @} sign. 18 | */ 19 | static void project_repos(context, repos, use_versions = true) { 20 | Boolean disable_submodule = false 21 | context.with { 22 | repos.each { repo -> 23 | def parsed_out_url = repo.url.tokenize('@') 24 | 25 | def parsed_url = parsed_out_url[0] 26 | 27 | def version = parsed_out_url[1] 28 | 29 | disable_submodule = (repo.disable_submodule ?: false) 30 | 31 | git { 32 | remote { 33 | url(parsed_url) 34 | } 35 | if (use_versions && version != null) { 36 | branch "*/tags/$version" 37 | } else if (repo.branch) { 38 | branch repo.branch 39 | } else { 40 | branch "master" 41 | } 42 | if (disable_submodule) { 43 | configure { node -> 44 | node / 'extensions' / 'hudson.plugins.git.extensions.impl.SubmoduleOption' { 45 | disableSubmodules disable_submodule 46 | } 47 | } 48 | } 49 | extensions { 50 | if (repo.sub_directory) { 51 | relativeTargetDirectory(repo.sub_directory) 52 | } 53 | if (repo.shallow && repo.shallow != null) { 54 | cloneOptions { 55 | shallow() 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | /*** 65 | * 66 | * Utility method to do git shallow-clones easily. 67 | * @see example 68 | * @param context A reference to the job object being modified 69 | * @param gitUrl A string containing a git repo URL 70 | * @param gitBranch Optional branch to check-out; defaults to master 71 | */ 72 | static void shallowGit(context, gitUrl, gitBranch = "master") { 73 | context.with { 74 | git { 75 | remote { 76 | url gitUrl 77 | } 78 | 79 | branch gitBranch 80 | 81 | extensions { 82 | cloneOptions { 83 | shallow() 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/builders/MultibranchPipelineJobBuilder.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.builders 2 | 3 | import javaposse.jobdsl.dsl.DslFactory 4 | import javaposse.jobdsl.dsl.jobs.MultibranchWorkflowJob 5 | 6 | 7 | /* 8 | NB: if you are polling via the GitHub API (github.com or GHE) and 9 | want to use webhooks with a multibranch pipeline, but the webhooks 10 | are not being created, it may be because you need to update Jenkins 11 | plugins; ensure that the 'Branch API Plugin' and the 'GitHub Branch 12 | Source Plugins' are both up to date 13 | */ 14 | 15 | 16 | /** 17 | * Multibranch Pipeline Job builder creates a set of Pipeline projects 18 | * according to detected branches in a Git repository 19 | * @param name job name 20 | * @param description description for the folder 21 | * @param branchSource the method used to access the git repo; see 22 | * BranchSourceType for options 23 | * @param gitCredentials Jenkins git credentials GUID used to scan branches, etc 24 | * @param ghOwner name of the organization or user when branchSource is 'GitHub' 25 | * @param ghRepo name of the git repo when branchSource is 'GitHub' 26 | * @param ghApiEndpoint (optional) the URL of the GitHub API endpoint 27 | * when branchSource is 'GitHub'; when pointing to a GitHub Enterprise API URL, 28 | * you will first need to make the corresponding configurations in your Jenkins's 29 | * 'GitHub' and 'GitHub Enterprise Servers' sections on the 'Configure System' 30 | * page (otherwise a custom non-github.com API endpoint will not work) 31 | * @param gitRemote the URL of the git repo when branchSource is 'git' 32 | * @param oldNumToKeep (optional) Number of builds to keep after a git branch 33 | * has been removed 34 | */ 35 | class MultibranchPipelineJobBuilder { 36 | String name 37 | String description 38 | BranchSourceType branchSource 39 | String gitCredentials 40 | String ghOwner 41 | String ghRepo 42 | String ghApiEndpoint 43 | String gitRemote 44 | int oldNumToKeep = 10 45 | 46 | enum BranchSourceType { 47 | /** 48 | * use git, instead of the GitHub API, to scan for branches, etc 49 | */ 50 | GIT, 51 | 52 | /** 53 | * use a GitHub API (GitHub Enterprise or github.com) to scan for branches, etc 54 | */ 55 | GITHUB, 56 | } 57 | 58 | MultibranchWorkflowJob build(DslFactory factory) { 59 | factory.multibranchPipelineJob(name) { 60 | it.description this.description 61 | 62 | branchSources { 63 | switch (branchSource) { 64 | case BranchSourceType.GIT: 65 | git { 66 | id(name) 67 | remote(gitRemote) 68 | credentialsId(gitCredentials) 69 | } 70 | break 71 | case BranchSourceType.GITHUB: 72 | github { 73 | id(name) 74 | scanCredentialsId(gitCredentials) 75 | repoOwner(ghOwner) 76 | repository(ghRepo) 77 | apiUri(ghApiEndpoint) 78 | } 79 | break 80 | default: 81 | throw new Exception("Unhandled BranchSource type") 82 | } 83 | } 84 | 85 | orphanedItemStrategy { 86 | discardOldItems { 87 | numToKeep(oldNumToKeep) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/rest/RestApiJobManagement.groovy: -------------------------------------------------------------------------------- 1 | // Credit: https://github.com/sheehan/job-dsl-gradle-example 2 | 3 | package jenkins.automation.rest 4 | 5 | import groovyx.net.http.HttpResponseDecorator 6 | import groovyx.net.http.RESTClient 7 | import javaposse.jobdsl.dsl.* 8 | import org.apache.http.entity.ContentType 9 | 10 | class RestApiJobManagement extends MockJobManagement { 11 | 12 | final RESTClient restClient 13 | private boolean crumbHeaderSet = false 14 | 15 | RestApiJobManagement(String baseUrl) { 16 | if (!baseUrl.endsWith("/")) { 17 | baseUrl += "/" 18 | } 19 | restClient = new RESTClient(baseUrl) 20 | restClient.handler.failure = { it } 21 | } 22 | 23 | void setCredentials(String username, String password) { 24 | crumbHeaderSet = false 25 | restClient.headers['Authorization'] = 'Basic ' + "$username:$password".bytes.encodeBase64() 26 | } 27 | 28 | @Override 29 | String getConfig(String jobName) throws JobConfigurationNotFoundException { 30 | String xml = fetchExistingXml(jobName) 31 | if (!xml) { 32 | throw new JobConfigurationNotFoundException(jobName) 33 | } 34 | 35 | xml 36 | } 37 | 38 | @Override 39 | boolean createOrUpdateConfig(Item item, boolean ignoreExisting) throws NameNotProvidedException { 40 | createOrUpdateConfig(item.name, item.xml, ignoreExisting, false) 41 | } 42 | 43 | @Override 44 | void createOrUpdateView(String viewName, String config, boolean ignoreExisting) throws NameNotProvidedException, ConfigurationMissingException { 45 | createOrUpdateConfig(viewName, config, ignoreExisting, true) 46 | } 47 | 48 | boolean createOrUpdateConfig(String name, String xml, boolean ignoreExisting, boolean isView) throws NameNotProvidedException { 49 | boolean success 50 | String status 51 | 52 | String existingXml = fetchExistingXml(name, isView) 53 | if (existingXml) { 54 | if (ignoreExisting) { 55 | success = true 56 | status = 'ignored' 57 | } else { 58 | success = update(name, xml, isView) 59 | status = success ? 'updated' : 'update failed' 60 | } 61 | } else { 62 | success = create(name, xml, isView) 63 | status = success ? 'created' : 'create failed' 64 | } 65 | 66 | println "$name - $status" 67 | success 68 | } 69 | 70 | @Override 71 | InputStream streamFileInWorkspace(String filePath) throws IOException { 72 | new File(filePath).newInputStream() 73 | } 74 | 75 | @Override 76 | String readFileInWorkspace(String filePath) throws IOException { 77 | new File(filePath).text 78 | } 79 | 80 | private boolean create(String name, String xml, boolean isView) { 81 | String job 82 | String path 83 | if (name.contains('/')) { 84 | int index = name.lastIndexOf('/') 85 | String folder = name[0..(index - 1)] 86 | job = name[(index + 1)..-1] 87 | path = getPath(folder, isView) + '/createItem' 88 | } else { 89 | job = name 90 | path = isView ? 'createView' : 'createItem' 91 | } 92 | 93 | setCrumbHeader() 94 | HttpResponseDecorator resp = restClient.post( 95 | path: path, 96 | body: xml, 97 | query: [name: job], 98 | requestContentType: 'application/xml; charset=UTF-8' 99 | ) 100 | 101 | resp.status == 200 102 | } 103 | 104 | private boolean update(String name, String xml, boolean isView) { 105 | setCrumbHeader() 106 | HttpResponseDecorator resp = restClient.post( 107 | path: getPath(name, isView) + '/config.xml', 108 | body: xml, 109 | requestContentType: 'application/xml; charset=UTF-8' 110 | ) 111 | 112 | resp.status == 200 113 | } 114 | 115 | private String fetchExistingXml(String name, boolean isView) { 116 | setCrumbHeader() 117 | HttpResponseDecorator resp = restClient.get( 118 | contentType: ContentType.DEFAULT_TEXT, 119 | path: getPath(name, isView) + '/config.xml', 120 | headers: [Accept: 'application/xml'], 121 | ) 122 | resp?.data?.text 123 | } 124 | 125 | private static String getPath(String name, boolean isView) { 126 | if (name.startsWith('/')) { 127 | return '/' + getPath(name[1..-1], isView) 128 | } 129 | isView ? "view/$name" : "job/${name.replaceAll('/', '/job/')}" 130 | } 131 | 132 | private setCrumbHeader() { 133 | if (crumbHeaderSet) 134 | return 135 | 136 | HttpResponseDecorator resp = restClient.get(path: 'crumbIssuer/api/xml') 137 | if (resp.status == 200) { 138 | restClient.headers[resp.data.crumbRequestField] = resp.data.crumb 139 | } 140 | crumbHeaderSet = true 141 | } 142 | } -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/GhUtils.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | import java.util.Map 4 | 5 | /** 6 | * Utility class for internally hosted GitHub Enterprise interaction 7 | */ 8 | 9 | class GhUtils { 10 | 11 | /** 12 | * 13 | * Utility method to watch a GitHub Enterprise or GitHub.com project and trigger on PR creation.
14 | * This is a wrapper for the GitHub Pull Request Builder Plugin. 15 | * @see example 16 | * @param context delegate 17 | * @param ghPrConfig A Map of configuration for the PR builder. 18 | Valid keys: 19 | *
ghProject The GitHub project name, e.g. username/reponame 20 | *
ghHostname The GitHub hostname, i.e. github.org.tld, 21 | * or github.com 22 | *
ghAuthId the Jenkins credential ID for GitHub authentication 23 | * see the plugin docs 24 | *
ghPermitAll optional Whether or not to allow all 25 | * contributors to build a PR. Defaults to false. 26 | *
ghPrHooks optional Whether or not to install webhooks 27 | * in the remote repo. Defaults to true. 28 | *
ghPrCron optional For use when ghPrHooks is false. 29 | * Uses standard cron syntax. 30 | *
ghPrOrgList optional A string of organizations to whitelist for 31 | * PRs. Defaults to 'jenkins'. 32 | *
ghPrStatusContext optional A context for this job's tests as it 33 | * will appear on PR issues in GH's UI. Defaults to 'Tests from GitHub PR Builder.' 34 | *
ghPrResultMessage optional A Map whose keys are the build 35 | * result (SUCCESS, FAILURE, or ERROR) and value is the message posted to GH as a result. 36 | */ 37 | static void ghPrWatcher(context, Map ghPrConfig) { 38 | def defaults = ghPrConfigDefaults 39 | context.with { 40 | scm { 41 | git { 42 | remote { 43 | github( 44 | ghPrConfig.get('ghProject'), 45 | 'https', 46 | ghPrConfig.get('ghHostname') 47 | ) 48 | refspec('+refs/pull/*:refs/remotes/origin/pr/*') 49 | } 50 | branch('${sha1}') 51 | } 52 | } 53 | configure { node -> 54 | node / 'triggers' / 'org.jenkinsci.plugins.ghprb.GhprbTrigger'( plugin: 'ghprb') { 55 | // Avoid 'null' in these blocks 56 | spec(ghPrConfig.get('ghPrCron', '')) 57 | cron(ghPrConfig.get('ghPrCron', '')) 58 | 59 | allowMembersOfWhitelistedOrgsAsAdmin(true) 60 | orgslist(ghPrConfig.get('ghPrOrgsList', defaults.ghPrOrgsList)) 61 | permitAll(ghPrConfig.get('ghPermitAll', defaults.ghPermitAll)) 62 | useGitHubHooks(ghPrConfig.get('ghPrHooks', defaults.ghPrHooks)) 63 | gitHubAuthId(ghPrConfig.get('ghAuthId')) 64 | extensions { 65 | 'org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus' { 66 | commitStatusContext( 67 | ghPrConfig.get( 68 | 'ghPrStatusContext', 69 | defaults.ghPrStatusContext 70 | ) 71 | ) 72 | triggeredStatus() 73 | startedStatus() 74 | statusUrl() 75 | addTestResults(true) 76 | def ghPrMessage = ghPrConfig.get( 77 | 'ghPrResultMessage', 78 | defaults.ghPrResultMessage 79 | ) 80 | def confCompletedStatus = completedStatus() 81 | ghPrMessage.each{ buildStatus, ghMessage -> 82 | confCompletedStatus << 'org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage' { 83 | delegate.message(ghMessage) 84 | delegate.result(buildStatus) 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | static Map ghPrConfigDefaults = [ 95 | ghPermitAll: false, 96 | ghPrHooks: true, 97 | ghPrOrgsList: 'jenkins', 98 | ghPrStatusContext: 'Tests from GitHub PR Builder', 99 | ghPrResultMessage: [ 'SUCCESS': 'Tests from PR builder completed successfully' ], 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/PluginUtils.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | /** 4 | * Common block of reusable configure blocks for various plugins support 5 | 6 | */ 7 | class PluginUtils { 8 | 9 | /** 10 | * 11 | * @param context delegate 12 | * @param SqsEndpoint AWS SQS queue url 13 | * @param buildServerUrlValue jenkins url 14 | * @param roomId optional (will read from global config if blank) used by tools like https://github.com/catops/hubot-sqs 15 | * @param customSQSMessageValue Custom message to be posted to AWS SQS queue. 16 | * @param includeCustomSQSMessageFlag Option to include custom message. Default value: false 17 | * @param startNotificationFlag Option to be notified when job starts. Default value: false 18 | * @param notifySuccessFlag Option to be notified when job succeeds. Default value: false 19 | * @param notifyAbortedFlag Option to be notified when job is aborted. Default value: false 20 | * @param notifyNotBuiltFlag Option to be notified when job is not built. Default value: false 21 | * @param notifyUnstableFlag Option to be notified when job is unstable. Default value: false 22 | * @param notifyFailureFlag Option to be notified when job fails. Default value: true 23 | * @param notifyBackToNormalFlag Option to be notified when job returns to SUCCESS state. Default value: false 24 | * @param notifyRepeatedFailureFlag Option to be notified when job fails repeatedly. Default value: false 25 | * @param sqsIncludeTestSummaryFlag Option to include test summary. Default value: false 26 | 27 | * @see SQS Support 28 | 29 | */ 30 | static void addSQSNotification(context, 31 | SqsEndpoint, 32 | buildServerUrlValue, 33 | roomId, 34 | customSQSMessageValue = "", 35 | includeCustomSQSMessageFlag = false, 36 | startNotificationFlag = false, 37 | notifySuccessFlag = false, 38 | notifyAbortedFlag = false, 39 | notifyNotBuiltFlag = false, 40 | notifyUnstableFlag = false, 41 | notifyFailureFlag = true, 42 | notifyBackToNormalFlag = false, 43 | notifyRepeatedFailureFlag = false, 44 | sqsIncludeTestSummaryFlag = false) { 45 | 46 | context.configure { Node project -> 47 | project / publishers << 'jenkins.plugins.sqs.SQSNotifier'(plugin: "sqs-notification") { 48 | endpoint(SqsEndpoint) 49 | buildServerUrl buildServerUrlValue 50 | room(roomId) 51 | startNotification startNotificationFlag 52 | notifySuccess notifySuccessFlag 53 | notifyAborted notifyAbortedFlag 54 | notifyNotBuilt notifyNotBuiltFlag 55 | notifyUnstable notifyUnstableFlag 56 | notifyFailure notifyFailureFlag 57 | notifyBackToNormal notifyBackToNormalFlag 58 | notifyRepeatedFailure notifyRepeatedFailureFlag 59 | sqsIncludeTestSummary sqsIncludeTestSummaryFlag 60 | includeCustomSQSMessage includeCustomSQSMessageFlag 61 | customSQSMessage customSQSMessageValue 62 | 63 | } 64 | } 65 | } 66 | 67 | static /** 68 | * Utility function for adding newrelic notifier plugin 69 | * @param context Publishers in this case, passed as a delegate 70 | * @param api_key newRelic api key 71 | * @param applicationId Application Id 72 | * @param jobName job Name 73 | * @param changeLog Changelog messages 74 | * @param user Jenkins user 75 | * @param revision revision number 76 | * 77 | * 78 | * @see New Relic 79 | */ 80 | void addNewRelicSupport(context, api_key, application_Id, jobName, changeLog, userId, rev) { 81 | 82 | context.configure { Node project -> 83 | project / publishers << 'org.jenkinsci.plugins.newrelicnotifier.NewRelicDeploymentNotifier'(plugin: "newrelic-deployment-notifier") { 84 | 85 | client(class: "org.jenkinsci.plugins.newrelicnotifier.api.NewRelicClientImpl") 86 | notifications { 87 | 'org.jenkinsci.plugins.newrelicnotifier.DeploymentNotificationBean' { 88 | apiKey api_key 89 | applicationId application_Id 90 | description jobName 91 | revision rev 92 | changelog changeLog 93 | user userId 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * 102 | * @param context Project passed from root of DSL 103 | * @param triggerLabel Restricted node label 104 | * @param triggerQuietPeriod Delay before starting job 105 | 106 | * @see example 107 | 108 | */ 109 | static void startupTrigger(context, 110 | String triggerLabel = 'master', 111 | int triggerQuietPeriod = 0) { 112 | context.configure { Node project -> 113 | project / 'triggers' / 'org.jvnet.hudson.plugins.triggers.startup.HudsonStartupTrigger'(plugin: "startup-trigger-plugin") { 114 | spec() 115 | label(triggerLabel) 116 | quietPeriod(triggerQuietPeriod) 117 | } 118 | } 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # CFPB Open Source Code of Conduct 2 | 3 | ## Introduction 4 | 5 | The [Consumer Financial Protection Bureau](https://www.consumerfinance.gov) (CFPB) is committed to 6 | building a safe, welcoming, harassment-free culture for everyone. We do not merely want an 7 | environment that is free from hostility, we want one that is actively welcoming and inclusive. We 8 | want our team, our workplace culture, and our open source community to reflect and celebrate the 9 | diversity of the people we serve. 10 | 11 | This Code of Conduct summarizes federal anti-harassment law and CFPB policy. 12 | 13 | ## Scope 14 | 15 | We expect everyone on the CFPB team, and those contributing to our open source community, to exhibit 16 | these behaviors and abide by applicable federal laws and CFPB policies. In addition, we expect 17 | everyone within CFPB spaces to exhibit these behaviors and refrain from behavior prohibited by 18 | anti-harassment laws and federal policies on harassment. These spaces include: 19 | 20 | - CFPB’s physical offices 21 | - CFPB events and meetings 22 | - All of CFPB’s online forums and virtual collaboration tools, including code repositories 23 | 24 | 25 | ## What we strive for 26 | 27 | At the CFPB, we strive to create a welcoming and inclusive culture that empowers people to best protect 28 | the financial interests of all consumers. That kind of atmosphere requires an open exchange of ideas 29 | balanced by thoughtful guidelines. Examples of behavior that contributes to a positive environment 30 | for our open source community include: 31 | 32 | - Demonstrating empathy and kindness toward other people 33 | - Being respectful of differing opinions, viewpoints, and experiences 34 | - Giving and gracefully accepting constructive feedback 35 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 36 | - Focusing on what is best not just for us as individuals, but for the overall community and public 37 | 38 | ## Unacceptable behavior 39 | 40 | To help understand the kinds of behaviors that are illegal or run counter to the culture we seek to 41 | foster, we've listed some actions below that violate federal law and CFPB policy. We've also included 42 | steps to take if you encounter behavior that runs contrary to this policy. 43 | 44 | The CFPB Policy Statement on Equal Employment Opportunity and Workplace Harassment forbids 45 | discrimination or harassment based on: 46 | 47 | - Race 48 | - Color 49 | - Religion 50 | - Sex (including pregnancy, sexual orientation, transgender status, gender identity or expression, gender non-conformity, or sex stereotyping of any kind) 51 | - National origin 52 | - Disability 53 | - Age (40 years or older) 54 | - Genetic information 55 | - Parental status 56 | - Political affiliation 57 | - Marital status 58 | - Uniformed status 59 | - Membership in a labor organization or union activities 60 | - Prior equal employment opportunity (EEO) or whistleblower activity 61 | - Any other factor unrelated to your merit 62 | 63 | The policy also forbids harassing conduct, which includes unwelcome conduct based on any (or a combination of) protected traits or characteristics. Such conduct may take the form of any of the following: 64 | 65 | - Offensive jokes, comments, objects, or pictures 66 | - Questions about a person’s identity (e.g., disability status, gender identity, sexual orientation, national origin, etc.) 67 | - Undue attention 68 | - Ridicule or mockery 69 | - Insults or put-downs 70 | - Touching/physical contact 71 | - Slurs or epithets 72 | - Threats or other forms of intimidation 73 | - Physical or sexual assault 74 | 75 | ## Reporting violations 76 | 77 | If you are a CFPB employee, former CFPB employee, or job applicant to CFPB and believe you have been 78 | discriminated against or harassed on the basis of race, color, religion, sex (including pregnancy, 79 | sexual orientation, transgender status, gender identity or expression, gender non-conformity, or sex 80 | stereotyping of any kind), national origin, disability, age (40 years or older), genetic information, 81 | parental status, or retaliated against for prior Equal Employment Opportunity (EEO) activity, contact the CFPB’s Office of Civil Rights. 82 | 83 | CFPB_EEO@consumerfinance.gov 84 | 85 | (202) 435-9EEO 86 | (855) 233-0362 87 | TTY: (202) 435-9742 88 | 89 | Office of Civil Rights 90 | Consumer Financial Protection Bureau 91 | 1700 G Street, NW 92 | Washington, D.C. 20552 93 | 94 | For help filing a complaint about discrimination on the basis of marital status, political 95 | affiliation, or any other non-merit factor, or for claims of retaliation for [whistleblower activity](https://www.consumerfinance.gov/office-civil-rights/whistleblowers/), contact the [Office of Special Counsel](https://www.osc.gov/) or the [Merit Systems Protection Board](https://www.mspb.gov/). 96 | 97 | For help filing a complaint about discrimination on the basis of uniformed status, you may contact 98 | the [Veterans’ Employment and Training Service (VETS)](https://www.dol.gov/vets/) at the Department of Labor, the [Merit Systems Protection Board](https://www.mspb.gov/), or the [Office of Special Counsel](https://osc.gov/), depending on the circumstances. 99 | 100 | For help filing a complaint about discrimination on the basis of membership in a labor organization, 101 | you may contact the [Federal Labor Relations Authority](https://flra.gov/) or your union (if applicable). 102 | 103 | ### Equal employment opportunity policy 104 | 105 | For more information about the CFPB’s equal employment opportunity (EEO) policies and procedures visit https://www.consumerfinance.gov/office-civil-rights/eeo-policy-and-reports/ 106 | 107 | ## Credits 108 | 109 | The CFPB is greatly appreciative of the multiple sources that we drew from to build this Code of Conduct, including: 110 | 111 | - [The Technology Transformation Services (TTS) Code of Conduct](https://18f.gsa.gov/code-of-conduct/) 112 | - [The Contributor Covenant](https://www.contributor-covenant.org/) 113 | - [Code for America Code of Conduct](https://github.com/codeforamerica/codeofconduct) 114 | - [Ada Initiative: HOWTO design a code of conduct for your community](https://adainitiative.org/2014/02/18/howto-design-a-code-of-conduct-for-your-community/) 115 | - [Geek Feminism Code of Conduct](https://geekfeminismdotorg.wordpress.com/about/code-of-conduct/) 116 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/CheckmarxUtils.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | 4 | import java.util.Map 5 | 6 | 7 | class CheckmarxUtils { 8 | 9 | /** 10 | * Reusable block to add Checkmarx scanning to a project 11 | * 12 | * @param context A reference to the job object being modified 13 | * (typically via delegate) 14 | * @param cxConfig A Map used to configure the Checkmarx project. 15 | * Valid keys: 16 | *
projectName: required, specifies the name of the 17 | * Checkmarx project 18 | *
groupId: required, specifies group ID, which is 19 | * actually the team ID, and which can be gotten by 20 | * browser-inspecting the 'Team' select box for an existing 21 | * Checkmarx job in the Jenkins UI 22 | *
preset: optional, defaults to "Checkmarx Default", 23 | * the ID of the Checkmarx preset configuration to use; this can be 24 | * gotten by browser-inspecting the 'Preset' select box for an existing 25 | * Checkmarx job in the Jenkins UI 26 | *
useOwnServerCredentials: optional, defaults to 27 | * false, specifies whether to use the global or per-job 28 | * Checkmarx server URL/credentials 29 | *
serverUrl: optional, specifies the Checkmarx server 30 | * URL to be used 31 | *
username: optional, specifies the Checkmarx username 32 | *
password: optional, specifies the Checkmarx password 33 | *
excludeFolders: optional, see checkmarxConfigDefaults 34 | * for the default, specifies the directories to exclude from scanning 35 | *
filterPattern: optional, see checkmarxConfigDefaults 36 | * for the default, specifies globs to filter out 37 | *
incremental: optional, defaults to true, 38 | * specifies whether scan should be incremental 39 | *
fullScanCycle: optional, defaults to 10, 40 | * specifies how frequently to run a full scan when normally doing 41 | * incremental scans 42 | *
comment: optional, additional comments to be adding 43 | * to the scan results 44 | *
vulnerabilityThresholdEnabled: optional, defaults to 45 | * true, fail the build if the number of vulnerabilities 46 | * exceeds the thresholds set 47 | *
highThresholdDefault: optional, defaults to 48 | * 1, sets the threshold for max number of 'high' 49 | * vulnerabilities 50 | *
mediumThresholdDefault: optional, defaults to 51 | * 2, sets the threshold for max number of 'medium' 52 | * vulnerabilities 53 | *
lowThresholdDefault: optional, defaults to 54 | * 3, set the threshold for max number of 'low' 55 | * vulnerabilities 56 | *
osaEnabled: optional, defaults to 57 | * false, define whether or not to run 58 | * OSA dependency scans 59 | *
osaIncludePattern: optional, defaults to 60 | * **/osa_dependencies/**, set the pattern that OSA 61 | * scanning will search to package and scan libraries 62 | *
osaExcludePattern: optional, defaults to 63 | * "", set the pattern OSA will exempt from 64 | * packaging and scanning when running OSA 65 | *
osaHighThreshold: optional, defaults to 66 | * 1, threshold for 'medium vulnerability' 67 | * OSA results required for FAILURE 68 | *
osaMediumThreshold: optional, defaults to 69 | * 1, threshold for 'medium vulnerability' 70 | * OSA results required fo build FAILURE 71 | *
osaLowThreshold: optional, defaults to 72 | * 1, threshold for 'low vulnerability' 73 | * OSA results required for build FAILURE 74 | * @see Checkmarx job Example 75 | * 76 | */ 77 | 78 | 79 | static void checkmarxScan(context, Map cxConfig) { 80 | assert cxConfig.projectName != null 81 | assert cxConfig.groupId != null 82 | 83 | def defaults = checkmarxConfigDefaults 84 | context.with { 85 | 86 | configure { 87 | it / builders / "com.checkmarx.jenkins.CxScanBuilder" { 88 | 89 | "useOwnServerCredentials"( 90 | cxConfig.get( 91 | "useOwnServerCredentials", 92 | defaults.useOwnServerCredentials 93 | ) 94 | ) 95 | 96 | if (cxConfig.useOwnServerCredentials) { 97 | "serverUrl"(cxConfig.serverUrl) 98 | "username"(cxConfig.username) 99 | "password"(cxConfig.password) 100 | } 101 | 102 | "projectName"(cxConfig.projectName) 103 | "groupId"(cxConfig.groupId) // really the Checkmarx "team" 104 | "preset"(cxConfig.get("preset", defaults.preset)) 105 | 106 | // TODO figure out whether this does anything 107 | "presetSpecified"(false) 108 | 109 | // TODO probably this should be configurable 110 | "exclusionsSetting"("job") 111 | 112 | "excludeFolders"( 113 | cxConfig.get("excludeFolders", defaults.excludeFolders) 114 | ) 115 | "filterPattern"( 116 | cxConfig.get("filterPattern", defaults.filterPattern) 117 | ) 118 | 119 | "incremental"( 120 | cxConfig.get("incremental", defaults.incremental) 121 | ) 122 | 123 | /* 124 | From the Checkmarx docs: 125 | When using incremental scan as part of CI/CD (for example as 126 | part of a build process) you need to make sure that a full 127 | scan is performed every X amount of incremental scans. 128 | Otherwise the changes will aggregate and when more than 7% 129 | of the code has changed CxSAST will either run a full scan 130 | or fail the scan, depending on the configuration. 131 | */ 132 | "fullScansScheduled"(true) 133 | "fullScanCycle"( 134 | cxConfig.get("fullScanCycle", defaults.fullScanCycle) 135 | ) 136 | 137 | // TODO should we really be setting this? 138 | "isThisBuildIncremental"(false) 139 | 140 | "sourceEncoding"("1") // this is the default 141 | 142 | "comment"(cxConfig.get("comment", defaults.comment)) 143 | 144 | // TODO probably this should be configurable 145 | "skipSCMTriggers"(false) 146 | "waitForResultsEnabled"(true) 147 | 148 | "generatePdfReport"(true) 149 | 150 | "vulnerabilityThresholdEnabled"( 151 | cxConfig.get( 152 | "vulnerabilityThresholdEnabled", 153 | defaults.vulnerabilityThresholdEnabled 154 | ) 155 | ) 156 | 157 | "highThreshold"( 158 | cxConfig.get("highThreshold", defaults.highThreshold) 159 | ) 160 | "mediumThreshold"( 161 | cxConfig.get("mediumThreshold", defaults.mediumThreshold) 162 | ) 163 | "lowThreshold"( 164 | cxConfig.get("lowThreshold", defaults.lowThreshold) 165 | ) 166 | 167 | // build failing won't work w/o this block 168 | "vulnerabilityThresholdResult" { 169 | "name"("FAILURE") 170 | "ordinal"("2") 171 | "color"("RED") 172 | "completeBuild"("true") 173 | } 174 | 175 | "osaEnabled"( 176 | cxConfig.get("osaEnabled", defaults.osaEnabled) 177 | ) 178 | "osaHighThreshold"( 179 | cxConfig.get("osaHighThreshold", defaults.osaHighThreshold) 180 | ) 181 | "osaMediumThreshold"( 182 | cxConfig.get("osaMediumThreshold", defaults.osaMediumThreshold) 183 | ) 184 | "osaLowThreshold"( 185 | cxConfig.get("osaLowThreshold", defaults.osaLowThreshold) 186 | ) 187 | "includeOpenSourceFolders"( 188 | cxConfig.get( 189 | "osaIncludePattern", 190 | defaults.osaIncludePattern 191 | ) 192 | ) 193 | "excludeOpenSourceFolders"( 194 | cxConfig.get( 195 | "osaExcludePattern", 196 | defaults.osaExcludePattern 197 | ) 198 | ) 199 | } 200 | } 201 | } 202 | } 203 | 204 | /** 205 | * A map defining the default Checkmarx config values used in `checkmarxScan`. 206 | */ 207 | static Map checkmarxConfigDefaults = [ 208 | useOwnServerCredentials: false, 209 | preset: "36", // "Default Checkmarx" 210 | excludeFolders: "resources, .git", 211 | incremental: true, 212 | fullScanCycle: "10", 213 | comment: "", 214 | vulnerabilityThresholdEnabled: true, 215 | highThreshold: 1, 216 | mediumThreshold: 2, 217 | lowThreshold: 3, 218 | osaEnabled: false, 219 | osaIncludePattern: "**/osa_dependencies/**", 220 | osaExcludePattern: "", 221 | osaHighThreshold: 1, 222 | osaMediumThreshold: 2, 223 | osaLowThreshold: 3, 224 | filterPattern: """ 225 | !**/_cvs/**/*, !**/.svn/**/*, !**/.hg/**/*, !**/.git/**/*, !**/.bzr/**/*, !**/bin/**/*, 226 | !**/obj/**/*, !**/backup/**/*, !**/.idea/**/*, !**/*.DS_Store, !**/*.ipr, !**/*.iws, 227 | !**/*.bak, !**/*.tmp, !**/*.aac, !**/*.aif, !**/*.iff, !**/*.m3u, !**/*.mid, !**/*.mp3, 228 | !**/*.mpa, !**/*.ra, !**/*.wav, !**/*.wma, !**/*.3g2, !**/*.3gp, !**/*.asf, !**/*.asx, 229 | !**/*.avi, !**/*.flv, !**/*.mov, !**/*.mp4, !**/*.mpg, !**/*.rm, !**/*.swf, !**/*.vob, 230 | !**/*.wmv, !**/*.bmp, !**/*.gif, !**/*.jpg, !**/*.png, !**/*.psd, !**/*.tif, !**/*.swf, 231 | !**/*.jar, !**/*.zip, !**/*.rar, !**/*.exe, !**/*.dll, !**/*.pdb, !**/*.7z, !**/*.gz, 232 | !**/*.tar.gz, !**/*.tar, !**/*.gz, !**/*.ahtm, !**/*.ahtml, !**/*.fhtml, !**/*.hdm, 233 | !**/*.hdml, !**/*.hsql, !**/*.ht, !**/*.hta, !**/*.htc, !**/*.htd, !**/*.war, !**/*.ear, 234 | !**/*.htmls, !**/*.ihtml, !**/*.mht, !**/*.mhtm, !**/*.mhtml, !**/*.ssi, !**/*.stm, 235 | !**/*.stml, !**/*.ttml, !**/*.txn, !**/*.xhtm, !**/*.xhtml, !**/*.class, !**/*.iml, !Checkmarx/Reports/*.* 236 | """.stripIndent(), 237 | ] 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/main/groovy/jenkins/automation/utils/CommonUtils.groovy: -------------------------------------------------------------------------------- 1 | package jenkins.automation.utils 2 | 3 | /** 4 | * Utils class when most reused common properties should live. 5 | * Adds a minimum base functionality required -build claiming, notifications and log. 6 | * @param context delegate passed in context 7 | */ 8 | class CommonUtils { 9 | 10 | /** 11 | * Adds bare minimum defaults 12 | */ 13 | 14 | static void addDefaults(context) { 15 | context.with { 16 | wrappers { 17 | colorizeOutput() 18 | timestamps() 19 | } 20 | logRotator { 21 | numToKeep(30) 22 | } 23 | publishers { 24 | allowBrokenBuildClaiming() 25 | } 26 | configure { Node project -> 27 | project / 'properties' / 'com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty'(plugin: "build-failure-analyzer") { 28 | doNotScan 'false' 29 | } 30 | } 31 | } 32 | } 33 | 34 | /** Utility function to add extended email 35 | * 36 | * @param List emails List of email string to make it seamlessly compatible with builders 37 | * @param triggersList List triggers E.g failure, fixed etc... 38 | * @param sendToDevelopers Default false, 39 | * @param sendToRequester Default true, 40 | * @param includeCulprits Default false, 41 | * @param sendToRecipientList Default true 42 | * @param preSendScript Default $DEFAULT_PRESEND_SCRIPT 43 | * @param content Default is $DEFAULT_CONTENT 44 | * 45 | * @see Common utils 46 | */ 47 | 48 | static void addExtendedEmail(context, List emails, List triggerList = ["failure", "unstable", "fixed"], sendToDevelopers = false, sendToRequester = true, includeCulprits = false, sendToRecipientList = true, preSendScript = "\$DEFAULT_PRESEND_SCRIPT", attachmentPattern = "", content="\$DEFAULT_CONTENT", subject = "\$DEFAULT_SUBJECT") { 49 | addExtendedEmail(context, emails.join(","), triggerList, sendToDevelopers, sendToRequester, includeCulprits, sendToRecipientList, preSendScript, attachmentPattern, content, subject ) 50 | } 51 | 52 | /** 53 | * Utility function to add extended email 54 | * @param String emails Comma separated string of emails 55 | * @param triggerList List triggers E.g failure, fixed etc... 56 | * @param sendToDevelopers Default false, 57 | * @param sendToRequester Default true, 58 | * @param includeCulprits Default false, 59 | * @param sendToRecipientList Default true 60 | * @param preSendScript Default $DEFAULT_PRESEND_SCRIPT 61 | * @param attachmentPattern Ant style pattern matching for attachments 62 | * @param content Default is $DEFAULT_CONTENT 63 | * @param subject Default is $DEFAULT_SUBJECT 64 | * 65 | * @see Common utils 66 | */ 67 | 68 | static void addExtendedEmail(context, String emails, List triggerList = ["failure", "unstable", "fixed"], sendToDevelopers = false, sendToRequester = true, includeCulprits = false, sendToRecipientList = true, preSendScript = "\$DEFAULT_PRESEND_SCRIPT", attachmentPattern = "", content = "\$DEFAULT_CONTENT", subject = "\$DEFAULT_SUBJECT") { 69 | 70 | context.with { 71 | extendedEmail { 72 | delegate.recipientList(emails) 73 | delegate.preSendScript(preSendScript) 74 | delegate.attachmentPatterns(attachmentPattern) 75 | delegate.defaultContent(content) 76 | delegate.defaultSubject(subject) 77 | 78 | triggers { 79 | triggerList.each { 80 | "${it}" { 81 | sendTo { 82 | if (sendToDevelopers) developers() 83 | if (sendToRequester) requester() 84 | if (includeCulprits) culprits() 85 | if (sendToRecipientList) recipientList() 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | } 93 | } 94 | 95 | /** 96 | * 97 | * @param context Closure context, i.e delegate 98 | * @param params Maps of params 99 | * emails: , please note it does not support ArrayList 100 | * triggers: ["failure", "unstable", "fixed"], 101 | * sendToDevs:, 102 | * sendToRequester:, 103 | * includeCulprits:, 104 | * endToRecipient:, 105 | * preSendScript = , 106 | * attachmentPattern = , 107 | * content = , 108 | * subject = 109 | * 110 | * @see Common utils 111 | 112 | */ 113 | static void addExtendedEmail(Map params, context) { 114 | 115 | params.triggerList = params.triggerList ?: ["failure", "unstable", "fixed"] 116 | params.sendToDevelopers = params.sendToDevelopers ?: false 117 | params.sendToRequester = params.sendToRequester ?: true 118 | params.includeCulprits = params.includeCulprits ?: false 119 | params.sendToRecipientList = params.sendToRecipientList ?: true 120 | params.preSendScript = params.preSendScript ?: "\$DEFAULT_PRESEND_SCRIPT" 121 | params.attachmentPattern = params.attachmentPattern ?: "" 122 | params.content = params.content ?: "\$DEFAULT_CONTENT" 123 | params.subject = params.subject ?: "\$DEFAULT_SUBJECT" 124 | 125 | def emails = params.emails 126 | 127 | context.with { 128 | extendedEmail { 129 | recipientList(emails) 130 | preSendScript(params.preSendScript) 131 | attachmentPatterns(params.attachmentPattern) 132 | defaultContent(params.content) 133 | defaultSubject(params.subject) 134 | 135 | triggers { 136 | params.triggerList.each { 137 | "${it}" { 138 | sendTo { 139 | if (params.sendToDevelopers) developers() 140 | if (params.sendToRequester) requester() 141 | if (params.includeCulprits) culprits() 142 | if (params.sendToRecipientList) recipientList() 143 | } 144 | } 145 | } 146 | } 147 | } 148 | 149 | } 150 | } 151 | 152 | /** 153 | * Utility function to add injectGlobalPasswords 154 | * 155 | * @see Common utils 156 | */ 157 | 158 | static void addInjectGlobalPasswords(context) { 159 | context.with { 160 | configure { 161 | it / buildWrappers / EnvInjectPasswordWrapper { 162 | injectGlobalPasswords(true) 163 | maskPasswordParameters(true) 164 | } 165 | } 166 | } 167 | } 168 | 169 | /** 170 | * Utility function to add log parser publisher 171 | * 172 | * @see Common utils 173 | */ 174 | 175 | static void addLogParserPublisher(context, rulesPath = "/var/lib/jenkins/shell_parse_rules.txt") { 176 | context.with { 177 | configure { 178 | it / publishers << 'hudson.plugins.logparser.LogParserPublisher' { 179 | unstableOnWarning true 180 | failBuildOnError true 181 | parsingRulesPath rulesPath 182 | } 183 | } 184 | } 185 | } 186 | 187 | /** 188 | * Utility to add a performance publisher block, for use in performance tests 189 | * 190 | * @see Common utils 191 | */ 192 | 193 | static void addPerformancePublisher(context, String reportPattern = "**/results/*.jtl", String unstableResponseTimeThreshold = "", int failedThresholdPositive, int failedThresholdNegative, int unstableThresholdPositive, int unstableThresholdNegative) { 194 | context.with { 195 | configure { 196 | it / publishers << 'hudson.plugins.performance.PerformancePublisher' { 197 | errorFailedThreshold 2 198 | errorUnstableThreshold 1 199 | errorUnstableResponseTimeThreshold unstableResponseTimeThreshold 200 | relativeFailedThresholdPositive failedThresholdPositive 201 | relativeFailedThresholdNegative failedThresholdNegative 202 | relativeUnstableThresholdPositive unstableThresholdPositive 203 | relativeUnstableThresholdNegative unstableThresholdNegative 204 | modeRelativeThresholds false 205 | configType 'ART' 206 | modeOfThreshold true 207 | compareBuildPrevious true 208 | modePerformancePerTestCase true 209 | modeThroughput true 210 | parsers { 211 | 'hudson.plugins.performance.JMeterParser' { 212 | glob reportPattern 213 | } 214 | } 215 | } 216 | } 217 | } 218 | } 219 | 220 | /** 221 | * Utility to disable concurrent builds in Pipeline jobs 222 | * 223 | * As of job-dsl plugin version 1.76, the old `concurrentBuilds(false)` syntax is deprecated and replaced with `disableConcurrentBuilds()` 224 | * The problem is that disableConcurrentBuilds() is a dynamic method, and thus it will not run via gradle and will only work in seed jobs. 225 | * This breaks any local usage of `gradlew rest` for running jobs against a local or remote Jenkins server, which currently is a key part of our 226 | * development workflow. 227 | * 228 | * This static disableCurrentBuilds() method retains the local development workflow while preventing developers from needing to litter 229 | * their job-dsl scripts with `configure` blocks 230 | * 231 | * @see Common utils 232 | */ 233 | static void disableConcurrentBuilds(context) { 234 | context.with { 235 | configure { 236 | it / 'properties' / 'org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty' {} 237 | } 238 | } 239 | } 240 | 241 | /** 242 | * Utility to add usernamePassword credentials binding 243 | * 244 | * the usernameVariable... style of usernamePassword{} credentials binding is "dynamic", and thus it will not run via gradle and will only work in seed jobs. 245 | * This breaks any local usage of `gradlew rest` for running jobs against a local or remote Jenkins server, which currently is a key part of our 246 | * development workflow. 247 | * 248 | * This addUsernamePasswordCredentials() method retains the local development workflow while preventing developers from needing to litter 249 | * their job-dsl scripts with `configure` blocks. 250 | * 251 | * @see Common utils Credentials Binding 252 | */ 253 | static void addUsernamePasswordCredentials(context, String credentialsId, String usernameVariable, String passwordVariable) { 254 | context.with { 255 | configure { Node project -> 256 | project / 'buildWrappers' / 'org.jenkinsci.plugins.credentialsbinding.impl.SecretBuildWrapper' / 'bindings' << 'org.jenkinsci.plugins.credentialsbinding.impl.UsernamePasswordMultiBinding' { 257 | delegate.credentialsId credentialsId 258 | delegate.usernameVariable usernameVariable 259 | delegate.passwordVariable passwordVariable 260 | } 261 | } 262 | } 263 | } 264 | 265 | /** 266 | * Utility to add AWS credentials binding 267 | * 268 | * amazonWebServicesCredentialsBinding is a "dynamic" method, and thus it will not run via gradle and will only work in seed jobs. 269 | * This breaks any local usage of `gradlew rest` for running jobs against a local or remote Jenkins server, which currently is a key part of our 270 | * development workflow. 271 | * 272 | * This addAmazonWebServicesCredentials() method retains the local development workflow while preventing developers from needing to litter 273 | * their job-dsl scripts with `configure` blocks. 274 | * 275 | * @see Common utils Credentials Binding 276 | */ 277 | static void addAmazonWebServicesCredentials(context, String credentialsId, String accessKeyVariable, String secretKeyVariable) { 278 | context.with { 279 | configure { Node project -> 280 | project / 'buildWrappers' / 'org.jenkinsci.plugins.credentialsbinding.impl.SecretBuildWrapper' / 'bindings' << 'com.cloudbees.jenkins.plugins.awscredentials.AmazonWebServicesCredentialsBinding' { 281 | delegate.credentialsId credentialsId 282 | delegate.accessKeyVariable accessKeyVariable 283 | delegate.secretKeyVariable secretKeyVariable 284 | } 285 | } 286 | } 287 | } 288 | 289 | /** 290 | * Common string for creating and activating a python 2.7 virtualenv in a shell block 291 | * 292 | * @see Common utils 293 | */ 294 | static String python27Virtualenv = """ 295 | if [ -d ".env" ]; then 296 | echo "**> virtualenv exists" 297 | else 298 | echo "**> creating python 2.7 virtualenv" 299 | virtualenv -p /usr/local/bin/python2.7 .env 300 | fi 301 | 302 | . .env/bin/activate 303 | 304 | if [ -f "requirements.txt" ]; then 305 | pip install -r requirements.txt 306 | fi 307 | """.stripIndent() 308 | } 309 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Base Job Builder 4 | 5 | ```groovy 6 | import jenkins.automation.builders.BaseJobBuilder 7 | 8 | BaseJobBuilder( 9 | name: "sample-job", 10 | description: "Description of your job", 11 | emails: ["foo@example.com","bar@example.com"] 12 | ).build(this) 13 | ``` 14 | 15 | ## Checkmarx Security Job Builder 16 | 17 | ```groovy 18 | import jenkins.automation.builders.CheckmarxSecurityJobBuilder 19 | 20 | def groupId = "ac43cb0d-034d-4b1e-9bf5-e7c1c46f71d2" 21 | def projectName ='sample-project' 22 | 23 | new CheckmarxSecurityJobBuilder( 24 | name: "${projectName}-checkmarx", 25 | description: "Sample checkmarx security job", 26 | scanRepo: [[url: "https://github.com/cfpb/jenkins-automation"]], 27 | checkmarxConfig: [ 28 | projectName: "${projectName}-checkmarx", 29 | groupId: "${groupId}", 30 | excludeFolders: ".git, build", 31 | comment: "Generated by Checkmarx job builder JAC", 32 | vulnerabilityThresholdEnabled: true, 33 | highThreshold: 4, 34 | mediumThreshold: 8, 35 | lowThreshold: 12, 36 | ], 37 | ).build(this) 38 | ``` 39 | 40 | ### Checkmarx Security Job Builder with OSA 41 | 42 | ```groovy 43 | import jenkins.automation.builders.BaseJobBuilder 44 | import jenkins.automation.utils.CheckmarxUtils 45 | 46 | def projectName = 'foo' 47 | def groupId = 'ac43cb0d-034d-4b1e-9bf5-e7c1c46f71d2' 48 | def osaCheckmarxConfig = [ 49 | projectName: "${projectName}-osa-checkmarx", 50 | groupId: "${groupId}", 51 | comment: 'Generated by Checkmarx job builder JAC', 52 | vulnerabilityThresholdEnabled: true, 53 | highThreshold: 1, 54 | mediumThreshold: 2, 55 | lowThreshold: 3, 56 | osaEnabled: true, 57 | osaHighThreshold: 1, 58 | osaMediumThreshold: 2, 59 | osaLowThreshold: 3, 60 | ] 61 | 62 | new BaseJobBuilder( 63 | name: "${projectName}-osa-checkmarx", 64 | description: 'Sample checkmarx security job', 65 | ).build(this).with { 66 | scm { 67 | git { 68 | remote { 69 | url('https://github.com/champain/jenkins-automation') 70 | } 71 | branch('checkmarx_osa_fixes') 72 | } 73 | } 74 | 75 | steps { 76 | gradle { 77 | tasks('copyDependencies') 78 | } 79 | } 80 | 81 | CheckmarxUtils.checkmarxScan(delegate, osaCheckmarxConfig) 82 | } 83 | 84 | ``` 85 | 86 | ## BDD Security Job Builder 87 | 88 | ```groovy 89 | import jenkins.automation.builders.BddSecurityJobBuilder 90 | 91 | def projectName ='sample-project' 92 | def bddSecurityRepo ="repo-to-scan' 93 | new BddSecurityJobBuilder( 94 | name: "${projectName}-bdd-security-job", 95 | description: "Sample bdd security job", 96 | baseUrl: "http://google.com", 97 | bddSecurityRepo: "${bddSecurityRepo}", 98 | chromedriverPath: "\\/Users\\/sotoo\\/homebrew\\/bin\\/Chromedriver" 99 | ).build(this) 100 | ``` 101 | 102 | ## Pipeline Builder 103 | 104 | ```groovy 105 | import jenkins.automation.builders.PipelineJobBuilder 106 | 107 | def script = """ 108 | pipeline { 109 | agent { label 'master' } 110 | stages { 111 | stage('hello') { 112 | steps { 113 | sh 'echo "Hello World"' 114 | } 115 | } 116 | } 117 | } 118 | """ 119 | 120 | new PipelineJobBuilder( 121 | name: 'Hello Pipeline With Script', 122 | description: 'This is a simple pipeline job', 123 | pipelineScript: script, 124 | sandboxFlag: false 125 | ).build(this).with { 126 | logRotator { 127 | numToKeep(365) 128 | } 129 | } 130 | 131 | new PipelineJobBuilder( 132 | name: 'Pipeline builder with stages', 133 | description: 'this is a simple pipeline job', 134 | stages: [[ 135 | stageName : 'First stage', 136 | jobName : 'Job 1', 137 | parameters: "[[\$class: 'StringParameterValue', name: 'foo', value: 'bar']]" 138 | ], 139 | [ 140 | stageName: 'Second stage', 141 | jobName : 'Job 2', 142 | ]] 143 | ).build(this) 144 | .with { 145 | logRotator { 146 | numToKeep(365) 147 | } 148 | } 149 | 150 | ``` 151 | 152 | ## Multibranch Pipeline Builder using 'git' as branch source (instead of GitHub API) 153 | 154 | ```groovy 155 | import jenkins.automation.builders.MultibranchPipelineJobBuilder 156 | 157 | new MultibranchPipelineJobBuilder( 158 | name: "multi-branch-pipeline-git", 159 | description: "Sample Multibranch Pipeline Job using git as the branch source", 160 | branchSource: MultibranchPipelineJobBuilder.BranchSourceType.GIT, 161 | gitCredentials: "009c8c9d-3cf5-4b2a-89f3-286977cabddf", 162 | gitRemote: "https://github.com/OrlandoSoto/orlando-shared-libraries", 163 | ).build(this) 164 | ``` 165 | 166 | ## Multibranch Pipeline Builder using a GitHub Enterprise API as branch source 167 | ```groovy 168 | import jenkins.automation.builders.MultibranchPipelineJobBuilder 169 | 170 | new MultibranchPipelineJobBuilder( 171 | name: "multi-branch-pipeline-github-enterprise", 172 | description: "Sample Multibranch Pipeline using the GitHub API as the branch source", 173 | branchSource: MultibranchPipelineJobBuilder.BranchSourceType.GITHUB, 174 | gitCredentials: "8fbdbaa0-d5ff-4acb-9e8f-27e49b77048a", 175 | ghOwner: "org-name", 176 | ghRepo: "repo-name", 177 | ghApiEndpoint: "https://github.example.com/api/v3", 178 | oldNumToKeep: 1 179 | ).build(this) 180 | ``` 181 | 182 | ## JS Build Job 183 | 184 | ```groovy 185 | import jenkins.automation.builders.JsJobBuilder 186 | 187 | String basePath = 'JsJobSamples' 188 | List developers = ['jane@example.com', 'joe@example.com'] 189 | 190 | def repos = [ 191 | [name: 'jenkins-automation', url: "https://github.com/cfpb/jenkins-automation@2.0"], 192 | [name: 'collab', url: "https://github.com/cfpb/collab"] 193 | [name: 'other', url: "https://github.com/cfpb/jenkins-automation", disable_submodule: true] 194 | ] 195 | folder(basePath) { 196 | description 'This example shows how to create jobs using Job builders.' 197 | } 198 | 199 | new JsJobBuilder( 200 | name: "$basePath/BuilderVsBuilders", 201 | description: 'An example using a job builder for a Javascript build jobs project', 202 | repos: repos, 203 | emails: developers, 204 | use_versions: true 205 | ).build(this) 206 | ``` 207 | 208 | ## Salesforce Build Job 209 | 210 | ```groovy 211 | 212 | import jenkins.automation.builders.SalesforceAntJobBuilder 213 | 214 | List developers = ['jane@example.com', 'joe@example.com'] 215 | 216 | def repo = "https://github.com/cfpb/salesforce-automation" 217 | 218 | new SalesforceAntJobBuilder( 219 | name: "example-salesforce-ant-job", 220 | description: 'An example using a job builder for a Salesforce Ant JobBuilder build jobs project.', 221 | repoUrl: repo, 222 | emails: developers, 223 | antTasks: ["retrieveUnpackaged"], 224 | antInstallerName:"ant-latest" 225 | ).build(this) 226 | 227 | ``` 228 | 229 | ## Sauce On Demand job 230 | ```groovy 231 | import jenkins.automation.builders.SauceConnectJobBuilder 232 | 233 | def projectName ='foo' 234 | new SauceConnectJobBuilder( 235 | name: "example-browser-test", 236 | description: "Sample Sauce on Demand job", 237 | emails: ['email1@server1.com', 'email2@server2.com'], 238 | sauceCredentialId: '1234', 239 | // Note that when using Gradle build sauceCredentialId does not get populated. 240 | // However, sauceCredentialId is correctly populated when built inside a Jenkins environment 241 | additionalOptions: "-v" 242 | ).build(this); 243 | ``` 244 | 245 | ## Using MultiScm Utility 246 | 247 | 248 | ```groovy 249 | import jenkins.automation.builders.BaseJobBuilder 250 | import jenkins.automation.utils.ScmUtils 251 | 252 | def repos = [ 253 | [name: 'jenkins-automation', url: "https://github.com/cfpb/jenkins-automation@2.0"], 254 | [name: 'collab', url: "https://github.com/cfpb/collab"], 255 | [name: 'hubot-aws-cfpb', url: "https://github.com/cfpb/hubot-aws-cfpb", branch: "main"] 256 | ] 257 | new BaseJobBuilder( 258 | name: "sample-job-with-multiscm", 259 | description: "A sample with multiple source control repositories", 260 | ).build(this).with { 261 | multiscm { 262 | ScmUtils.project_repos(delegate, repos, true) 263 | } 264 | } 265 | ``` 266 | 267 | ## Using the Git shallow clone utility 268 | 269 | 270 | ```groovy 271 | import jenkins.automation.builders.BaseJobBuilder 272 | import jenkins.automation.utils.ScmUtils 273 | 274 | new BaseJobBuilder( 275 | name: "sample-job-with-git-shallow-clone", 276 | description: "A sample demonstrating a repo being git shallow-cloned", 277 | ).build(this).with { 278 | scm { 279 | ScmUtils.shallowGit(delegate, "https://github.com/cfpb/jenkins-automation", "feature-branch") 280 | } 281 | } 282 | ``` 283 | 284 | 285 | ## Customizing your job 286 | 287 | ### Determining the environment 288 | 289 | ```groovy 290 | import jenkins.automation.utils.EnvironmentUtils 291 | 292 | // In our Jenkinses, we name this variable "JAC_ENVIRONMENT" with values of "DEV", "STAGE", or "PROD" 293 | // In your Jenkins, you can name the variable whatever you like; replace the variable you pass into 294 | // EnvironmentUtils.getInstance(...) with your global variable name 295 | 296 | def env = EnvironmentUtils.getInstance("${JAC_ENVIRONMENT}") 297 | println "Environment is " + env.getEnv() 298 | 299 | if (env.isDev()){ 300 | // Do environment-specific things here 301 | } 302 | 303 | job('test') { 304 | steps { 305 | shell "echo ${env.getEnv()}" 306 | } 307 | } 308 | 309 | ``` 310 | 311 | ### Running a job on a schedule 312 | 313 | ```groovy 314 | job('example') { 315 | triggers { 316 | cron("H/15 * * * *") 317 | } 318 | } 319 | ``` 320 | 321 | ### Including a [Jenkins Custom Tool](https://wiki.jenkins-ci.org/display/JENKINS/Custom+Tools+Plugin) 322 | 323 | ```groovy 324 | job('example') { 325 | wrappers { 326 | customTools(['jq']) 327 | } 328 | } 329 | ``` 330 | 331 | ### Including variables in a shell step 332 | 333 | ```groovy 334 | var variable_from_this_script = 'foo'; 335 | 336 | job('example') { 337 | steps { 338 | shell(""" 339 | my_command \ 340 | --my-arg ${variable_from_this_script} \ 341 | --other-arg \${JENKINS_VARIABLE} 342 | """.stripIndent() 343 | ) 344 | } 345 | } 346 | ``` 347 | 348 | ## Common Utils 349 | 350 | ### Defaults 351 | 352 | ```groovy 353 | import jenkins.automation.utils.CommonUtils 354 | 355 | job('example'){ 356 | CommonUtils.addDefaults(delegate) 357 | } 358 | ``` 359 | 360 | 361 | ### Extended Email 362 | 363 | ```groovy 364 | import jenkins.automation.utils.CommonUtils 365 | 366 | job("example"){ 367 | CommonUtils.addExtendedEmail(delegate, emails = 'foo@example.com, bar@example.com') 368 | } 369 | 370 | // override accepts emails as a list. Compatible with builders 371 | job('example'){ 372 | publishers { 373 | CommonUtils.addExtendedEmail(delegate, emails = ['foo@example.com', 'bar@example.com']) 374 | } 375 | } 376 | 377 | // Override default email triggers. 378 | job('example'){ 379 | publishers { 380 | CommonUtils.addExtendedEmail(delegate, emails, triggers=['statusChanged']) 381 | } 382 | } 383 | 384 | // Override default email pre-send script, by providing a groovy code 385 | job('example'){ 386 | publishers { 387 | CommonUtils.addExtendedEmail(delegate, emails, triggers, sendToDevs, senToReq, includeCulprits, sendToRecipient, "cancel = true") 388 | } 389 | } 390 | 391 | // Override default email pre-send script by providing path to the script file 392 | job('example'){ 393 | publishers { 394 | CommonUtils.addExtendedEmail(delegate, emails, triggers, sendToDevs, sendToReq, includeCulprits, sendToRecipient, "\${SCRIPT, template='path/to/script.groovy'}" 395 | } 396 | } 397 | // Override using configuration map, a little nicer way to invoke the function with named arguments 398 | job('example'){ 399 | publishers { 400 | CommonUtils.addExtendedEmail(delegate, emails:emails, triggers:['statusChanged'], attachmentPattern:".csv") 401 | } 402 | } 403 | 404 | // Override using configuration map, a little nicer way to invoke the function with named arguments 405 | job('example'){ 406 | publishers { 407 | CommonUtils.addExtendedEmail(delegate, emails:emails, triggers:['statusChanged'], attachmentPattern:".csv") 408 | } 409 | } 410 | 411 | // Override the default content and customize subject 412 | job('example using configuration map'){ 413 | publishers { 414 | CommonUtils.addExtendedEmail(delegate, emails:emails, content: "foo", subject: "bar") 415 | } 416 | } 417 | // Override using configuration map, a little nicer way to invoke the function with named arguments 418 | job('example using configuration map') { 419 | publishers { 420 | CommonUtils.addExtendedEmail(emails: 'foo@example.com', triggers: ['statusChanged'], attachmentPattern: ".csv", delegate,) 421 | } 422 | } 423 | // Override the default content and customize subject 424 | job('example') { 425 | publishers { 426 | CommonUtils.addExtendedEmail(emails: 'foo@example.com', content: "foo", subject: "bar", delegate) 427 | } 428 | } 429 | 430 | 431 | ``` 432 | 433 | ### Inject global passwords 434 | 435 | ```groovy 436 | import jenkins.automation.builders.BaseJobBuilder 437 | import jenkins.automation.utils.CommonUtils 438 | 439 | new BaseJobBuilder( 440 | name: "sample-base-job-with-additional-config", 441 | description: "A job with some additional configurations added" 442 | ).build(this).with { 443 | CommonUtils.addInjectGlobalPasswords(delegate) 444 | } 445 | ``` 446 | 447 | ### Add shell parsing rules 448 | 449 | ```groovy 450 | import jenkins.automation.builders.BaseJobBuilder 451 | import jenkins.automation.utils.CommonUtils 452 | 453 | new BaseJobBuilder( 454 | name: "sample-base-job-with-log-parsing", 455 | description: "A job with log parsing added" 456 | ).build(this).with { 457 | //how to use CommonUtils; pass a custom filename to override the default 458 | CommonUtils.addLogParserPublisher(delegate, "/var/lib/jenkins/some_rules_file.txt") 459 | } 460 | ``` 461 | 462 | ### Disable Concurrent Builds 463 | 464 | ```groovy 465 | import jenkins.automation.builders.PipelineJobBuilder 466 | import jenkins.automation.utils.CommonUtils 467 | 468 | def script = """ 469 | pipeline { 470 | agent { label 'master' } 471 | stages { 472 | stage('hello') { 473 | steps { 474 | sh 'echo "Hello World"' 475 | } 476 | } 477 | } 478 | } 479 | """ 480 | new PipelineJobBuilder( 481 | name: 'pipeline-job-disables-concurrent-build', 482 | description: 'This is a simple pipeline job that disables concurrent builds', 483 | pipelineScript: script, 484 | sandboxFlag: false 485 | ).build(this).with { 486 | CommonUtils.disableConcurrentBuilds(delegate) 487 | } 488 | ``` 489 | 490 | ### Credentials Binding 491 | 492 | ```groovy 493 | import jenkins.automation.builders.BaseJobBuilder 494 | import jenkins.automation.utils.CommonUtils 495 | 496 | new BaseJobBuilder( 497 | name: "sample-job-with-credentials-variable-bindings", 498 | description: "This is a job that uses our credentialsBinding helpers" 499 | ).build(this).with { 500 | //you can use one or the other or together if you need to 501 | CommonUtils.addUsernamePasswordCredentials(delegate, "some-credentials-id", "SOME-USERNAME-VAR", "SOME-PASSWORD-VAR") 502 | CommonUtils.addAmazonWebServicesCredentials(delegate, "some-aws-credentials-id", "SOME-ACCESS-KEY-VAR", "SOME-SECRET-KEY-VAR") 503 | } 504 | ``` 505 | 506 | ### Add virtualenv to a shell step 507 | 508 | ```groovy 509 | import jenkins.automation.builders.BaseJobBuilder 510 | import jenkins.automation.utils.CommonUtils 511 | 512 | new BaseJobBuilder( 513 | name: "sample-base-job-with-virtualenv", 514 | description: "A job that creates and activates a python 2.7 virtualenv" 515 | ).build(this).with { 516 | steps { 517 | shell( CommonUtils.python27Virtualenv + """ 518 | # pip install ansible 519 | ls -la 520 | env 521 | echo "Hello world" 522 | """.stripIndent() 523 | ) 524 | } 525 | } 526 | ``` 527 | 528 | ### Add a performance publisher block 529 | 530 | ```groovy 531 | import jenkins.automation.builders.BaseJobBuilder 532 | import jenkins.automation.utils.CommonUtils 533 | 534 | new BaseJobBuilder( 535 | name: "sample-base-job-with-performance-publisher", 536 | description: "A job with a performance publisher. It does not include the actual bits that run the load tests" 537 | ).build(this).with { 538 | steps { 539 | shell("echo 'Run jmeter tests here'") 540 | } 541 | CommonUtils.addPerformancePublisher(delegate,failedThresholdPositive=10, failedThresholdNegative=10, unstableThresholdPositive=5, unstableThresholdNegative=5) 542 | } 543 | ``` 544 | 545 | ## Plugin Utils 546 | 547 | ### New Relic 548 | 549 | ``` 550 | import jenkins.automation.builders.BaseJobBuilder 551 | 552 | import jenkins.automation.utils.PluginUtils 553 | 554 | def job=new BaseJobBuilder( 555 | name: "sample-base-job-with-new-relic", 556 | description: "A job with some additional plugin added" 557 | ).build(this).with { 558 | PluginUtils.addNewRelicSupport(delegate, "nr-api-key", "new", "foo", "some_log", "deploy", '1') //context, apiKey, applicationId, jobName, changeLog, user, revision 559 | 560 | } 561 | ``` 562 | 563 | ### SQS 564 | 565 | ``` 566 | import jenkins.automation.builders.BaseJobBuilder 567 | 568 | import jenkins.automation.utils.PluginUtils 569 | 570 | def job=new BaseJobBuilder( 571 | name: "sample-base-job-with-sqs-support", 572 | description: "A job with some additional plugin added" 573 | ).build(this).with { 574 | PluginUtils.addSQSNotification(delegate, "" ,"https://localhost/jenkins","foobar",customSQSMessageValue="Say something sharp" ) 575 | } 576 | ``` 577 | 578 | ### Using a startup trigger 579 | 580 | ```groovy 581 | import jenkins.automation.builders.BaseJobBuilder 582 | import jenkins.automation.utils.PluginUtils 583 | 584 | new BaseJobBuilder( 585 | name: "sample-base-job-with-startup-triggers", 586 | description: "A job with startup triggers" 587 | ).build(this).with { 588 | PluginUtils.startupTrigger(delegate) 589 | } 590 | ``` 591 | 592 | 593 | ### Using GH PR Watcher 594 | 595 | This PR watcher can be used to help configure jobs for either GitHub.com 596 | or a hosted GitHub Enterprise instance at a different hostname. 597 | 598 | Create a job that builds pull requests for a specific repo in a 599 | GitHub Enterprise instance. The `` should be 600 | replaced with a credential ID created by the GitHub Pull Request Builder 601 | itself. This can be found in the `config.xml` of a job already configured 602 | to use the builder or from the `org.jenkinsci.plugins.ghprb.GhprbTrigger.xml` 603 | in a Jenkins installation. 604 | 605 | When running multiple PR Builder jobs on the same repo it's recommended to set 606 | a unique `ghPrStatusContext` so that all results appear as individual checks 607 | in the UI for the PR. 608 | 609 | #### For GitHub Enterprise 610 | 611 | ```groovy 612 | import jenkins.automation.builders.BaseJobBuilder 613 | import jenkins.automation.utils.GhUtils 614 | 615 | new BaseJobBuilder( 616 | name: 'GHE_PR_builder', 617 | description: 'Does GHE PR building from internal GHE', 618 | ).build(this).with { 619 | GhUtils.ghPrWatcher( 620 | delegate, 621 | [ 622 | ghProject: 'cfpb/reponame', 623 | ghHostname: 'github.org.tld', 624 | ghAuthId: '', 625 | ghPrHooks: true, 626 | ghPrOrgsList: 'InternalDev', 627 | ghPrStatusContext: 'Test your example job', 628 | ghPrResultMessage: [ 629 | 'SUCCESS': 'Tests completed normally', 630 | 'ERROR': 'Tests errored', 631 | 'FAILURE': 'Tests failed', 632 | ] 633 | ] 634 | ) 635 | 636 | steps { 637 | shell(''' 638 | sh test.sh 639 | '''.stripIndent() 640 | ) 641 | } 642 | } 643 | ``` 644 | 645 | #### For GitHub.com 646 | 647 | This job builder disables PRs from being built unless the creator is a member 648 | of an organization in the orgs list, disables hooks since 649 | public github can't talk back to your internal Jenkins, creates a cron, 650 | and sets the orgs list so that anyone that belongs can automatically have 651 | their PR built. Any organization member can also simply comment 'ok to test' 652 | on a PR from a user outside of the organization to have that PR built 653 | with Jenkins. 654 | 655 | ```groovy 656 | import jenkins.automation.builders.BaseJobBuilder 657 | import jenkins.automation.utils.GhUtils 658 | 659 | new BaseJobBuilder( 660 | name: 'GH_PR_builder', 661 | description: 'PR build from GitHub.com', 662 | ).build(this).with { 663 | GhUtils.ghPrWatcher( 664 | delegate, 665 | [ 666 | ghProject: 'cfpb/reponame', 667 | ghHostname: 'github.com', 668 | ghAuthId: '', 669 | ghPrHooks: false, 670 | ghPrCron: '*/2 * * * *', 671 | ghPrOrgsList: 'CFPB', 672 | ghPrStatusContext: 'Test your example job', 673 | ghPrResultMessage: [ 674 | 'SUCCESS': 'Tests completed normally', 675 | 'ERROR': 'Tests errored', 676 | 'FAILURE': 'Tests failed', 677 | ] 678 | ] 679 | ) 680 | 681 | steps { 682 | shell(''' 683 | sh test.sh 684 | '''.stripIndent() 685 | ) 686 | } 687 | } 688 | ``` 689 | --------------------------------------------------------------------------------