Level up your Unity Packages with CI/CD

RunningMattress
5 min readMar 5, 2023

--

This is the second post in a series about creating Unity Packages and distributing them via GitHub.

Check out part one below

So you’re up and running sharing code across your projects now, but you want to improve your CI/CD pipeline and provide better information for your packages’ users.

In this part two we’ll explore how we can improve the CI/CD pipeline to help us write higher-quality code and automatically generate release notes.

Add auto changelogs

To start with, we’re gonna ensure that we enforce conventional commits on this repository, this is a crucial first step in generating your release notes.

There are a few ways we can do this:

  • Pre-commit checks
    These are great and if you’re the only one working on your project this is a nice straightforward option. However, it can get complicated if you’re working with others and don’t have the tooling set up to install pre-commit hooks for everyone. They’re also not so great for less tech focussed colleagues as the error messages are often hard to read.
  • GitHub action to validate all commits
    This is also quite restrictive and checks too late, in my opinion (no one wants to rewrite all their commit messages after they’ve made the PR), as well as suffering some of the above issues.
  • GitHub action to validate the PR title and enforced merge conventions
    This is how we’ll do this, it offers the most freedom whilst still ensuring our main branch only contains conventional commits. However, if the other options work for you then they’ll also work with the rest of the pipeline we’ll talk about today.

Let’s add the following GitHub action to our project

name: Check PR title
on:
pull_request:
types:
- opened
- reopened
- edited
- synchronize

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: aslafy-z/conventional-pr-title-action@v3
id: pr_title_check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: PR Comment
if: ${{ failure() }}
uses: thollander/actions-comment-pull-request@v1
with:
message: |
Add a prefix like "fix: ", "feat: " or "feat!: " to indicate what kind of release this pull request corresponds to. The title should match the commit message format as specified by https://www.conventionalcommits.org/.

This checks we have formatted the title of the PR according to the conventional commits spec.

Next, we’ll change the way PRs are merged into the project to ensure the title is taken as the merge commit message.

In the settings page for our repository, we’ll scroll down to the Pull Requests section in the general tab, and set it up like so.

Our pull request merges settings.

This means that when a PR is merged it’ll squash all commits on that branch into 1 and take the PR title as the commit message.

So that’s all the setup taken care of, let’s look at how to turn that into a changelog.

We’ll update our package publishing workflow for this. Add the following snippet just after the version bump and before we set up node.

    - name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"

- name: Update CHANGELOG
id: changelog
uses: requarks/changelog-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.previoustag.outputs.tag }}

- name: Commit CHANGELOG.md
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: main
commit_message: 'docs: update CHANGELOG.md for ${{ steps.previoustag.outputs.tag }} [skip ci]'
file_pattern: CHANGELOG.md

This will grab the newly created tag, create/update the CHANGELOG.md file and push it to our main branch as well.

And there we are, automatically generated change logs.

Unity will link us to our changelog right from the package manager

Create a GitHub release

Whilst we’re improving the publish GitHub action, let’s go a step further and create a GitHub release for this as well. We’ll use the changelog we made previously to add some content to the release.

Add the below snippet at the end of the publish

    - name: Create Release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
draft: false
name: ${{ steps.previoustag.outputs.tag }}
body: ${{ steps.changelog.outputs.changes }}
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.previoustag.outputs.tag }}

Now when we merge a PR, not only will a new package be published, but a GitHub release will be created so we can share the news and show what’s been changed.

There’s our release!
Here’s our changelist from earlier, automatically embedded into our release

Add some automated review help

Finally, to wrap up part two, let’s help ourselves out by adding an automated reviewer. We all make mistakes now and then and a little help goes a long way. We’re gonna use MegaLinter for this as they’ve already pulled together all the linters we want to use.

Open up a command prompt or terminal in your repository folder and run

npx mega-linter-runner --install

Follow the instructions to set it up according to your needs and desire.

Our megalinter config

You’ll then want to make a quick change to the mega-linter.yml file to prevent duplicate runs on pull requests, change the on: block to the below code instead

on: 
pull_request:
branches: [main]

The config file sometimes also includes the wrong linters, set your’s up to look like this instead or follow Megalinter’s guides to configure it how you’d like.

# Configuration file for MegaLinter
# See all available variables at https://megalinter.io/configuration/ and in linters documentation

APPLY_FIXES: all # all, none, or list of linter keys
# ENABLE: # If you use ENABLE variable, all other languages/formats/tooling-formats will be disabled by default
# ENABLE_LINTERS: # If you use ENABLE_LINTERS variable, all other linters will be disabled by default
# DISABLE:
# - COPYPASTE # Uncomment to disable checks of excessive copy-pastes
# - SPELL # Uncomment to disable checks of spelling mistakes
SHOW_ELAPSED_TIME: true
FILEIO_REPORTER: true
# DISABLE_ERRORS: true # Uncomment if you want MegaLinter to detect errors but not block CI to pass

ENABLE_LINTERS:
# Looks for excessive uses of copying and pasting your code around your project.
- COPYPASTE_JSCPD
# Formats your CSharp code according to CSharpier standards
- CSHARP_CSHARPIER
# Format CSS code (handy for uss files as well)
- CSS_STYLELINT
# Formating for Json files
- JSON_PRETTIER
# Spell checker
- CSPELL
# Watches out for any missed merge conflict markers
- REPOSITORY_GIT_DIFF

Now you can commit your files to a branch, create a pull request, sit back and relax as the robots check your code for you.

This was part two of a series about making Unity packages. Follow me for part three, coming soon.

All the code above is available in a public template repository: https://github.com/RunningMattress/upm-test-package

--

--

RunningMattress

Lead Gameplay Programmer, experience at a range of studios from small start-ups to AAA, I specialise in: Jenkins, Unity and DevOps with nearly 10 years exp