diff --git a/.gitignore b/.gitignore index 496ee2c..30ee891 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.DS_Store \ No newline at end of file +.DS_Store +.vagrant \ No newline at end of file diff --git a/exercises/01-jenkins-installation/instructions.md b/exercises/01-jenkins-installation/instructions.md index b3364c4..289d0f6 100644 --- a/exercises/01-jenkins-installation/instructions.md +++ b/exercises/01-jenkins-installation/instructions.md @@ -1,12 +1,12 @@ # Exercise 1 -In this exercise, you will practice installing and configuring Jenkins on your machine. +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. ## Installing Jenkins 1. Download the [latest stable Jenkins WAR file](http://mirrors.jenkins.io/war-stable/latest/jenkins.war). To download from the command line, use `wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war`. 2. Open a terminal and navigate to the directory containing the WAR file. -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`. After a couple of seconds you should see the message "Jenkins is fully up and running" in the log output. +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. 4. Open a browser of your choice and navigate to `localhost:`. The default is `localhost:8080`. 5. In the screen named "Unlock Jenkins", enter the password from the console output. Press the "Continue" button. 6. In the screen named "Customize Jenkins", select the option "Install suggested plugins". diff --git a/exercises/02-job-creation/instructions.md b/exercises/02-job-creation/instructions.md index 1ec5d9a..7ca95c5 100644 --- a/exercises/02-job-creation/instructions.md +++ b/exercises/02-job-creation/instructions.md @@ -5,10 +5,10 @@ In this exercise, you will create a new freestyle job and configure it to build ## Defining, Configuring and Organizing a Job 1. From the dashboard, click the "New Item" button. -2. Enter the item name "my-freestyle-job" and select "Freestyle project". Press the "OK" button. +2. Enter the item name `my-freestyle-job` and select "Freestyle project". Press the "OK" button. 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. 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. 5. Run the build two more times. What do you see? -6. Create a new view named "test". Add the job to the view. -7. Create a new folder named "freestyle" as part of the view. Move the job into the folder. +6. Create a new view named `test`. Add the job to the view. +7. Create a new folder named `freestyle` as part of the view. Move the job into the folder. 8. Locate the build information in `$JENKINS_HOME`. Inspect the directory structure. \ No newline at end of file diff --git a/exercises/04-scm-configuration/solution/solution.md b/exercises/04-scm-configuration/solution/solution.md index 799a7da..6d63507 100644 --- a/exercises/04-scm-configuration/solution/solution.md +++ b/exercises/04-scm-configuration/solution/solution.md @@ -8,7 +8,7 @@ Configure the Git SCM and point the proper URL. The default is the `master` bran ![Git SCM](./images/git-scm.png) -Create the Gradle build step. +Create the Gradle build step. Ensure to select the "Use Gradle Wrapper" option. ![Git SCM](./images/gradle-build-step.png) @@ -68,4 +68,4 @@ Build step 'Invoke Gradle script' changed build result to SUCCESS Finished: SUCCESS ``` -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. \ No newline at end of file +**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. \ No newline at end of file diff --git a/exercises/05-test-execution-and-reporting/instructions.md b/exercises/05-test-execution-and-reporting/instructions.md index 3741896..af0520d 100644 --- a/exercises/05-test-execution-and-reporting/instructions.md +++ b/exercises/05-test-execution-and-reporting/instructions.md @@ -8,7 +8,7 @@ In this exercise, you will configure the existing job to execute tests. Furtherm 2. Execute the build twice to generate a graph. Have a look at the executed tests in the test results. 3. Include all test results (unit and integration tests) in the reporting. 4. Have a look at how the test result trend changes. -5. Install the JaCoCo plugin. +5. Install the [JaCoCo plugin](https://plugins.jenkins.io/jacoco). 6. Reconfigure the Gradle build step to also generate JaCoCo reports: `clean build jacocoTestReport jacocoIntegrationTestReport`. 7. Add a post-build action for publish the JaCoCo reports. Use the following path to look for results: `build/jacoco/**.exec`. 8. Execute the build twice to generate a graph. Have a look at the code coverage results. \ No newline at end of file diff --git a/exercises/05-test-execution-and-reporting/solution/solution.md b/exercises/05-test-execution-and-reporting/solution/solution.md index 4c77f5d..58a7863 100644 --- a/exercises/05-test-execution-and-reporting/solution/solution.md +++ b/exercises/05-test-execution-and-reporting/solution/solution.md @@ -16,7 +16,7 @@ The trend changes accordingly. ![Changed Test Result Trend](./images/all-test-result-trend.png) -Install the JaCoCo plugin. +Install the JaCoCo plugin from the Plugin Manager page. ![JaCoCo Plugin](./images/jacoco-plugin.png) diff --git a/exercises/07-artifacts/instructions.md b/exercises/07-artifacts/instructions.md index 96f89cd..b02cb5f 100644 --- a/exercises/07-artifacts/instructions.md +++ b/exercises/07-artifacts/instructions.md @@ -8,7 +8,7 @@ You will enhance the existing job to generate a JAR file and store it on Jenkins 2. Execute the build. The build should list the artifact `gradle-initializr-1.0.0.jar`. 3. Have a look at the recorded fingerprints of this build. 4. Render the MD5 hash of the artifact and the usage of the artifact. -5. Install the Copy Artifacts plugin. +5. Install the [Copy Artifacts plugin](https://plugins.jenkins.io/copyartifact). 6. Create a downstream job named `consumer`. 7. Configure the downstream job to use the artifact produced by the upstream job. 8. Run the the build for the job `gradle-initializr`. diff --git a/exercises/07-artifacts/solution/solution.md b/exercises/07-artifacts/solution/solution.md index 6d55e3a..15983af 100644 --- a/exercises/07-artifacts/solution/solution.md +++ b/exercises/07-artifacts/solution/solution.md @@ -12,7 +12,7 @@ Have a look at the details of the fingerprinting. ![Fingerprint Details](./images/fingerprint-details.png) -Install the Copy Artifacts plugin. +Install the Copy Artifacts plugin from the Plugin Manager page. ![Copy Artifacts Plugin](./images/copy-artifacts-plugin.png) diff --git a/exercises/10-distributed-builds/instructions.md b/exercises/10-distributed-builds/instructions.md index 8043a74..cec9639 100644 --- a/exercises/10-distributed-builds/instructions.md +++ b/exercises/10-distributed-builds/instructions.md @@ -2,16 +2,25 @@ You will start a VM using Vagrant as practice environment to set up a distributed build with 2 agents. +## Starting up the VMs + +1. To start up the VMs, navigate to this directory. It should contain a `Vagrantfile`. +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.99.201` and `192.168.99.202`. The username/password credentials for those VMs are `jenkins:jenkins`. + ## Configuring and Executing Jobs in a Distributed Build 1. Go to "Manage Jenkins" > "Manage Nodes". You should see a single `master` node. 2. Configure the `master` node by setting the # of executor value to 0. That will take care of never using the `master` for job workload. -3. Have other physical or virtual machines ready that can act as agent nodes. Log into the machine as `root` user and create a new user named `jenkins` with the commands `useradd -d /var/lib/jenkins jenkins` and `passwd jenkins`. From the `master` node, copy the contents of the `id_rsa.key` file to the clipboard. Paste the contents of the clipboard to the file `/var/lib/jenkins/.ssh/authorized_keys`. -4. 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 host and provide credentials by selecting "SSH Username with private key". To keep things easy select "Non verifying Verification Strategy". +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.99.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". 4. Trigger a build. You should see that the build is only executed on the agent and not the `master` node. -5. Add at least one more agent. +5. Add one more agent with the IP address `192.168.99.202`. 6. Trigger a build. You should see that the build can be executed on any of the agents. 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". 8. Configure the `gradle-initializr` job and assign the label `java`. 9. Trigger a build of the `gradle-initializr` job. It is only executed by the dedicated agent. -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. \ No newline at end of file +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. + +## Shutting down the VMs + +1. After you are done with the exercise, navigate to the directory containing the `Vagrantfile`. +2. Tear down the VM by executing `vagrant destroy -f`. \ No newline at end of file diff --git a/exercises/10-distributed-builds/solution/images/agent-config.png b/exercises/10-distributed-builds/solution/images/agent-config.png index df42c4a..3d2cf34 100644 Binary files a/exercises/10-distributed-builds/solution/images/agent-config.png and b/exercises/10-distributed-builds/solution/images/agent-config.png differ diff --git a/exercises/10-distributed-builds/solution/images/agent-label-config.png b/exercises/10-distributed-builds/solution/images/agent-label-config.png index f44ffa5..5d43db8 100644 Binary files a/exercises/10-distributed-builds/solution/images/agent-label-config.png and b/exercises/10-distributed-builds/solution/images/agent-label-config.png differ diff --git a/exercises/10-distributed-builds/solution/images/node-overview.png b/exercises/10-distributed-builds/solution/images/node-overview.png index 0dcb8c6..a767b1e 100644 Binary files a/exercises/10-distributed-builds/solution/images/node-overview.png and b/exercises/10-distributed-builds/solution/images/node-overview.png differ diff --git a/exercises/11-pipeline-job/instructions.md b/exercises/11-pipeline-job/instructions.md index d6b4d9c..952da0d 100644 --- a/exercises/11-pipeline-job/instructions.md +++ b/exercises/11-pipeline-job/instructions.md @@ -1,14 +1,14 @@ # Exercise 11 -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. +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. ## Creating a Pipeline Job -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. -2. In Jenkins, set up the credentials `SONARCLOUD_TOKEN` and `HEROKU_API_KEY` if they don't exist yet. +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. +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). 3. Create a new multi-branch pipeline job for the repository. 4. The initial build is triggered automatically. Have a look at the pipeline in the standard view and its console output. -5. Install the Blue Ocean plugin. +5. Install the [Blue Ocean plugin](https://plugins.jenkins.io/blueocean). 6. Open the Blue Ocean pipeline visualization for the job. Manually trigger the deployment step. Open a browser with the deployed application on Heroku. 7. Create a new branch named `bugfix` and push it to the remote repository. The code should be based off of `master`. 8. Select "Scan Multibranch Pipeline Now". The job should build the new branch. \ No newline at end of file diff --git a/exercises/12-basic-jenkinsfile/instructions.md b/exercises/12-basic-jenkinsfile/instructions.md index 2b29603..6326759 100644 --- a/exercises/12-basic-jenkinsfile/instructions.md +++ b/exercises/12-basic-jenkinsfile/instructions.md @@ -5,13 +5,13 @@ In this exercise, you'll create a declarative pipeline for a Go-based project. T ## Writing a Basic Jenkinsfile 1. Create a new GitHub repository named `go-on-jenkins`. -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!". Initialize the project via Go Modules. +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. 3. Commit the files and push them to the remote repository. 4. Set up a new pipeline job for this repository in Jenkins. 5. Install the [Jenkins Go plugin](https://plugins.jenkins.io/golang). -6. Configure the latest Go runtime as global tool. +6. Configure the latest Go runtime as global tool. Use a name that reflects the Go version. Remember the name for the next step. 7. Enhance the `Jenkinsfile` based on the following requirements. The Jenkinsfile should use the declarative syntax. * The job can run on all agents. * The job sets the environment variable `GO111MODULES=on`. - * The job uses the Go runtime from the global tool definition. + * The job uses the Go runtime from the global tool definition (see previous step). * The job specifies one build stage named "Build". The build stage executes the shell command `go build`. \ No newline at end of file diff --git a/exercises/13-advanced-jenkinsfile/instructions.md b/exercises/13-advanced-jenkinsfile/instructions.md index ae8501b..d6d5a9f 100644 --- a/exercises/13-advanced-jenkinsfile/instructions.md +++ b/exercises/13-advanced-jenkinsfile/instructions.md @@ -1,6 +1,6 @@ # Exercise 13 -We'll want to enhance the 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. +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. ## Enhancing a Pipeline With Advanced Features @@ -8,13 +8,11 @@ We'll want to enhance the pipeline by additional stages and implement a release * Add a build step that runs the shell command `go test ./...`. * Generate code coverage metrics by adding the option `-coverprofile=coverage.txt` to the build step. * Publish the code coverage metrics to CodeCov by sending a curl command `curl -s https://codecov.io/bash | bash -s -`. - * Log into [CodeCov](https://codecov.io/), determine the CodeCov token for the repository (aka Repository Upload Token) and set it up as credential in Jenkins. - * Retrieve the credential and set the value as environment variable named `CODECOV_TOKEN`. 2. Add a stage named `Code Analysis` that uses [golangci-lint](https://github.com/golangci/golangci-lint) to detect issues with the code. - * 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.17.1` + * 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` * Add a build step that runs `golangci-lint` with the shell command `golangci-lint run`. 3. Add a stage named `Release` that uses [GoReleaser](https://github.com/goreleaser/goreleaser). * Only build this step if the commit has been tagged. - * Set up a credential in Jenkins named `github_token` and set your GitHub token. + * 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. * Retrieve the credential and set the value as environment variable named `GITHUB_TOKEN`. * Add a build step that runs the shell command `curl -sL https://git.io/goreleaser | bash`. \ No newline at end of file diff --git a/exercises/13-advanced-jenkinsfile/solution/solution.md b/exercises/13-advanced-jenkinsfile/solution/solution.md index f7557f6..6c7f29f 100644 --- a/exercises/13-advanced-jenkinsfile/solution/solution.md +++ b/exercises/13-advanced-jenkinsfile/solution/solution.md @@ -1,16 +1,9 @@ # Solution -Create the credentials for the CodeCov token. - -![CodeCov Credentials](./images/codecov_token_credentials.png) - You can implement the "Test" stage as follows. ```groovy stage('Test') { - environment { - CODECOV_TOKEN = credentials('CODECOV_TOKEN') - } steps { sh 'go test ./... -coverprofile=coverage.txt' sh "curl -s https://codecov.io/bash | bash -s -" @@ -23,7 +16,7 @@ You can implement the "Code Analysis" stage as follows. ```groovy stage('Code Analysis') { steps { - sh 'curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.17.1' + sh 'curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.18.0' sh 'golangci-lint run' } } @@ -47,4 +40,48 @@ stage('Release') { sh 'curl -sL https://git.io/goreleaser | bash' } } +``` + +The final `Jenkinsfile` looks similar to the solution below. + +```groovy +pipeline { + agent any + tools { + go 'go-1.12' + } + environment { + GO111MODULE = 'on' + } + stages { + stage('Build') { + steps { + sh 'go build' + } + } + stage('Test') { + steps { + sh 'go test ./... -coverprofile=coverage.txt' + sh "curl -s https://codecov.io/bash | bash -s -" + } + } + stage('Code Analysis') { + steps { + sh 'curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.18.0' + sh 'golangci-lint run' + } + } + stage('Release') { + when { + buildingTag() + } + environment { + GITHUB_TOKEN = credentials('GITHUB_TOKEN') + } + steps { + sh 'curl -sL https://git.io/goreleaser | bash' + } + } + } +} ``` \ No newline at end of file diff --git a/exercises/14-shared-library/solution/solution.md b/exercises/14-shared-library/solution/solution.md index 1423182..7b7667b 100644 --- a/exercises/14-shared-library/solution/solution.md +++ b/exercises/14-shared-library/solution/solution.md @@ -13,7 +13,7 @@ The directory structure of shared library repository should have the following s Define the pipeline as global variable in the file `standard.groovy`. ```groovy -def call(String goToolName = 'go-1.12', String golangCiVersion = 'v1.12.5') { +def call(String goToolName = 'go-1.12', String golangCiVersion = 'v1.18.0') { pipeline { agent any tools { @@ -29,9 +29,6 @@ def call(String goToolName = 'go-1.12', String golangCiVersion = 'v1.12.5') { } } stage('Test') { - environment { - CODECOV_TOKEN = credentials('CODECOV_TOKEN') - } steps { sh 'go test ./... -coverprofile=coverage.txt' sh "curl -s https://codecov.io/bash | bash -s -"