Hands-On: Github PR comments with Jenkins CI/CD logs integration
Developer Experience is an important topic. Improved productivity and happy developers are key to success. We made Jenkins logs accessible and available to everyone at a glance through Pull Request (PR) comments, to raise our developer's productivity.
TLDR; What you can expect from reading on
a script to create, update and delete comments of a GitHub Pull Request; written in JavaScript
a script to write Jenkins logs as a GitHub comment; written in Javascript
a Jenkinsfile to use the scripts above on failure and success
a complete example with the folder structure of how an integrated setup could look like
You'll find all the scripts from below in our related blog repository: github.com/satellytes/blog-jenkins-github-pr-comments
Our initial situation
In our customer project, we are working in an enterprise environment which uses a central GitHub enterprise as a version control system and a managed, but customized Jenkins instance per department.
This article focuses on a problem we faced within our mono-repository, which is publicly available internally. Hundreds of developers around the globe are working with and on our project, but not all have the same GitHub permissions. This leads to the fact that not all were able to access our Jenkins and therefore are not able to directly see the outcome and logs of their Pull Request checks. Another fact is, that our Jenkins can only be reached through the internal corporate network, which requires contracted developers (like us) to start a separate Citrix Desktop connection.
To make GitHub logs accessible and available to everyone at a glance, we decided to make the Jenkins error logs accessible as Pull Request (PR) comments to make our developers happy and raise productivity.
Hands-on knowledge of one or more tools of the following is required to fully understand this blog post: Github API, Jenkins (GroovyScript), Javascript
Getting started
The CRUD (create, update, delete) operations for GitHub PR comments can be done quite simply through the GitHub REST API. Authentication requires a PAT (Personal Access Token) with repo
scope, which you can create on your GitHub Developer Settings.
We use environment variables to define arguments because parts of our used variables are predefined in the Jenkins pipeline. The environment variables can be set in the command line like this: VARIABLE_NAME=value node scriptname.js
and we are listing example usage in the JsDoc description at the top of the scripts.
Additionally, we rely on axios
as an HTTP request client, which you can install with npm install axios
to your project.
Now that we are prepared, let’s get our hands dirty and continue with the scripts.
Script: package.json
In case you have a greenfield project. Otherwise, just install axios
in your existing project.
Script: helpers.js
The following helpers are used for easier access to environment variables and are used throughout our other scripts.
Creating and updating GitHub comments
Creating a comment is as simple as doing an authenticated POST request to the API endpoint /v3/repos/${repo}/issues/${issueNumber}/comments
where issueNumber
is either a PR or issue number.
We will use the accordion format for posting our message. The headline will show a given string (existingContent
variable of the createOrUpdateComment
function below), which is also used to identify a commit upon an update:
And the content (up to 65k characters) will be inside the accordion:
We want to keep the messages lean. So for subsequent updates, we will use the PATCH request to update the initial comment, rather than posting a new one. What we are doing to achieve it:
GET a list of existing comments (API)
search their body for the title we have chosen above
PATCH the updated comment (API)
Let's read on and get to know the scripts.
Script: cli-github-methods.js
This is a file that contains functions around the GitHub API that we use not just in the pipeline functions we describe here, but also in other pipelines. Currently, it exports a method named createOrUpdateComment
that is used to create and update GitHub comments.
Script: gh-add-or-update-comment.js
This script reads a given file and writes its content to a Pull Request comment. Since we run it usually within a Jenkins PR multibranch pipeline, the [BUILD_URL
environment variable](https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables) is defined. It can also be defined manually for local testing purposes. Additionally, the LOGFILE
variable represents a path to a file with the content that gets stored as content.
Script: gh-remove-comment.js
Similar to the script above, we fetch the comments and check if a comment exists. If it exists, we do a DELETE request on the comment to remove it.
Alright! Now that we have all required scripts collected, we can continue to integrate them into our CI/CD pipeline.
Since we are working mainly with Jenkins, the following example is written in GroovyScript to be used in such a pipeline.
Script: Retrieving Jenkins Logs
There are numerous ways to retrieve the log content of the current run, but some of them require groovy sandbox whitelisting and are not recommended (whitelisting opens attack vectors for intruders). So we listed the most common here for your reference. The last one is mentioned in some sources and we mention it as a “do not use”, since it is blacklisted by the Jenkins Core team.
1. Accessing Jenkins Build Console Output with the Jenkins groovy API:
2. Using Shell Command to Access Build Log:
3. Accessing Jenkins Build Log via REST API (curl):
Now let’s prepare the Jenkins pipeline so a job can report its own failure and success as a GitHub comment:
Jenkinsfile post failure and success GitHub commenting
This is the very last post
block of a Jenkinsfile, so it gets always executed upon any error during runtime. Since we might also report errors on network hiccups, where we did not even reach the npm
install or yarn install
stage, we need to make sure that axios
is always available. The simplest approach for us was to just replace an existing package.json with a simplified one and install it.
Also, we are using withCredentials
to get the PAT from the Jenkins secrets store, where it is stored under the key value git-pat-token.
Script: Jenkinsfile
Jenkinsfiles are written in GroovyScript, the Standard language for Jenkins pipelines. The post block is placed at the very end of the Jenkinsfile within the last closing bracket ( }
) so it counts for the whole pipeline stages.
The failure
block is triggered upon each error and creates the log file comment entry, the success
is triggered upon a successful pipeline run and removes an existing failure comment.
Putting everything together
We assume you have already a Jenkins instance running, that contains an agent with Node.js installed. Or you have a single-node setup of Jenkins which includes the
nodejs
plugin, so the agent would have Node.js available as well.
Now let’s wrap together what we created so far and take a look how a possible folder structure of the whole setup could look like.
Including the Jenkins logs
In our use case, we used option 3 to fetch the logs via Jenkins API. And since we like clean code, we have excluded some parts into separate files.
ci/shared.groovy
Script: Jenkinsfile
For simplicity, we provide the GitHub personal access token as an inline variable GIT_AUTH_TOKEN. In a production environment, you would store it in the Jenkins vault and retrieve it like listed in the Jenkinsfile partial above (withCredentials
).
Now you can create a Jenkins job that uses that Jenkinsfile and enjoy the log errors as PR comments.
Conclusion
By integrating Jenkins logs as GitHub PR comments, we've successfully tackled the challenge of restricted access within our project. This solution not only enhances visibility but also fosters collaboration and productivity among developers globally. The scripts showcased here, coupled with CI/CD pipeline integration, provide a streamlined way to offer accessible feedback, enabling quick and efficient responses to PR outcomes.
We hope you found the earlier sections useful and are experiencing these improvements in your PR comments.