Bumping Unity Projects, UPM Packages and Monorepos

Wondering how to SemVer-bump your Unity stuff without manual file editing? Look no further.

Last updated on February 22, 2020. Created on February 21, 2020.

Video of using ubump's interactive CLI mode.


I created a SemVer-bumping tool for Unity projects, UPM packages and monorepos. It's called ubump. Running it is as simple as typing npx ubump, and pressing enter. Go check out the GitHub page for more information, but here I'll explain why I made this hybrid CLI-API utility.

The Problem

When I started building a collection of Unity DOTS demos for your viewing pleasure, I began to wonder how other people bump versions for their Unity projects in general. As my code evolved into a UPM monorepo, it was increasingly evident that I needed some kind of automated SemVer-bumping solution. Strictly for UPM packages, since they have the same version field as NPM packages, one solution would be to wire up semantic-release in CI, and use commitizen locally, automating the process of version bumping through zealous commit linting.

Here are some examples:

  • docs(configuration): fix CLI usage with --branches option (#1465)
  • feat: require Node.js >=10.13
  • revert: fix: allow plugins to set environment variables to be used by other plugins

Is anyone else unnerved by the lack of human-readability in these commit messages? I pulled these directly from the semantic-release GitHub repository. I don't know what docs(configuration) fix so-and-so actually means. Also, is requiring a specific version of Node.js really a feature? And why say revert: fix: so-and-so? A revert is inherently obvious in Git. While nobody's commit messages will ever be perfect, these are consistently... strange. Is the goal for commit messages themselves to eventually be Turing complete? I will say these messages are better than what an old coworker regularly used: auto commit message.

Thanks, [REDACTED], your auto-commit script still haunts me.

Now, with all that said, the Unity project version itself is a bit of a problem. The project version is a field called productVersion, and it's in ProjectSettings/ProjectSettings.asset, which is actually a YAML file that can't be processed by a conventional YAML parser without throwing errors since it uses some weird custom tag in the header. Awesome.

And if you've got multiple packages, effectively as a UPM monorepo, say in the Packages directory, good luck bumping them. The Node community seems to still be struggling to figure that out, especially since the bumping should be accompanied by Git tagging—and those tags have to be unique! In other words, we can't tag one package with v1.0.5 and another with the same tag in the same repository.

I had to do something. I had to act. Versioning some packages shouldn't be difficult.

The Solution

Again, I made ubump. It can be used as:

  1. An interative CLI, which is the default when running ubump. You would run this instead of committing, pushing, and tagging your own bumps, as inspired by release-it and np of the Node world.
  2. A non-interactive CLI, generally for orchestrating ubump from CI. If you've many collaborators, and you want to automate version bumping with a branching strategy like GitLab Flow, then my tool should work great for you.
  3. An API, a Node-based one since that's what ubump is built with. You'd just import ubump into your file and off you go.

The feature set will grow, so I leave you to peruse the GitHub page. Still, ubump's interactive CLI sanity-checks your project for unstaged and staged changes. It also lets you know when you're not in the master branch. Not to mention, when you select a pre bump type, you will be further prompted for a specific prerelease identifier—the default is the existing one if it exists—otherwise it's just prerelease.

ubump has no problem updating that pesky ProjectSettings.asset file. And no matter how many packages you have, and no matter where the package.json files are located (although they should be in the Packages directory if you read Unity's docs), ubump will find those files, and kill update them at your instruction. It just globs through your project's entire directory tree.

And what about tagging? Don't worry about that. ubump tags the main project (or if there's just one package defined at the root) with v*.*.* (the v is just a default that you can override, by the way). As for tagging multiple packages, ubump creates Git subtree branches for them so that they can be distributed separately from the main project. The commits on those branches are tagged accordingly.

So, if, for instance, you have a subpackage called com.reese.nav, ubump will create a branch called nav, and furthermore version it like nav/v*.*.*. No colliding version tags! And all the tags ubump creates are annotated, meaning they include automated changelog messages based on past commits!


I should mention that there's a plethora of commands and options you can pass to ubump to tailor it for your needs. I implore you to give it a shot, at the very least trying it on a test project to see if it's right for you. If you have any concerns or suggestions, please let me know on GitHub Issues. I hope ubump makes things easier!

© Reese Schultz

My code is released under the MIT license.