5 High Impact Enhancements You Can Make to Your GitHub CI/CD Pipeline
GitHub is a powerful versioning and source control system as it is. But did you know there are some quick and easy ways to get more performance out of it? And a lot of these down below are great additions to your pipelines that your upper-level management and auditors will love to hear.
Let's get into it.
If you want to see the final code, workflows, and configurations for this article, they can be found here in this repository.
Automated Unit Testing Comments on Pull Requests
This enhancement uses two GitHub Action packages to achieve its goal. Code Coverage Summary takes an existing Cobertura formatted code coverage file and exports a markdown summary version of it. This then allows us to chain Sticky Pull Request Comment and add a comment to an existing Pull Request.
As an added bonus, we will add that markdown code coverage report and paste it as part of the Job Summary at the end of the workflow.
Author Note: Code Coverage Summary requires a Linux runner to be used for the package to work properly.
Inside of your project, let's add a .github
folder at the root level.
Now let's go inside of our .github
folder and add a workflows
folder. Inside of the workflows
folder, we want to add a new .yml
file that will run whenever a Pull Request is created going to the main branch. Just as an example, I will name it pr.status-checks.yml
.
Now we will write out our Status Check workflow. For this particular example, I am using .NET for the solution and basis for this post. But any programming language that can generate a Cobertura Unit Testing file should be good.
In this particular workflow, we need to make sure we add the permissions: write all
property as part of the job. This allows the workflow the ability to modify pull requests and the GitHub Actions Job Summary itself.
Here's the workflow we should write to achieve this:
name: Pull Request - Status Checks
on:
pull_request:
branches: [ main ]
jobs:
build:
permissions: write-all
runs-on: ubuntu-latest
name: PR - Status Check
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.x
- name: Restore Dependencies
run: dotnet restore Impact/Impact.sln
- name: Build
run: dotnet build Impact/Impact.sln --configuration Release --no-restore
- name: Test
run: dotnet test Impact/Impact.sln --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage" --logger trx --results-directory ./coverage
- name: Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
filename: coverage/**/coverage.cobertura.xml
badge: true
indicators: true
format: markdown
output: both
- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
recreate: true
path: code-coverage-results.md
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
Once you open up a Pull Request, this Status Check workflow should run. If your Cobertura file exports properly from the testing step of your workflow, the rest of the GitHub Actions packages should run and complete as well.
At the bottom of your GitHub Actions workflow that runs, you should see a Code Coverage Summary report that shows overall statistics for testing on your project.
And you should see that same report as a comment in your Pull Request comments section:
Code Coverage Report Generator
On the subject of Code Coverage reports, if you want something more substantial, Daniel Palme's Report Generator might be what you are looking for. His code coverage generator supports various formats for consumption and exportation.
Here is the Report Generator GitHub Actions Package.
The only thing we need to modify in our existing pr.status-checks.yml
workflow file is adding the Report Generator package and Upload Artifact to attach the zip file that gets generated.
Now our workflow file looks like this:
name: Pull Request - Status Checks
on:
pull_request:
branches: [ main ]
jobs:
build:
permissions: write-all
runs-on: ubuntu-latest
name: PR - Status Check
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.x
- name: Restore Dependencies
run: dotnet restore Impact/Impact.sln
- name: Build
run: dotnet build Impact/Impact.sln --configuration Release --no-restore
- name: Test
run: dotnet test Impact/Impact.sln --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage" --logger trx --results-directory ./coverage
- name: Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
filename: coverage/**/coverage.cobertura.xml
badge: true
indicators: true
format: markdown
output: both
- name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2
if: github.event_name == 'pull_request'
with:
recreate: true
path: code-coverage-results.md
- name: Write to Job Summary
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY
- name: ReportGenerator
uses: danielpalme/ReportGenerator-GitHub-Action@5.2.0
with:
reports: coverage/**/coverage.cobertura.xml
targetdir: coveragereport
- name: Upload coverage report artifact
uses: actions/upload-artifact@v2.2.3
with:
name: CoverageReport
path: coveragereport
Near the bottom of your Workflow Job Summary page should be an artifact produced by Report Generator:
After you download that artifact and unzip it, index.html
contains a good summary of unit testing statistics across your project:
And if you open up one of your classes that was tested, you also get an in-depth look at what was tested using color-coded gutters:
The Usage Page on Daniel Palme's Report Generator website has tons of information on how his package exports out these summary reports. Just about every use case I can think of is supported.
Pull Request / Issue Templates
Standardizing GitHub Issues or Pull Requests is a great way to add consistency to your repositories. The predictable format can help guide developers and reviewers on what to expect whenever a new request is made.
Let's start with the Pull Request Template. We want to create a pull_request_template.md
file inside of .github.
Using Markdown, we can set up a standard format on what every Pull Request will look like when it gets created in GitHub. Here is an example you can copy and paste for this example:
# Description
Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
- [ ] Test A
- [ ] Test B
**Test Configuration**:
* Firmware version:
* Hardware:
* Toolchain:
* SDK:
# Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
Once this Markdown file has been added to the Main Branch, when we create a new Pull Request, we should see that Markdown pre-populated inside the text area section of a PR:
And after the Pull Request is created:
Arthur Coudouy has a good blog post about this on his website: GitHub pull request template. And here is GitHub's Pull Request Template Documentation.
Issue templates are treated a bit differently. We need to create an ISSUE_TEMPLATE
folder under .github and we can add a 1-bug-report.yml
as well. Next, we can populate that new YAML file with this Markdown text (taken from GitHub's Issue Template Documentation):
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: ["bug", "triage"]
assignees:
- octocat
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: input
id: contact
attributes:
label: Contact Details
description: How can we get in touch with you if we need more info?
placeholder: ex. email@example.com
validations:
required: false
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
value: "A bug happened!"
validations:
required: true
- type: dropdown
id: version
attributes:
label: Version
description: What version of our software are you running?
options:
- 1.0.2 (Default)
- 1.0.3 (Edge)
default: 0
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: What browsers are you seeing the problem on?
multiple: true
options:
- Firefox
- Chrome
- Safari
- Microsoft Edge
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com)
options:
- label: I agree to follow this project's Code of Conduct
required: true
Now when you go and create a new Issue, there will be a new template waiting for you:
Clicking on Get Started will bring you to the Issue creation screen where you can fill in values from the YAML configuration file:
And our newly created Issue:
GitHub's Issue Template Documentation goes into great detail on how to best create multiple templates for your GitHub repositories.
Automated Style and Linting Checks
This is another great one to add on top of unit testing. It is one thing to test that your code is functioning as it should. It is another to see if it is styled sensibly.
For this one, let's add linting.status-checks.yml
under the workflows
folder. And for this one, we will run this check as Push instead of a Pull Reqest. For this example, this is a .NET formatting check using dotnet format and checking the .editorconfig
file. Depending on the language you use, you will have different requirements and commands to run this functionality.
We can use this workflow to run this Status Check:
name: Linting - Status Checks
on:
push:
branches-ignore: [ main ]
jobs:
build:
runs-on: ubuntu-latest
name: PR - Status Check
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.x
- name: Restore Dependencies
run: dotnet restore Impact/Impact.sln
- name: Build
run: dotnet build Impact/Impact.sln --configuration Release --no-restore
- name: Format
run: dotnet format Impact/Impact.sln --verify-no-changes
And here's the successful run:
Microsoft's Dotnet Format Documentation
Release Drafter
When I talk about adding this feature to a GitHub repository, everybody's ears perk up. Release Drafter is a GitHub Actions Package that takes PRs that have been merged to the main branch and adds them as a draft to the Releases portion of that repository.
We need to add two files for this enhancement. The first will be the Release Drafter configuration file. Let's create release-drafter.yml
inside of our .github
folder and add this as our test bench:
name-template: 'v$RESOLVED_VERSION 🌈'
tag-template: 'v$RESOLVED_VERSION'
categories:
- title: '🚀 Features'
labels:
- 'feature'
- 'enhancement'
- title: '🐛 Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- title: '🧰 Maintenance'
label: 'chore'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- 'major'
minor:
labels:
- 'minor'
patch:
labels:
- 'patch'
default: patch
template: |
## Changes
$CHANGES
Next, we add another release-drafter.yml
inside of .github/workflows
and add this to the file:
name: Release Drafter
on:
push:
branches:
- main
permissions:
contents: read
jobs:
update_release_draft:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Once we get these two files into the main branch, our repository will be primed to add Pull Requests into a drafted release. A Release Drafter workflow will run after a PR gets merged into the main branch:
What we see inside our drafted release is an auto-incremented title, changes that were made, and a link back to the PR that got merged into the main branch.
This is one of the easiest enhancements you can make to your GitHub repositories. It is a great way to track changes and have that paper trail of who did what, etc.
Thanks for reading and I hope this was helpful to you. If you have not implemented some of these in your repositories, these make great enhancements to give them some extra punch.
If you want to see the final code, workflows, and configurations for this article, they can be found here in this repository.
Until next time!