├── .gitignore ├── README.md ├── exercises ├── 01-jenkins-installation │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── customize-jenkins.png │ │ ├── first-admin-user.png │ │ ├── instance-configuration.png │ │ ├── jenkins-dashboard.png │ │ ├── jenkins-ready.png │ │ ├── plugin-installation-progress.png │ │ └── unlock-jenkins.png │ │ └── solution.md ├── 02-job-creation │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── build-history.png │ │ ├── build-with-params.png │ │ ├── job-configuration.png │ │ ├── job-in-folder.png │ │ ├── job-in-view.png │ │ ├── move-job.png │ │ ├── new-folder.png │ │ ├── new-freestyle-job.png │ │ └── new-view.png │ │ └── solution.md ├── 03-build-trigger-and-steps │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── both-jobs-in-folder.png │ │ ├── build-downstream.png │ │ ├── build-trigger-and-step.png │ │ ├── executed-downstream-job.png │ │ └── string-parameter.png │ │ └── solution.md ├── 04-scm-configuration │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── git-scm.png │ │ ├── gradle-build-step.png │ │ └── new-job.png │ │ └── solution.md ├── 05-test-execution-and-reporting │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── all-test-result-trend.png │ │ ├── jacoco-config.png │ │ ├── jacoco-plugin.png │ │ ├── jacoco-report.png │ │ ├── jacoco-tasks.png │ │ ├── jacoco-trend.png │ │ ├── publish-all-tests.png │ │ ├── publish-unit-tests.png │ │ └── unit-test-result-trend.png │ │ └── solution.md ├── 06-notifications │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── add-webhook.png │ │ ├── change-gradle-tasks.png │ │ ├── chat-room-message.png │ │ ├── create-room.png │ │ ├── google-chat-plugin.png │ │ ├── notification-config.png │ │ ├── webhook-naming.png │ │ └── webhook-url.png │ │ └── solution.md ├── 07-artifacts │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── archive-artifacts.png │ │ ├── build-artifact.png │ │ ├── copy-artifacts-plugin.png │ │ ├── copy-artifacts.png │ │ ├── fingerprint-details.png │ │ ├── fingerprints-upstream.png │ │ ├── recorded-fingerprint.png │ │ └── trigger-upstream.png │ │ └── solution.md ├── 08-matrix-security │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── create-user.png │ │ ├── job-overview.png │ │ ├── matrix-security-permissions.png │ │ ├── user-login.png │ │ └── user-overview.png │ │ └── solution.md ├── 09-rest-api │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── api-token.png │ │ ├── build-by-user.png │ │ ├── create-user.png │ │ ├── disabled-job.png │ │ └── user-permissions.png │ │ └── solution.md ├── 10-distributed-builds │ ├── Vagrantfile │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── agent-config.png │ │ ├── agent-label-config.png │ │ ├── build-for-label.png │ │ ├── job-label-config.png │ │ ├── master-config.png │ │ ├── node-overview.png │ │ └── queued-job.png │ │ └── solution.md ├── 11-pipeline-job │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── blue-ocean-manual-step.png │ │ ├── blue-ocean-plugin.png │ │ ├── bugfix-branch.png │ │ ├── console-manual-step.png │ │ ├── credentials.png │ │ ├── finished-build.png │ │ ├── job-only-master.png │ │ ├── job-scm.png │ │ ├── multi-branch-pipeline-job.png │ │ ├── standard-pipeline.png │ │ └── triggered-manual-step.png │ │ └── solution.md ├── 12-basic-jenkinsfile │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── declarative-pipeline.png │ │ ├── go-global-tool.png │ │ ├── go-plugin.png │ │ ├── job-scm.png │ │ └── new-job.png │ │ └── solution.md ├── 13-advanced-jenkinsfile │ ├── instructions.md │ └── solution │ │ ├── images │ │ ├── codecov_token_credentials.png │ │ └── github_token_credentials.png │ │ └── solution.md └── 14-shared-library │ ├── instructions.md │ └── solution │ ├── images │ ├── pipeline-view.png │ └── shared-library-config.png │ └── solution.md ├── prerequisites └── instructions.md └── slides.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vagrant -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Certified Jenkins Engineer (CJE) Crash Course 2 | 3 | CloudBees, the company behind the automation server Jenkins, is a major player in the Continuous Integration and Continuous Delivery space. The [Certified Jenkins Engineer (CJE) certification](https://www.cloudbees.com/jenkins/jenkins-certification) was designed to verify your qualifications in configuring and managing Jenkins jobs, pipelines, and cluster environments. Whether you are a seasoned Jenkins user or someone who wants to deeply learn the most important aspects of Jenkins, then this course is for you. 4 | 5 | This practical course is designed to walk you through all the topics covered in the exam to fully prepare to pass the certification exam. The trainer and [CJE](https://certificates.cloudbees.com/cd0e3338-9080-416b-aba2-3f75f9fe349f), Benjamin Muschko, will also share his personal experience with preparing for all aspects of the exam. 6 | 7 | ## Prerequisites 8 | 9 | All exercises in this repository practice a self-contained portion of the [CJE curriculum](https://www.cloudbees.com/cloudbees-university/training-certifications/jenkins). Please make sure to follow the [instructions](./prerequisites/instructions.md) for setting up your environment before joining the training. 10 | 11 | ## Exercises 12 | 13 | All [exercises](./exercises) are numbered and live in dedicated directories starting with the name `exercise-`. You'll find instructions for each exercise in each folder. Solutions are available in the `solution` folder. Try to solve each exercise yourself before having a look at the solution. 14 | 15 | ## Additional Resources 16 | 17 | * 💡 [CloudBees: Certification information](https://www.cloudbees.com/jenkins/certification) 18 | * 📚 [CloudBees: CJE learning path](https://university.cloudbees.com/path/certified-jenkins-engineer-cje-exam-preparation) 19 | * 📚 [Addison-Wesley: Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation](https://learning.oreilly.com/library/view/continuous-delivery-reliable/9780321670250/) 20 | * 📚 [O'Reilly: Jenkins 2: Up and Running](https://learning.oreilly.com/library/view/jenkins-2-up/9781491979587/) 21 | * ✍🏻 [Benjamin Muschko: Best practices for writing Jenkins shared libraries](https://bmuschko.com/blog/jenkins-shared-libraries/) 22 | * ✍🏻 [Benjamin Muschko: Step by step instructions on how to use the Jenkins Kubernetes plugin for different use cases](https://github.com/bmuschko/jenkins-with-kubernetes) 23 | * 🎞️ [A Cloud Guru: Certified Jenkins Engineer](https://learn.acloud.guru/course/f2956e4d-87f1-4f26-b4db-ffb8e01a1afb) 24 | * 🧪 [Udemy: Certified Jenkins Engineer (CJE) Practice Exam](https://www.udemy.com/course/certified-jenkins-engineer-practice-exam/) 25 | -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 2 | 3 | In this exercise, you will practice installing and configuring Jenkins on your machine. Feel free to experiment with a different Jenkins distribution than the proposed WAR file. 4 | 5 | ## Installing Jenkins 6 | 7 | 1. Download the [latest stable Jenkins WAR file](https://get.jenkins.io/war-stable/latest/jenkins.war). To download from the command line, use `wget https://get.jenkins.io/war-stable/latest/jenkins.war`. 8 | 2. Open a terminal and navigate to the directory containing the WAR file. 9 | 3. Run the command `java -jar jenkins.war` to start Jenkins on port 8080. Provide the `--httpPort` option if you experience a port conflict e.g. `java -jar jenkins.war --httpPort=9999`. For more information on starting and stopping Jenkins, see the [user guide](https://wiki.jenkins.io/display/JENKINS/Starting+and+Accessing+Jenkins). After a couple of seconds you should see the message "Jenkins is fully up and running" in the log output. 10 | 4. Open a browser of your choice and navigate to `localhost:`. The default is `localhost:8080`. 11 | 5. In the screen named "Unlock Jenkins", enter the password from the console output. Press the "Continue" button. 12 | 6. In the screen named "Customize Jenkins", select the option "Install suggested plugins". 13 | 7. In the screen named "Create First Admin User", enter all fields with credentials for your Jenkins administrator. Press the "Save and Continue" button. 14 | 8. In the screen named "Instance Configuration", keep the default value. Press the "Save and Finish" button. 15 | 9. You should see the message "Jenkins is ready!". Press the "Start using Jenkins" button. You'll be redirected to the Jenkins dashboard. 16 | -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/customize-jenkins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/customize-jenkins.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/first-admin-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/first-admin-user.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/instance-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/instance-configuration.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/jenkins-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/jenkins-dashboard.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/jenkins-ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/jenkins-ready.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/plugin-installation-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/plugin-installation-progress.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/images/unlock-jenkins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/01-jenkins-installation/solution/images/unlock-jenkins.png -------------------------------------------------------------------------------- /exercises/01-jenkins-installation/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Download the WAR file with the help of `wget`. 4 | 5 | ```bash 6 | wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war 7 | --2019-08-09 16:06:23-- http://mirrors.jenkins.io/war-stable/latest/jenkins.war 8 | Resolving mirrors.jenkins.io (mirrors.jenkins.io)... 52.202.51.185 9 | Connecting to mirrors.jenkins.io (mirrors.jenkins.io)|52.202.51.185|:80... connected. 10 | HTTP request sent, awaiting response... 302 Found 11 | Location: http://mirror.xmission.com/jenkins/war-stable/2.176.2/jenkins.war [following] 12 | --2019-08-09 16:06:23-- http://mirror.xmission.com/jenkins/war-stable/2.176.2/jenkins.war 13 | Resolving mirror.xmission.com (mirror.xmission.com)... 2607:fa18:0:3::13, 198.60.22.13 14 | Connecting to mirror.xmission.com (mirror.xmission.com)|2607:fa18:0:3::13|:80... connected. 15 | HTTP request sent, awaiting response... 200 OK 16 | Length: 77379386 (74M) [application/java-archive] 17 | Saving to: ‘jenkins.war’ 18 | 19 | jenkins.war 100%[=================================================>] 73.79M 6.46MB/s in 14s 20 | 21 | 2019-08-09 16:06:37 (5.36 MB/s) - ‘jenkins.war’ saved [77379386/77379386] 22 | ``` 23 | 24 | First, start the Jenkins server. Copy the password printed in the log output to the clipboard. 25 | 26 | ```bash 27 | $ java -jar jenkins.war 28 | Running from: /Users/bmuschko/dev/tools/jenkins-2.150.1/jenkins.war 29 | webroot: $user.home/.jenkins 30 | Jul 16, 2019 4:59:30 PM org.eclipse.jetty.util.log.Log initialized 31 | INFO: Logging initialized @501ms to org.eclipse.jetty.util.log.JavaUtilLog 32 | Jul 16, 2019 4:59:30 PM winstone.Logger logInternal 33 | INFO: Beginning extraction from war file 34 | Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.handler.ContextHandler setContextPath 35 | WARNING: Empty contextPath 36 | Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.Server doStart 37 | INFO: jetty-9.4.z-SNAPSHOT; built: 2019-02-15T16:53:49.381Z; git: eb70b240169fcf1abbd86af36482d1c49826fa0b; jvm 1.8.0_181-b13 38 | Jul 16, 2019 4:59:32 PM org.eclipse.jetty.webapp.StandardDescriptorProcessor visitServlet 39 | INFO: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet 40 | Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.session.DefaultSessionIdManager doStart 41 | INFO: DefaultSessionIdManager workerName=node0 42 | Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.session.DefaultSessionIdManager doStart 43 | INFO: No SessionScavenger set, using defaults 44 | Jul 16, 2019 4:59:32 PM org.eclipse.jetty.server.session.HouseKeeper startScavenging 45 | INFO: node0 Scavenging every 660000ms 46 | Jenkins home directory: /Users/bmuschko/.jenkins found at: $user.home/.jenkins 47 | Jul 16, 2019 4:59:34 PM org.eclipse.jetty.server.handler.ContextHandler doStart 48 | INFO: Started w.@21ec5d87{Jenkins v2.176.1,/,file:///Users/bmuschko/.jenkins/war/,AVAILABLE}{/Users/bmuschko/.jenkins/war} 49 | Jul 16, 2019 4:59:34 PM org.eclipse.jetty.server.AbstractConnector doStart 50 | INFO: Started ServerConnector@388ffbc2{HTTP/1.1,[http/1.1]}{0.0.0.0:8080} 51 | Jul 16, 2019 4:59:34 PM org.eclipse.jetty.server.Server doStart 52 | INFO: Started @4486ms 53 | Jul 16, 2019 4:59:34 PM winstone.Logger logInternal 54 | INFO: Winstone Servlet Engine v4.0 running: controlPort=disabled 55 | Jul 16, 2019 4:59:35 PM jenkins.InitReactorRunner$1 onAttained 56 | INFO: Started initialization 57 | Jul 16, 2019 4:59:35 PM jenkins.InitReactorRunner$1 onAttained 58 | INFO: Listed all plugins 59 | Jul 16, 2019 4:59:36 PM jenkins.InitReactorRunner$1 onAttained 60 | INFO: Prepared all plugins 61 | Jul 16, 2019 4:59:36 PM jenkins.InitReactorRunner$1 onAttained 62 | INFO: Started all plugins 63 | Jul 16, 2019 4:59:36 PM jenkins.InitReactorRunner$1 onAttained 64 | INFO: Augmented all extensions 65 | Jul 16, 2019 4:59:37 PM jenkins.InitReactorRunner$1 onAttained 66 | INFO: Loaded all jobs 67 | Jul 16, 2019 4:59:37 PM hudson.model.AsyncPeriodicWork$1 run 68 | INFO: Started Download metadata 69 | Jul 16, 2019 4:59:37 PM hudson.util.Retrier start 70 | INFO: Attempt #1 to do the action check updates server 71 | Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh 72 | INFO: Refreshing org.springframework.web.context.support.StaticWebApplicationContext@54fe6e54: display name [Root WebApplicationContext]; startup date [Tue Jul 16 16:59:38 MDT 2019]; root of context hierarchy 73 | Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory 74 | INFO: Bean factory for application context [org.springframework.web.context.support.StaticWebApplicationContext@54fe6e54]: org.springframework.beans.factory.support.DefaultListableBeanFactory@3e77b308 75 | Jul 16, 2019 4:59:38 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 76 | INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3e77b308: defining beans [authenticationManager]; root of factory hierarchy 77 | Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh 78 | INFO: Refreshing org.springframework.web.context.support.StaticWebApplicationContext@2b22ca16: display name [Root WebApplicationContext]; startup date [Tue Jul 16 16:59:38 MDT 2019]; root of context hierarchy 79 | Jul 16, 2019 4:59:38 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory 80 | INFO: Bean factory for application context [org.springframework.web.context.support.StaticWebApplicationContext@2b22ca16]: org.springframework.beans.factory.support.DefaultListableBeanFactory@28d4e2d4 81 | Jul 16, 2019 4:59:38 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 82 | INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@28d4e2d4: defining beans [filter,legacy]; root of factory hierarchy 83 | Jul 16, 2019 4:59:38 PM jenkins.install.SetupWizard init 84 | INFO: 85 | 86 | ************************************************************* 87 | ************************************************************* 88 | ************************************************************* 89 | 90 | Jenkins initial setup is required. An admin user has been created and a password generated. 91 | Please use the following password to proceed to installation: 92 | 93 | 34155b26571e4a2eb6eca68ddc4d5749 94 | 95 | This may also be found at: /Users/bmuschko/.jenkins/secrets/initialAdminPassword 96 | 97 | ************************************************************* 98 | ************************************************************* 99 | ************************************************************* 100 | 101 | Jul 16, 2019 4:59:42 PM hudson.model.UpdateSite updateData 102 | INFO: Obtained the latest update center data file for UpdateSource default 103 | Jul 16, 2019 4:59:42 PM hudson.model.UpdateSite updateData 104 | INFO: Obtained the latest update center data file for UpdateSource default 105 | Jul 16, 2019 4:59:43 PM jenkins.InitReactorRunner$1 onAttained 106 | INFO: Completed initialization 107 | Jul 16, 2019 4:59:43 PM hudson.model.DownloadService$Downloadable load 108 | INFO: Obtained the updated data file for hudson.tasks.Maven.MavenInstaller 109 | Jul 16, 2019 4:59:43 PM hudson.util.Retrier start 110 | INFO: Performed the action check updates server successfully at the attempt #1 111 | Jul 16, 2019 4:59:43 PM hudson.model.AsyncPeriodicWork$1 run 112 | INFO: Finished Download metadata. 5,856 ms 113 | Jul 16, 2019 4:59:43 PM hudson.UDPBroadcastThread run 114 | INFO: Cannot listen to UDP port 33,848, skipping: java.net.SocketException: Can't assign requested address 115 | Jul 16, 2019 4:59:43 PM hudson.WebAppMain$3 run 116 | INFO: Jenkins is fully up and running 117 | ``` 118 | 119 | Paste the password from the clipboard into the password field. 120 | 121 | ![Unlock Jenkins](./images/unlock-jenkins.png) 122 | 123 | Select the option to install recommended plugins. 124 | 125 | ![Customize Jenkins](./images/customize-jenkins.png) 126 | 127 | The installation process will run for a couple of minutes. 128 | 129 | ![Plugin Installation Progress](./images/plugin-installation-progress.png) 130 | 131 | Enter credentials for your first admin user. 132 | 133 | ![First Admin User](./images/first-admin-user.png) 134 | 135 | Skip over the instance configuration step. 136 | 137 | ![Instance Configuration](./images/instance-configuration.png) 138 | 139 | Jenkins indicates that it is ready for use. 140 | 141 | ![Jenkins Is Ready](./images/jenkins-ready.png) 142 | 143 | The installation process is complete. You should land on the dashboard. 144 | 145 | ![Jenkins Dashboard](./images/jenkins-dashboard.png) -------------------------------------------------------------------------------- /exercises/02-job-creation/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 2 2 | 3 | In this exercise, you will create a new freestyle job and configure it to build with parameters. You'll also have a look at the Jenkins home directory to inspect the directory structure representing the job. 4 | 5 | ## Defining, Configuring and Organizing a Job 6 | 7 | 1. From the dashboard, click the "New Item" button. 8 | 2. Enter the item name `my-freestyle-job` and select "Freestyle project". Press the "OK" button. 9 | 3. In the job configuration, define the option to only keep the last 2 builds. Provide the description "A simple freestyle job". Upon building the project, a String parameter named `MESSAGE` should be provided. Press the "Save" button. 10 | 4. Trigger a new build by pressing the "Build with Parameters" button. Enter a value for the `MESSAGE` parameter. The build should finish successfully. Locate the provided parameter value in the build information. 11 | 5. Run the build three more times. What do you see? 12 | 6. Create a new view named `test`. Add the job to the view. 13 | 7. Create a new folder named `freestyle` as part of the view. Move the job into the folder. 14 | 8. Locate the build information in `$JENKINS_HOME`. Inspect the directory structure. -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/build-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/build-history.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/build-with-params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/build-with-params.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/job-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/job-configuration.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/job-in-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/job-in-folder.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/job-in-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/job-in-view.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/move-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/move-job.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/new-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/new-folder.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/new-freestyle-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/new-freestyle-job.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/images/new-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/02-job-creation/solution/images/new-view.png -------------------------------------------------------------------------------- /exercises/02-job-creation/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | We'll start by creating the new freestyle job. 4 | 5 | ![New Freestyle Job](./images/new-freestyle-job.png) 6 | 7 | Configure the job as follows. 8 | 9 | ![Job Configuration](./images/job-configuration.png) 10 | 11 | The build will ask for a parameter value when triggered. 12 | 13 | ![Build with Parameters](./images/build-with-params.png) 14 | 15 | The build history only stores the previous two builds. 16 | 17 | ![Build History](./images/build-history.png) 18 | 19 | Create a new view. 20 | 21 | ![New View](./images/new-view.png) 22 | 23 | After adding the job to the view, it will show up in a separate tab. 24 | 25 | ![Job in View](./images/job-in-view.png) 26 | 27 | Create a new folder. 28 | 29 | ![New Folder](./images/new-folder.png) 30 | 31 | The job became a child of the folder after moving it there. 32 | 33 | ![Job In Folder](./images/job-in-folder.png) 34 | 35 | Navigating to the `job` directory under the Jenkins Home reveals the build history. 36 | 37 | ```bash 38 | $ cd /Users/bmuschko/.jenkins/jobs/freestyle/jobs 39 | $ tree my-freestyle-job 40 | my-freestyle-job 41 | ├── builds 42 | │   ├── 1 43 | │   │   ├── build.xml 44 | │   │   ├── changelog.xml 45 | │   │   └── log 46 | │   ├── 2 47 | │   │   ├── build.xml 48 | │   │   ├── changelog.xml 49 | │   │   └── log 50 | │   ├── 3 51 | │   │   ├── build.xml 52 | │   │   ├── changelog.xml 53 | │   │   └── log 54 | │   ├── lastFailedBuild -> -1 55 | │   ├── lastStableBuild -> 3 56 | │   ├── lastSuccessfulBuild -> 3 57 | │   ├── lastUnstableBuild -> -1 58 | │   ├── lastUnsuccessfulBuild -> -1 59 | │   └── legacyIds 60 | ├── config.xml 61 | ├── lastStable -> builds/lastStableBuild 62 | ├── lastSuccessful -> builds/lastSuccessfulBuild 63 | └── nextBuildNumber 64 | 65 | 8 directories, 15 files 66 | ``` -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 2 | 3 | In this exercise, you will configure the job to build based on a cron definition. Moreover, you will create a downstream job that should be built automatically if the upstream job is successful. 4 | 5 | ## Configuring Build Triggers and Steps for a Job 6 | 7 | 1. Add a default value for the build parameter named `MESSAGE` e.g. `Hello World!`. 8 | 2. Create a build trigger that builds the project every minute. 9 | 3. Add a build step that executes the shell command `echo "Message: $MESSAGE"`. The message is value of the parameter. On Windows, you have to execute a Windows batch command. 10 | 4. After a minute the first execution should have been triggered. Check the log output of the build and find the rendered message. 11 | 5. Create another freestyle project named `downstream-job` in the same folder. 12 | 6. Configure the initial job to execute the `downstream-job` if it was stable. 13 | -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/solution/images/both-jobs-in-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/03-build-trigger-and-steps/solution/images/both-jobs-in-folder.png -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/solution/images/build-downstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/03-build-trigger-and-steps/solution/images/build-downstream.png -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/solution/images/build-trigger-and-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/03-build-trigger-and-steps/solution/images/build-trigger-and-step.png -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/solution/images/executed-downstream-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/03-build-trigger-and-steps/solution/images/executed-downstream-job.png -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/solution/images/string-parameter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/03-build-trigger-and-steps/solution/images/string-parameter.png -------------------------------------------------------------------------------- /exercises/03-build-trigger-and-steps/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Modify the existing build parameter. 4 | 5 | ![String Parameter](./images/string-parameter.png) 6 | 7 | Add the cron build trigger and the build step. 8 | 9 | ![Build Trigger And Step](./images/build-trigger-and-step.png) 10 | 11 | The output render the interpolated value of the `echo` command. 12 | 13 | ```bash 14 | Started by timer 15 | Running as SYSTEM 16 | Building in workspace /Users/bmuschko/.jenkins/workspace/freestyle/my-freestyle-job 17 | [my-freestyle-job] $ /bin/sh -xe /var/folders/02/3dgzjkqj4kz0g7lnrk0w93c00000gn/T/jenkins3548490840940668236.sh 18 | + echo "Message: Hello World!" 19 | Message: Hello World! 20 | Finished: SUCCESS 21 | ``` 22 | 23 | Create a new job in the same folder. 24 | 25 | ![Both Jobs In Folder](./images/both-jobs-in-folder.png) 26 | 27 | Configure the downstream job from the initial job. 28 | 29 | ![Downstream Job Configuration](./images/build-downstream.png) 30 | 31 | The downstream job indicates the upstream job. 32 | 33 | ![Downstream Build Information](./images/executed-downstream-job.png) -------------------------------------------------------------------------------- /exercises/04-scm-configuration/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 4 2 | 3 | In this exercise, you will create a new job that pulls the source code from a [repository on GitHub](https://github.com/bmuschko/gradle-initializr) named Gradle Initializr. Upon triggering a build, the job will execute a Gradle build. 4 | 5 | ## Configuring a GitHub Repository 6 | 7 | 1. Create a new freestyle job named `gradle-initializr`. 8 | 2. Configure Git as the SCM and use the repository URL `https://github.com/bmuschko/gradle-initializr.git`. Only build from the branch `master`. 9 | 3. Add a build step "Invoke Gradle script" to run the Gradle command `clean build` using the Wrapper. 10 | 4. Trigger a build and look at the output. -------------------------------------------------------------------------------- /exercises/04-scm-configuration/solution/images/git-scm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/04-scm-configuration/solution/images/git-scm.png -------------------------------------------------------------------------------- /exercises/04-scm-configuration/solution/images/gradle-build-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/04-scm-configuration/solution/images/gradle-build-step.png -------------------------------------------------------------------------------- /exercises/04-scm-configuration/solution/images/new-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/04-scm-configuration/solution/images/new-job.png -------------------------------------------------------------------------------- /exercises/04-scm-configuration/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Create a new job. 4 | 5 | ![Freestyle Job](./images/new-job.png) 6 | 7 | Configure the Git SCM and point the proper URL. The default is the `master` branch. 8 | 9 | ![Git SCM](./images/git-scm.png) 10 | 11 | Create the Gradle build step. Ensure to select the "Use Gradle Wrapper" option. 12 | 13 | ![Git SCM](./images/gradle-build-step.png) 14 | 15 | The build log should look similar to the output below. 16 | 17 | ```bash 18 | Started by user Admin 19 | Running as SYSTEM 20 | Building in workspace /Users/bmuschko/.jenkins/workspace/gradle-initializr 21 | No credentials specified 22 | Cloning the remote Git repository 23 | Cloning repository git@github.com:bmuschko/gradle-initializr.git 24 | > git init /Users/bmuschko/.jenkins/workspace/gradle-initializr # timeout=10 25 | Fetching upstream changes from git@github.com:bmuschko/gradle-initializr.git 26 | > git --version # timeout=10 27 | > git fetch --tags --force --progress git@github.com:bmuschko/gradle-initializr.git +refs/heads/*:refs/remotes/origin/* 28 | > git config remote.origin.url git@github.com:bmuschko/gradle-initializr.git # timeout=10 29 | > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10 30 | > git config remote.origin.url git@github.com:bmuschko/gradle-initializr.git # timeout=10 31 | Fetching upstream changes from git@github.com:bmuschko/gradle-initializr.git 32 | > git fetch --tags --force --progress git@github.com:bmuschko/gradle-initializr.git +refs/heads/*:refs/remotes/origin/* 33 | > git rev-parse refs/remotes/origin/master^{commit} # timeout=10 34 | > git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10 35 | Checking out Revision 8e725ea3507f5ac0f8251234e1ff0f214b228d3d (refs/remotes/origin/master) 36 | > git config core.sparsecheckout # timeout=10 37 | > git checkout -f 8e725ea3507f5ac0f8251234e1ff0f214b228d3d 38 | Commit message: "Update docs" 39 | First time build. Skipping changelog. 40 | [Gradle] - Launching build. 41 | [gradle-initializr] $ gradle clean build 42 | Starting a Gradle Daemon (subsequent builds will be faster) 43 | > Task :clean UP-TO-DATE 44 | > Task :compileJava 45 | > Task :compileGroovy NO-SOURCE 46 | > Task :processResources 47 | > Task :classes 48 | > Task :bootJar 49 | > Task :jar SKIPPED 50 | > Task :assemble 51 | > Task :compileTestJava NO-SOURCE 52 | > Task :compileTestGroovy 53 | > Task :processTestResources NO-SOURCE 54 | > Task :testClasses 55 | > Task :test 56 | > Task :compileIntegrationTestJava NO-SOURCE 57 | > Task :compileIntegrationTestGroovy 58 | > Task :processIntegrationTestResources NO-SOURCE 59 | > Task :integrationTestClasses 60 | > Task :integrationTest 61 | 2019-07-17 10:07:48.803 INFO 67741 --- [ Thread-6] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor' 62 | > Task :check 63 | > Task :build 64 | 65 | BUILD SUCCESSFUL in 30s 66 | 8 actionable tasks: 7 executed, 1 up-to-date 67 | Build step 'Invoke Gradle script' changed build result to SUCCESS 68 | Finished: SUCCESS 69 | ``` 70 | 71 | **As a side note:** The GitHub plugin is [currently broken](https://issues.jenkins-ci.org/browse/JENKINS-11337) if you wanted to build multiple branches with a single job. You will have to model it as a multi-branch pipeline job. -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 5 2 | 3 | In this exercise, you will configure the existing job to execute tests. Furthermore, you'll enhance the job to process the test results and code coverage metrics. 4 | 5 | ## Displaying JUnit and JaCoCo Test Results 6 | 7 | 1. Configure the `gradle-initializr` project to parse the JUnit XML results for unit test. The files can be found in the directory `build/test-results/test`. 8 | 2. Execute the build twice to generate a graph. Have a look at the executed tests in the test results. 9 | 3. Include all test results (unit and integration tests) in the reporting. 10 | 4. Have a look at how the test result trend changes. 11 | 5. Install the [JaCoCo plugin](https://plugins.jenkins.io/jacoco). 12 | 6. Reconfigure the Gradle build step to also generate JaCoCo reports: `clean build jacocoTestReport jacocoIntegrationTestReport`. 13 | 7. Add a post-build action for publish the JaCoCo reports. Use the following path to look for results: `build/jacoco/**.exec`. 14 | 8. Execute the build twice to generate a graph. Have a look at the code coverage results. -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/all-test-result-trend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/all-test-result-trend.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/jacoco-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/jacoco-config.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/jacoco-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/jacoco-plugin.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/jacoco-report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/jacoco-report.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/jacoco-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/jacoco-tasks.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/jacoco-trend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/jacoco-trend.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/publish-all-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/publish-all-tests.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/publish-unit-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/publish-unit-tests.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/images/unit-test-result-trend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/05-test-execution-and-reporting/solution/images/unit-test-result-trend.png -------------------------------------------------------------------------------- /exercises/05-test-execution-and-reporting/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Publish the JUnit reports by pointing to the exact directory containing the XML files. 4 | 5 | ![Publish JUnit Results](./images/publish-unit-tests.png) 6 | 7 | After executing the build twice you will see a test result trend graph. 8 | 9 | ![JUnit Test Trend](./images/unit-test-result-trend.png) 10 | 11 | Publish the JUnit reporting by using a wild card. 12 | 13 | ![Publish All JUnit Results](./images/publish-all-tests.png) 14 | 15 | The trend changes accordingly. 16 | 17 | ![Changed Test Result Trend](./images/all-test-result-trend.png) 18 | 19 | Install the JaCoCo plugin from the Plugin Manager page. 20 | 21 | ![JaCoCo Plugin](./images/jacoco-plugin.png) 22 | 23 | Change the task list executed by the Gradle build step. 24 | 25 | ![JaCoCo Tasks](./images/jacoco-tasks.png) 26 | 27 | Configure JaCoCo reporting. 28 | 29 | ![JaCoCo Configuration](./images/jacoco-config.png) 30 | 31 | The JaCoCo coverage trend renders after executing the build twice. 32 | 33 | ![JaCoCo Coverage Report Trend](./images/jacoco-trend.png) 34 | 35 | You can drill into the details of the report. 36 | 37 | ![JaCoCo Coverage Report Trend](./images/jacoco-report.png) -------------------------------------------------------------------------------- /exercises/06-notifications/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 6 2 | 3 | This exercise will demonstrate the use of a Google Chat room as way to send a notification upon a failed build. Given the locked down nature of Google Chat, this exercise will only be demonstrated by the instructor. 4 | 5 | **This exercise will only be demonstrated on screen. You will be able to follow along if you have a [G Suite account](https://gsuite.google.com/products/chat/).** 6 | 7 | ## Notifying the Team Upon a Broken Build 8 | 9 | 1. Change the list of Gradle tasks to `doesnotexist` to emulate a failure. The build will fail as the task doesn't exist in the build script. 10 | 2. Install the [Google Chat Notification plugin](https://plugins.jenkins.io/google-chat-notification). 11 | 3. Create a new chat room in Google Chat named `jenkins-test` at [https://chat.google.com/](https://chat.google.com/). 12 | 4. For the chat room configure the webhook. 13 | 5. Configure the job to send a notification whenever the job fails. Use the webhook generated on Google Chat. 14 | 6. Execute the build. The build should fail and send a notification to the chat room. -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/add-webhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/add-webhook.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/change-gradle-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/change-gradle-tasks.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/chat-room-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/chat-room-message.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/create-room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/create-room.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/google-chat-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/google-chat-plugin.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/notification-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/notification-config.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/webhook-naming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/webhook-naming.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/images/webhook-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/06-notifications/solution/images/webhook-url.png -------------------------------------------------------------------------------- /exercises/06-notifications/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Change the list of Gradle tasks first. 4 | 5 | ![Gradle Tasks](./images/change-gradle-tasks.png) 6 | 7 | Find the plugin and install it. 8 | 9 | ![Google Chat Plugin](./images/google-chat-plugin.png) 10 | 11 | Add a new chat room. 12 | 13 | ![Chat Room](./images/create-room.png) 14 | 15 | For the chat room, click the little cog icon and create a new webhook. 16 | 17 | ![Add Webhook](./images/add-webhook.png) 18 | 19 | Enter an appropriate name for the webhook. 20 | 21 | ![Webhook Naming](./images/webhook-naming.png) 22 | 23 | Copy the generate webhook URL to the clipboard. 24 | 25 | ![Webhook URL](./images/webhook-url.png) 26 | 27 | In the Jenkins job, create a new Google Chat notification. Add the webhook URL and provide a name. 28 | 29 | ![Notification Configuration](./images/notification-config.png) 30 | 31 | Run a build. It should fail and send a new message to the chat room. 32 | 33 | ![Chat Room Message](./images/chat-room-message.png) -------------------------------------------------------------------------------- /exercises/07-artifacts/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 7 2 | 3 | You will enhance the existing job to generate a JAR file and store it on Jenkins. The job should record the MD5 hash for the artifact. Later, you will configure a downstream job that should be able to reuse the existing artifact. 4 | 5 | ## Storing and Fingerprinting Artifacts 6 | 7 | 1. Create a post-build action for archiving JAR files with the pattern `build/libs/*.jar`. Enable the fingerprinting option. 8 | 2. Execute the build. The build should list the artifact `gradle-initializr-1.0.0.jar`. 9 | 3. Have a look at the recorded fingerprints of this build. 10 | 4. Render the MD5 hash of the artifact and the usage of the artifact. 11 | 5. Install the [Copy Artifacts plugin](https://plugins.jenkins.io/copyartifact). 12 | 6. Create a downstream job named `consumer`. 13 | 7. Configure the downstream job to use the artifact produced by the upstream job. 14 | 8. Run the the build for the job `gradle-initializr`. 15 | 9. Have a look at the fingerprints of the downstream job. -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/archive-artifacts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/archive-artifacts.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/build-artifact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/build-artifact.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/copy-artifacts-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/copy-artifacts-plugin.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/copy-artifacts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/copy-artifacts.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/fingerprint-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/fingerprint-details.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/fingerprints-upstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/fingerprints-upstream.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/recorded-fingerprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/recorded-fingerprint.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/images/trigger-upstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/07-artifacts/solution/images/trigger-upstream.png -------------------------------------------------------------------------------- /exercises/07-artifacts/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | In the job configuration, archive the artifact produced by the build. Check the box for generating fingerprints. 4 | 5 | ![Archive Artifacts](./images/archive-artifacts.png) 6 | 7 | Execute the build and find the archived artifact. 8 | 9 | ![Archived Artifact](./images/build-artifact.png) 10 | 11 | Have a look at the details of the fingerprinting. 12 | 13 | ![Fingerprint Details](./images/fingerprint-details.png) 14 | 15 | Install the Copy Artifacts plugin from the Plugin Manager page. 16 | 17 | ![Copy Artifacts Plugin](./images/copy-artifacts-plugin.png) 18 | 19 | Trigger the downstream job upon success. 20 | 21 | ![Trigger Downstream](./images/trigger-upstream.png) 22 | 23 | Copy artifacts from the upstream job. 24 | 25 | ![Copy Artifact From Upstream](./images/copy-artifacts.png) 26 | 27 | You can see the full usage of the artifact in upstream and downstream jobs. 28 | 29 | ![Fingerprint Usage](./images/fingerprints-upstream.png) -------------------------------------------------------------------------------- /exercises/08-matrix-security/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 8 2 | 3 | In this exercise, you'll create a new user and assign read permissions. You will verify permissions for the admin user and the new user by logging into Jenkins with the appropriate credentials. 4 | 5 | ## Creating a User and Setting Permissions 6 | 7 | 1. Ensure that you are logged in as admin user. 8 | 2. Create a new user named `miller`. Provide the relevant information. 9 | 3. Configure Matrix Security by adding the admin user and the newly created user. The admin user should have all permissions. The `miller` user should only have read permissions for jobs and views. 10 | 4. Log out of the system. 11 | 5. Log in with the user `miller` and navigate to one of the jobs. Notice that you cannot edit the configuration or trigger a new build. -------------------------------------------------------------------------------- /exercises/08-matrix-security/solution/images/create-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/08-matrix-security/solution/images/create-user.png -------------------------------------------------------------------------------- /exercises/08-matrix-security/solution/images/job-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/08-matrix-security/solution/images/job-overview.png -------------------------------------------------------------------------------- /exercises/08-matrix-security/solution/images/matrix-security-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/08-matrix-security/solution/images/matrix-security-permissions.png -------------------------------------------------------------------------------- /exercises/08-matrix-security/solution/images/user-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/08-matrix-security/solution/images/user-login.png -------------------------------------------------------------------------------- /exercises/08-matrix-security/solution/images/user-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/08-matrix-security/solution/images/user-overview.png -------------------------------------------------------------------------------- /exercises/08-matrix-security/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Create the new user. 4 | 5 | ![Create User](./images/create-user.png) 6 | 7 | The new user is now listed. 8 | 9 | ![User Overview](./images/user-overview.png) 10 | 11 | Configure users and permissions. 12 | 13 | ![Matrix Security](./images/matrix-security-permissions.png) 14 | 15 | Log in with new user. 16 | 17 | ![Matrix Security](./images/user-login.png) 18 | 19 | The job doesn't allow any editing or build triggering operations. 20 | 21 | ![Job Overview](./images/job-overview.png) -------------------------------------------------------------------------------- /exercises/09-rest-api/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 9 2 | 3 | This exercise explores the use of the REST API to trigger a build for an existing job. You will configure security features as necessary. 4 | 5 | ## Using the REST API for Common Operations 6 | 7 | 1. Create a new user named `buildbot`. 8 | 2. Add administration permissions for the user. 9 | 3. Log in with user `buildbot`. 10 | 4. Generate the API token for the user. 11 | 5. Generate a Jenkins crumb from the command line. 12 | 6. Export the environment variables `JENKINS_CRUMB` and `JENKINS_API_TOKEN` with the correct values. 13 | 7. Trigger a build of the job `gradle-initializr` with a `curl` command using the REST API. 14 | 8. Disable the job `gradle-initializr` with a `curl` command using the REST API. 15 | 9. Reenable the job `gradle-initializr` with a `curl` command using the REST API. 16 | 10. Download the Jenkins CLI client. 17 | 11. Trigger the build of the job `gradle-initializr` with a `curl` command using the Jenkins CLI. 18 | -------------------------------------------------------------------------------- /exercises/09-rest-api/solution/images/api-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/09-rest-api/solution/images/api-token.png -------------------------------------------------------------------------------- /exercises/09-rest-api/solution/images/build-by-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/09-rest-api/solution/images/build-by-user.png -------------------------------------------------------------------------------- /exercises/09-rest-api/solution/images/create-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/09-rest-api/solution/images/create-user.png -------------------------------------------------------------------------------- /exercises/09-rest-api/solution/images/disabled-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/09-rest-api/solution/images/disabled-job.png -------------------------------------------------------------------------------- /exercises/09-rest-api/solution/images/user-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/09-rest-api/solution/images/user-permissions.png -------------------------------------------------------------------------------- /exercises/09-rest-api/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Create the new user. 4 | 5 | ![Create User](./images/create-user.png) 6 | 7 | Add user permissions. 8 | 9 | ![User Permissions](./images/user-permissions.png) 10 | 11 | Generate the API token. 12 | 13 | ![API Token](./images/api-token.png) 14 | 15 | Generate the Jenkins crumb from the CLI. 16 | 17 | ```bash 18 | $ curl -u "buildbot:pwd" 'http://localhost:8080/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' 19 | Jenkins-Crumb:890d0b4c9c1b111deb55b196813a0ae1 20 | $ export JENKINS_CRUMB=Jenkins-Crumb:890d0b4c9c1b111deb55b196813a0ae1 21 | $ export JENKINS_API_TOKEN=11e2f3c68399b6bc3a28bc06e002be104d 22 | ``` 23 | 24 | Trigger a build with the `curl` command. 25 | 26 | ```bash 27 | $ curl -X POST -H "$JENKINS_CRUMB" http://buildbot:$JENKINS_API_TOKEN@localhost:8080/job/gradle-initializr/build 28 | ``` 29 | 30 | Disable the job via the REST API. You will see that the job indicated its status. 31 | 32 | ```bash 33 | $ curl -X POST -H "$JENKINS_CRUMB" http://buildbot:$JENKINS_API_TOKEN@localhost:8080/job/gradle-initializr/disable 34 | ``` 35 | 36 | ![Disabled Job](./images/disabled-job.png) 37 | 38 | Reenable the job. 39 | 40 | ```bash 41 | $ curl -X POST -H "$JENKINS_CRUMB" http://buildbot:$JENKINS_API_TOKEN@localhost:8080/job/gradle-initializr/enable 42 | ``` 43 | 44 | Download the Jenkins URL by calling the URL `localhost:8080/jnlpJars/jenkins-cli.jar` from the browser. 45 | 46 | In the terminal, navigate to the directory that contains the Jenkins CLI JAR file. Use the Jenkins CLI to trigger a build with the correct command. This simply provide the password instead of the API token. 47 | 48 | ```bash 49 | $ java -jar jenkins-cli.jar -s http://localhost:8080 -auth buildbot:pwd build gradle-initializr 50 | ``` -------------------------------------------------------------------------------- /exercises/10-distributed-builds/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | config.vm.box = "ubuntu/trusty64" 10 | 11 | (1..2).each do |number| 12 | config.vm.define "jenkins-agent-#{number}" do |node| 13 | node.vm.network "forwarded_port", guest: 22, host: "909#{number}" 14 | node.vm.network "private_network", ip: "192.168.56.20#{number}" 15 | node.vm.hostname = "jenkins-agent-#{number}" 16 | end 17 | end 18 | 19 | config.vm.provider "virtualbox" do |vb| 20 | vb.memory = "2048" 21 | end 22 | 23 | config.vm.provision "shell", inline: <<-SHELL 24 | sudo add-apt-repository ppa:openjdk-r/ppa -y 25 | sudo apt-get update 26 | sudo apt-get -y install openjdk-11-jdk 27 | sudo update-alternatives --config java 28 | sudo apt-get -y install git 29 | sudo useradd -m jenkins 30 | echo "jenkins:jenkins" | sudo chpasswd 31 | sudo sed -i 's/pkcs12/jks/g' /etc/java-11-openjdk/security/java.security 32 | sudo rm /etc/ssl/certs/java/cacerts 33 | sudo update-ca-certificates -f 34 | SHELL 35 | end 36 | -------------------------------------------------------------------------------- /exercises/10-distributed-builds/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 10 2 | 3 | You will start a VM using Vagrant as practice environment to set up a distributed build with 2 agents. 4 | 5 | ## Starting up the VMs 6 | 7 | 1. To start up the VMs, navigate to this directory. It should contain a `Vagrantfile`. 8 | 2. Run the command `vagrant up`. Bringing up the VMs might take some minutes. The two VMs will be available with the IP address `192.168.56.201` and `192.168.56.202`. The username/password credentials for those VMs are `jenkins:jenkins`. 9 | 10 | ## Configuring and Executing Jobs in a Distributed Build 11 | 12 | 1. Go to "Manage Jenkins" > "Manage Nodes". You should see a single controller node. 13 | 2. Configure the controller node by setting the # of executor value to 0. That will take care of never using the controller for job workload. 14 | 3. Add new nodes by clicking "New Nodes". Enter an appropriate name and select the option "Permanent Agent". Use the remote directory `/home/jenkins/jenkins_slave` and set the # of executors to 2. Enter the IP address `192.168.56.201` as host and provide credentials by selecting "Username with password". You will likely have to create a new credential. To keep things easy select "Non verifying Verification Strategy". 15 | 4. Trigger a build. You should see that the build is only executed on the agent and not the controller node. 16 | 5. Add one more agent with the IP address `192.168.56.202`. 17 | 6. Trigger a build. You should see that the build can be executed on any of the agents. 18 | 7. Configure one of the agents to only build jobs with a specific label e.g. `java`. Change the "Usage" field to "Only build jobs with label expressions matching this node". 19 | 8. Configure the `gradle-initializr` job and assign the label `java`. 20 | 9. Trigger a build of the `gradle-initializr` job. It is only executed by the dedicated agent. 21 | 10. Trigger other jobs that do not have the label `java` assigned to them. They are waiting for an agent that can execute the build. 22 | 23 | ## Shutting down the VMs 24 | 25 | 1. After you are done with the exercise, navigate to the directory containing the `Vagrantfile`. 26 | 2. Tear down the VM by executing `vagrant destroy -f`. 27 | -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/agent-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/agent-config.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/agent-label-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/agent-label-config.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/build-for-label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/build-for-label.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/job-label-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/job-label-config.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/master-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/master-config.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/node-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/node-overview.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/images/queued-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/10-distributed-builds/solution/images/queued-job.png -------------------------------------------------------------------------------- /exercises/10-distributed-builds/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Configure the controller node. 4 | 5 | ![Master Configuration](./images/master-config.png) 6 | 7 | Add a new agent node. 8 | 9 | ![Agent Configuration](./images/agent-config.png) 10 | 11 | You will see that the controller node isn't even listed anymore in the executor overview. 12 | 13 | ![Node Overview](./images/node-overview.png) 14 | 15 | Reconfigure the agent node to only build jobs with a specific label. 16 | 17 | ![Agent Label Configuration](./images/agent-label-config.png) 18 | 19 | Reconfigure the job to only use agents that can handle a specific label. 20 | 21 | ![Job Label Configuration](./images/job-label-config.png) 22 | 23 | A build of the job is now only handled by an agent with the assigned label. 24 | 25 | ![Build For Labeled Agent](./images/build-for-label.png) 26 | 27 | Other jobs sit in a queue waiting for an agent that can handle the execution criteria. 28 | 29 | ![Build For Labeled Agent](./images/queued-job.png) 30 | -------------------------------------------------------------------------------- /exercises/11-pipeline-job/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 11 2 | 3 | In this exercise, you will set up a new multi-branch pipeline job and point it to an existing repository on GitHub. You will visualize the pipeline with the standard view and the Blue Ocean plugin. The job uses [SonarCloud](https://sonarcloud.io/) to capture static code analysis metrics and deploys the application to [Heroku](https://www.heroku.com/). You will need to create accounts for both services to follow along. Both services offer free tiers. 4 | 5 | ## Creating a Pipeline Job 6 | 7 | 1. Have a look at the [repository](https://github.com/bmuschko/todo-spring-boot) `bmuschko/todo-spring-boot` on GitHub. The repository already contains the build definition in the form of a `Jenkinsfile`. Identify each of the steps. Fork and clone the repository so you can later on create and push new branches. 8 | 2. In Jenkins, set up the credentials `SONARCLOUD_TOKEN` and `HEROKU_API_KEY` if they don't exist yet. The SonarCloud token can be retrieved under [My Account > Security](https://sonarcloud.io/account/security/). The Heroku API can be generated under [Account Settings > API Key](https://dashboard.heroku.com/account). 9 | 3. Create a new multi-branch pipeline job for the repository. 10 | 4. The initial build is triggered automatically. Have a look at the pipeline in the standard view and its console output. 11 | 5. Install the [Blue Ocean plugin](https://plugins.jenkins.io/blueocean). 12 | 6. Open the Blue Ocean pipeline visualization for the job. Manually trigger the deployment step. Open a browser with the deployed application on Heroku. 13 | 7. Create a new branch named `bugfix` and push it to the remote repository. The code should be based off of `master`. 14 | 8. Select "Scan Multibranch Pipeline Now". The job should build the new branch. -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/blue-ocean-manual-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/blue-ocean-manual-step.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/blue-ocean-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/blue-ocean-plugin.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/bugfix-branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/bugfix-branch.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/console-manual-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/console-manual-step.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/credentials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/credentials.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/finished-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/finished-build.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/job-only-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/job-only-master.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/job-scm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/job-scm.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/multi-branch-pipeline-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/multi-branch-pipeline-job.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/standard-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/standard-pipeline.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/images/triggered-manual-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/11-pipeline-job/solution/images/triggered-manual-step.png -------------------------------------------------------------------------------- /exercises/11-pipeline-job/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | The created credentials required for the job. 4 | 5 | ![Credentials](./images/credentials.png) 6 | 7 | Create a new item from the dashboard. 8 | 9 | ![Multi-Branch Pipeline Job](./images/multi-branch-pipeline-job.png) 10 | 11 | Point the job to the SCM. 12 | 13 | ![Multi-Branch Pipeline Job](./images/job-scm.png) 14 | 15 | Upon pressing the OK button, the job will scan all available branches in the repository. 16 | 17 | ```bash 18 | Started 19 | [Thu Jul 18 09:23:53 MDT 2019] Starting branch indexing... 20 | > git --version # timeout=10 21 | > git ls-remote --symref git@github.com:bmuschko/todo-spring-boot.git # timeout=10 22 | Creating git repository in /Users/bmuschko/.jenkins/caches/git-a68a5e08a54549aaef01872e9adb6218 23 | > git init /Users/bmuschko/.jenkins/caches/git-a68a5e08a54549aaef01872e9adb6218 # timeout=10 24 | Setting origin to git@github.com:bmuschko/todo-spring-boot.git 25 | > git config remote.origin.url git@github.com:bmuschko/todo-spring-boot.git # timeout=10 26 | Fetching & pruning origin... 27 | Listing remote references... 28 | > git config --get remote.origin.url # timeout=10 29 | > git --version # timeout=10 30 | > git ls-remote -h git@github.com:bmuschko/todo-spring-boot.git # timeout=10 31 | Fetching upstream changes from origin 32 | > git config --get remote.origin.url # timeout=10 33 | > git fetch --tags --force --progress origin +refs/heads/*:refs/remotes/origin/* --prune 34 | Checking branches... 35 | Checking branch master 36 | ‘Jenkinsfile’ found 37 | Met criteria 38 | Scheduled build for branch: master 39 | Processed 1 branches 40 | [Thu Jul 18 09:23:57 MDT 2019] Finished branch indexing. Indexing took 4 sec 41 | Finished: SUCCESS 42 | ``` 43 | 44 | You can see the different stages of the pipeline in the standard view. 45 | 46 | ![Standard Pipeline](./images/standard-pipeline.png) 47 | 48 | You can see the different stages of the pipeline in the standard view. 49 | 50 | ![Standard Pipeline](./images/standard-pipeline.png) 51 | 52 | To console output allows for triggering or aborting the manual deployment step. 53 | 54 | ![Console Manual Step](./images/console-manual-step.png) 55 | 56 | Install the Blue Ocean plugin. 57 | 58 | ![Blue Ocean Plugin](./images/blue-ocean-plugin.png) 59 | 60 | The Blue Ocean pipeline view offers a UI element for triggering a manual step. 61 | 62 | ![Blue Ocean Manual Step](./images/blue-ocean-manual-step.png) 63 | 64 | Press the button for processing with the manual step. 65 | 66 | ![Blue Ocean Triggered Manual Step](./images/triggered-manual-step.png) 67 | 68 | The finished pipeline in Blue Ocean. 69 | 70 | ![Blue Ocean Finished Pipeline](./images/finished-build.png) 71 | 72 | Check out the repository and push a new branch. 73 | 74 | ```bash 75 | $ git clone git@github.com:bmuschko/todo-spring-boot.git 76 | Cloning into 'todo-spring-boot'... 77 | remote: Enumerating objects: 230, done. 78 | remote: Total 230 (delta 0), reused 0 (delta 0), pack-reused 230 79 | Receiving objects: 100% (230/230), 108.69 KiB | 452.00 KiB/s, done. 80 | Resolving deltas: 100% (105/105), done. 81 | $ cd todo-spring-boot 82 | $ git branch bugfix 83 | $ git checkout bugfix 84 | Switched to branch 'bugfix' 85 | $ git push origin bugfix 86 | Total 0 (delta 0), reused 0 (delta 0) 87 | remote: 88 | remote: Create a pull request for 'bugfix' on GitHub by visiting: 89 | remote: https://github.com/bmuschko/todo-spring-boot/pull/new/bugfix 90 | remote: 91 | To github.com:bmuschko/todo-spring-boot.git 92 | * [new branch] bugfix -> bugfix 93 | ``` 94 | 95 | After scanning the repository, the new branch will be available and was triggered to build automatically. 96 | 97 | ![Bugfix Branch](./images/bugfix-branch.png) -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 12 2 | 3 | In this exercise, you'll create a declarative pipeline for a Go-based project. The `Jenkinsfile` will use some of the basic features of the pipeline DSL. 4 | 5 | ## Writing a Basic Jenkinsfile 6 | 7 | 1. Create a new GitHub repository named `go-on-jenkins`. 8 | 2. Create a new `Jenkinsfile` in the root directory of the repository as well as a simple `main.go` file. The main function just prints "Hello World!". The repository https://github.com/bmuschko/go-helloworld-template shows an example. 9 | 3. Commit the files and push them to the remote repository. 10 | 4. Set up a new pipeline job for this repository in Jenkins. 11 | 5. Install the [Jenkins Go plugin](https://plugins.jenkins.io/golang). 12 | 6. Configure the latest Go runtime as global tool. Use a name that reflects the Go version. Remember the name for the next step. 13 | 7. Enhance the `Jenkinsfile` based on the following requirements. The Jenkinsfile should use the declarative syntax. 14 | * The job can run on all agents. 15 | * The job sets the environment variable `GO111MODULES=on`. 16 | * The job uses the Go runtime from the global tool definition (see previous step). 17 | * The job specifies one build stage named "Build". The build stage executes the shell command `go build`. -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/solution/images/declarative-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/12-basic-jenkinsfile/solution/images/declarative-pipeline.png -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/solution/images/go-global-tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/12-basic-jenkinsfile/solution/images/go-global-tool.png -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/solution/images/go-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/12-basic-jenkinsfile/solution/images/go-plugin.png -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/solution/images/job-scm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/12-basic-jenkinsfile/solution/images/job-scm.png -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/solution/images/new-job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/12-basic-jenkinsfile/solution/images/new-job.png -------------------------------------------------------------------------------- /exercises/12-basic-jenkinsfile/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Create a new job. 4 | 5 | ![New Job](./images/new-job.png) 6 | 7 | Configure the appropriate SCM. 8 | 9 | ![Job SCM](./images/job-scm.png) 10 | 11 | Install the Go plugin. 12 | 13 | ![Go Plugin](./images/go-plugin.png) 14 | 15 | Configure a Go runtime as global tool. 16 | 17 | ![Go Global Tool](./images/go-global-tool.png) 18 | 19 | The `main.go` file could similar to the one below. 20 | 21 | ```go 22 | package main 23 | 24 | import "fmt" 25 | 26 | func main() { 27 | fmt.Println("hello world") 28 | } 29 | ``` 30 | 31 | The final `Jenkinsfile` looks similar to the solution below. 32 | 33 | ```groovy 34 | pipeline { 35 | agent any 36 | tools { 37 | go 'go-1.12' 38 | } 39 | environment { 40 | GO111MODULE = 'on' 41 | } 42 | stages { 43 | stage('Build') { 44 | steps { 45 | sh 'go build' 46 | } 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | A build of the job installs the Go runtime and executes the build step. 53 | 54 | ![Declarative Pipeline](./images/declarative-pipeline.png) -------------------------------------------------------------------------------- /exercises/13-advanced-jenkinsfile/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 13 2 | 3 | We'll want to enhance the existing Go pipeline by additional stages and implement a release workflow. The project is going to use an external tool called [GoReleaser](https://goreleaser.com/) to publish cross-compiled artifacts to [GitHub Releases](https://help.github.com/en/github/administering-a-repository/creating-releases). The binaries should only be released if the commit has been tagged. You will need to create an account on [CodeCov](https://codecov.io/) and set up a [GitHub token](https://github.com/settings/tokens) to follow along. 4 | 5 | ## Enhancing a Pipeline With Advanced Features 6 | 7 | 1. Add stage named `Test` that executes the Go `test` command. 8 | * Add a build step that runs the shell command `go test ./...`. 9 | * Generate code coverage metrics by adding the option `-coverprofile=coverage.txt` to the build step. 10 | * Publish the code coverage metrics to CodeCov by sending a curl command `curl -s https://codecov.io/bash | bash -s -`. 11 | 2. Add a stage named `Code Analysis` that uses [golangci-lint](https://github.com/golangci/golangci-lint) to detect issues with the code. 12 | * Add a build step for installing the `golangci-lint` with the shell command `curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.18.0` 13 | * Add a build step that runs `golangci-lint` with the shell command `golangci-lint run`. 14 | 3. Add a stage named `Release` that uses [GoReleaser](https://github.com/goreleaser/goreleaser). 15 | * Only build this step if the commit has been tagged. 16 | * Set up a credential in Jenkins named `github_token` and set your [GitHub token](https://github.com/settings/tokens). Create a new token if you didn't set up one yet. 17 | * Retrieve the credential and set the value as environment variable named `GITHUB_TOKEN`. 18 | * Add a build step that runs the shell command `curl -sL https://git.io/goreleaser | bash`. -------------------------------------------------------------------------------- /exercises/13-advanced-jenkinsfile/solution/images/codecov_token_credentials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/13-advanced-jenkinsfile/solution/images/codecov_token_credentials.png -------------------------------------------------------------------------------- /exercises/13-advanced-jenkinsfile/solution/images/github_token_credentials.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/13-advanced-jenkinsfile/solution/images/github_token_credentials.png -------------------------------------------------------------------------------- /exercises/13-advanced-jenkinsfile/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | You can implement the "Test" stage as follows. 4 | 5 | ```groovy 6 | stage('Test') { 7 | steps { 8 | sh 'go test ./... -coverprofile=coverage.txt' 9 | sh "curl -s https://codecov.io/bash | bash -s -" 10 | } 11 | } 12 | ``` 13 | 14 | You can implement the "Code Analysis" stage as follows. 15 | 16 | ```groovy 17 | stage('Code Analysis') { 18 | steps { 19 | sh 'curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.18.0' 20 | sh 'golangci-lint run' 21 | } 22 | } 23 | ``` 24 | 25 | Create the credentials for the GitHub token. 26 | 27 | ![GitHub Credentials](./images/github_token_credentials.png) 28 | 29 | You can implement the "Release" stage as follows. 30 | 31 | ```groovy 32 | stage('Release') { 33 | when { 34 | buildingTag() 35 | } 36 | environment { 37 | GITHUB_TOKEN = credentials('GITHUB_TOKEN') 38 | } 39 | steps { 40 | sh 'curl -sL https://git.io/goreleaser | bash' 41 | } 42 | } 43 | ``` 44 | 45 | The final `Jenkinsfile` looks similar to the solution below. 46 | 47 | ```groovy 48 | pipeline { 49 | agent any 50 | tools { 51 | go 'go-1.12' 52 | } 53 | environment { 54 | GO111MODULE = 'on' 55 | } 56 | stages { 57 | stage('Build') { 58 | steps { 59 | sh 'go build' 60 | } 61 | } 62 | stage('Test') { 63 | steps { 64 | sh 'go test ./... -coverprofile=coverage.txt' 65 | sh "curl -s https://codecov.io/bash | bash -s -" 66 | } 67 | } 68 | stage('Code Analysis') { 69 | steps { 70 | sh 'curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.18.0' 71 | sh 'golangci-lint run' 72 | } 73 | } 74 | stage('Release') { 75 | when { 76 | buildingTag() 77 | } 78 | environment { 79 | GITHUB_TOKEN = credentials('GITHUB_TOKEN') 80 | } 81 | steps { 82 | sh 'curl -sL https://git.io/goreleaser | bash' 83 | } 84 | } 85 | } 86 | } 87 | ``` -------------------------------------------------------------------------------- /exercises/14-shared-library/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 14 2 | 3 | In this exercise, you'll take the existing pipeline definition from the previous exercise and turn it into a reusable shared library. 4 | 5 | ## Writing and Using a Shared Library 6 | 7 | 1. Set up a new GitHub repository named `jenkins-standard-go-pipeline`. It will define a standard pipeline definition for Go projects implemented as shared library. 8 | 2. Add the file `vars/standard.groovy` that defines the declarative pipeline as global variable. Make the Go tool name and golang-ci version configurable with the help of parameters. 9 | 3. Push the changes to the `master` branch and configure the shared library in Jenkins. 10 | 4. Configure the shared library for consumption in Jenkins with the name `go-pipeline`. 11 | 5. Set up a new GitHub repository named `go-project-by-template`. Initialize a new Go project by running `go mod init github.com/bmuschko/hello-world` and adding a simple `main.go` file. 12 | 6. Add a new `Jenkinsfile`. Consume the shared library and call the global variable. 13 | 7. Trigger a build and visualize the pipeline in Jenkins. -------------------------------------------------------------------------------- /exercises/14-shared-library/solution/images/pipeline-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/14-shared-library/solution/images/pipeline-view.png -------------------------------------------------------------------------------- /exercises/14-shared-library/solution/images/shared-library-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/exercises/14-shared-library/solution/images/shared-library-config.png -------------------------------------------------------------------------------- /exercises/14-shared-library/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | The directory structure of shared library repository should have the following structure. 4 | 5 | ``` 6 | . 7 | └── vars 8 | └── standard.groovy 9 | 10 | 1 directory, 1 file 11 | ``` 12 | 13 | Define the pipeline as global variable in the file `standard.groovy`. 14 | 15 | ```groovy 16 | def call(String goToolName = 'go-1.12', String golangCiVersion = 'v1.18.0') { 17 | pipeline { 18 | agent any 19 | tools { 20 | go "$goToolName" 21 | } 22 | environment { 23 | GO111MODULE = 'on' 24 | } 25 | stages { 26 | stage('Compile') { 27 | steps { 28 | sh 'go build' 29 | } 30 | } 31 | stage('Test') { 32 | steps { 33 | sh 'go test ./... -coverprofile=coverage.txt' 34 | sh "curl -s https://codecov.io/bash | bash -s -" 35 | } 36 | } 37 | stage('Code Analysis') { 38 | steps { 39 | sh "curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin $golangCiVersion" 40 | sh 'golangci-lint run' 41 | } 42 | } 43 | stage('Release') { 44 | when { 45 | buildingTag() 46 | } 47 | environment { 48 | GITHUB_TOKEN = credentials('GITHUB_TOKEN') 49 | } 50 | steps { 51 | sh 'curl -sL https://git.io/goreleaser | bash' 52 | } 53 | } 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | Configure the shared library under _Manage Jenkins > Configure System_. 60 | 61 | ![Shared Library Configuration](./images/shared-library-config.png) 62 | 63 | The directory structure should look as shown below. 64 | 65 | ``` 66 | . 67 | ├── Jenkinsfile 68 | ├── go.mod 69 | └── main.go 70 | 71 | 0 directories, 3 files 72 | ``` 73 | 74 | The `Jenkinsfile` uses the shared library and calls the global variable. Optionally, you can configure the pipeline by passing in parameters. 75 | 76 | ```groovy 77 | @Library('go-pipeline') _ 78 | 79 | standard() 80 | ``` 81 | 82 | The resulting build will go through all the pipeline stages defined in the shared library. 83 | 84 | ![Pipeline View](./images/pipeline-view.png) -------------------------------------------------------------------------------- /prerequisites/instructions.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | Excercise will require the tooling listed below. Ensure that all of those tools have been installed before attending the training if you want to following along. The training does not reserve time for setting up or verifying the installed tools or their respective versions. 4 | 5 | ## Recommended preparation 6 | 7 | * Attendees will need access to a Jenkins instance, either local or remote. We will walk through the [installation process during the class](../exercises/01-jenkins-installation/instructions.md), but please download all relevant binaries for your OS in advance. Please reference the Jenkins user documentation. 8 | * [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed on the local machine 9 | * A free [account on GitHub](https://github.com/) (optional, but recommended so you can pursue the exercises that use GitHub as a source code repository) 10 | * Install [JDK 8 or higher](https://www.oracle.com/technetwork/java/javase/downloads/index.html) (optional, but recommended so you can pursue the Java-based exercises) 11 | * Install [Vagrant](https://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) (optional, but recommended so you can pursue the distributed builds exercise) 12 | * Some of the later exercises will ask you create an account on external services e.g. Heroku or CodeCov. All of those services provide a free tier. Please set up the following accounts if you want to follow along: 13 | * [SonarCloud](https://sonarcloud.io/) 14 | * [Heroku](https://www.heroku.com/) 15 | * [CodeCov](https://codecov.io/) -------------------------------------------------------------------------------- /slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/cje-crash-course/442db915e2c50b717b2671cd53b546e3c0c2eb05/slides.pdf --------------------------------------------------------------------------------