[HL-28] How to Test Code in Jenkins with GitHub Pull Requests
This all starts with an idea that I’d like to run unit tests whenever a pull request or a commit to a pull request is created.
I thought a bit to directly write a build server which listens for GitHub webhooks. However, I think it’s better to use an existing tool like Jenkins or AWS CodePipeline for visualizing, monitoring and easy-to-maintain.
I had a look at CodePipeline, but it only listens to changes of a branch instead of commits in a pull request like what TravisCI does. I probably can use API Gateway and Lambda to implement, but Jenkins seems to be a more sophisticated tool to learn.
Honestly, my first impression is that Jenkins’s UI is really Java-style (not looking very nice). I will use one of my previous repo in this demo, https://github.com/HengfengLi/pymapplot.
Workflow
The workflow should be as the following diagram:
- Developers create a pull request and push local branch to GitHub.
- GitHub post a request to Jenkins’s webhook endpoint.
- Jenkins pulls latest code from GitHub to local directory.
- Jenkins builds or tests code.
- Jenkins informs GitHub about building/testing results.
Steps
1. Run Jenkins with docker
Run the following command:
docker run -p 8080:8080 -v {YourHostPath}/jenkins_home:/var/jenkins_home jenkins/jenkins
If you want to persist configurations and plugins, you need to map a volume
from host to container /var/jenkins_home
.
You will see a password in console and copy it in somewhere for next step.
2. Setup Jenkins
- Go to
http://localhost:8080
- Input the saved password or you can find the password in
/var/jenkins_home/secrets/initialAdminPassword
- Choose
Install suggested plugins
- Create a new admin
- Go to
Manage Jenkins
- Click
Configure Global Security
- Choose
Safe HTML
inMarkup Formatter
dropdown menu (HTML will be displayed inBuild History
instead of plain text).
3. Install plugins
- Visit
Manage Jenkins
and thenManage Plugins
- Find
GitHub Pull Request Builder
plugin - Install without restart
4. Configure Jenkins
- Visit
Manage Jenkins
and thenConfigure System
- Find
GitHub Pull Request Builder
section - Input Jenkins’s url to
Jenkins URL override
field. I usengrok
to expose my local port for testing, so it is a url likehttps://XXXXXXXXX.ngrok.io
- Click
Add
button next toCredentials
, then chooseJenkins
- In
Kind
, chooseSecret text
- Input your GitHub access token (with
repo
andadmin:repo_hook
permissions) inSecret
field - Type
jenkins-global
inDescription
field (this is an option name). - Click
Add
- Update
Description
inGitHub Pull Request Builder
section - Add GitHub username into
Admin list
- Save the changes
5. Configure GitHub webhooks
Add webhool url in GitHub repository
- Go the repository
- Click
Settings
, clickWebhooks
, addwebhook
- Type
https://XXXXXXXXX.ngrok.io/ghprbhook/
inPayload URL
- Choose
application/x-www-form-urlencoded
inContent type
- Choose
Let me select individual events
- Choose
Pull requests
,Pushes
,Issue comments
- Click
Add webhook
Enable Jenkins GitHub plugin service
- Go the repository
- Click
Settings
, clickIntegrations & services
- Click
Add service
- Choose
Jenkins GitHub plugin
- Type
https://XXXXXXXXX.ngrok.io/github-webhook/
inJenkins hook url
- Click
Add service
6. Create a project
- Go to Jenkins’s homepage
- Click
New Item
- Type
pull-request-demo
in name - Choose
Freestyle project
- Click
OK
7. Configure the project
General
- Tick
GitHub project
- Type your project url like
https://github.com/HengfengLi/pymapplot
inProject url
Source Code Management
- Click
Git
- Type
https://github.com/HengfengLi/pymapplot.git
inRepository URL
- Click
Advanced
- Type
origin
inName
- Type
+refs/pull/*:refs/remotes/origin/pr/*
inRefspec
Build Triggers
- Tick
GitHub Pull Request Builder
- Tick
Use github hooks for build triggering
Build
- Click
Add build step
- Choose
Execute shell
- Type the testing command
python -m unittest discover tests
8. Build now
- Go to the project homepage
- Click
Build Now
- You can see a job has been scheduled (see above figure)
- You can also check the following console log:
Started by user hengfeng
Building in workspace /var/jenkins_home/workspace/pull-request-demo
Cloning the remote Git repository
Cloning repository https://github.com/HengfengLi/pymapplot.git
> git init /var/jenkins_home/workspace/pull-request-demo # timeout=10
Fetching upstream changes from https://github.com/HengfengLi/pymapplot.git
> git --version # timeout=10
> git fetch --tags --progress https://github.com/HengfengLi/pymapplot.git +refs/heads/*:refs/remotes/origin/*
> git config remote.origin.url https://github.com/HengfengLi/pymapplot.git # timeout=10
> git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
> git config remote.origin.url https://github.com/HengfengLi/pymapplot.git # timeout=10
Fetching upstream changes from https://github.com/HengfengLi/pymapplot.git
> git fetch --tags --progress https://github.com/HengfengLi/pymapplot.git +refs/pull/*:refs/remotes/origin/pr/*
> git rev-parse refs/remotes/origin/master^{commit} # timeout=10
> git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
Checking out Revision 117a9d187255c126ad445ea19b21c63eb8a8c676 (refs/remotes/origin/master)
> git config core.sparsecheckout # timeout=10
> git checkout -f 117a9d187255c126ad445ea19b21c63eb8a8c676
Commit message: "Merge pull request #5 from HengfengLi/feature/update-gitignore"
First time build. Skipping changelog.
[pull-request-demo] $ /bin/sh -xe /tmp/jenkins8196395077623670742.sh
+ python -m unittest discover tests
.
----------------------------------------------------------------------
Ran 1 test in 0.017s
OK
Finished: SUCCESS
9. Test pull requests
- Check out a local branch
- Make some changes
- Push to a remote branch
- Make a pull request
- You will see a new job is scheduled
- You can open the pull request page and see the following changes:
Summary
Yes, so many configurations. There are still many plugins and configurations that can be explored. This is just a “hello world” example.
There are some further questions:
- How to ensure the security?
- I’d like to ensure only corporate network can access the admin portal, but I also want to expose the webhook endpoints.
- How to build docker images inside a Jenkins container?
- How to deploy code to AWS ECS or Beanstalk?
It’s really nice to have such a small feature to check every commit of your pull request, adding lint checking and unit tests. This can really improve codebase’s quality and encourage to write more tests.