Making It Useful (MfGames Conventional Commit)

We wrote mfgames-conventional-commits to solve a small itch of wanting to be able to calculate version numbers based on Conventional Commits but only for commits that touch specific directories. That way, a change to MfGames.IO would bump that version up but not touch the versioning for MfGames.Nitride in the same monorepo. For the most part, it was to allow the CIL projects to have different versions as they all had different lineages before they were combined.

As things go, we always intended to polish and expand on the tool but other things got in the way. But then D. Moonfire was working on Git For Authors and realized they didn't have a good solution for calculating version numbers of novels. Since we already had this tool, we spent a few days making it more useful and capable for single package projects and require less overhead to configure it.

Simplified Setup

The configuration file goes into //.config/mfgames-conventional-commit.json as usual, but setup has gotten significant easier. we added a defaults section that applies to every project along with substitutions for packages with {package} that allow each pack to have their own directories.

{
  "$schema": "https://mfgames.com/mfgames-conventional-commit-rs/schemas/v0.json",
  "defaults": {
    "tag_prefix": "{package}-",
    "directories": { "include": ["src/{package}"] },
    "files": { "include": ["src/{package}/**/*"] }
  },
  "packages": {
    "MfGames.Cryptography": {},
    "MfGames.DI.AutofacExtensions": {},
    "MfGames.Gallium": {},
    "MfGames.IO": {},
    // There is more here
    "MfGames.ToolBuilder.Tables": {}
  }
}

It used to be we had to have the tag_prefix and files elements in each one with a customization per project to include the path. Now, {package} can be used to substitute the package name and defaults to apply to any package that doesn't have settings underneath itself. In the future, we'll have some configuration that will scan the //src directory for the package names, but not this round.

Version Numbers

We also have two variants of version numbers based on usage. The first is the “flatten” version which calculates the most relevant major, minor, or patch version and applies it to the latest found tag. So if you have a “feat”, “fix”, “fix” on 1.0.0, it would be “1.1.0”. In contrast is the “detail” version which applies the version one at a time. So if you had the same “feat”, “fix”, “fix” on 1.0.0, the result would be “1.1.2”.

Most software projects seem to make a conscious decision to bump up the version. With semantic releases, the versioning is done on the pipeline. For a long time, we used “flattened” as the basic approach since that is what Semantic Release did. However, with novels and the like, sometimes they go out without the pipeline running and it is useful to make sure new versions show up; hence the use of “detail”.

This also resulted in a slightly different output for the mfgames-conventional-commit version command:

$ cd src/MfGames.IO
$ mfgames-conventional-commit version
 package_name  last_version  flatten_version  flatten_changed  detail_version  detail_changed
 MfGames.IO    2.7.0         2.7.0            false            2.7.0           false
$ mfgames-conventional-commit version --all | head -n 5
 package_name                        last_version  flatten_version  flatten_changed  detail_version  detail_changed
 MfGames.Cryptography                14.7.0        14.7.0           false            14.7.0          false
 MfGames.DI.AutofacExtensions                      0.0.1            true             0.0.1           true
 MfGames.Gallium                     3.6.0         3.6.0            false            3.6.0           false
 MfGames.IO                          2.7.0         2.7.0            false            2.7.0           false

Because we're fond of changing format as needed, so it can also dump a JSON or Markdown version. And all logging is sent to stderr, so piping can work properly.

$ mfgames-conventional-commit version --json | jq
[
  {
    "package_name": "MfGames.IO",
    "last_version": "2.7.0",
    "flatten_version": "2.7.0",
    "flatten_changed": false,
    "detail_version": "2.7.0",
    "detail_changed": false
  }
]
$ mfgames-conventional-commit version --all --format markdown | head -n 5
package_name last_version flatten_version flatten_changed detail_version detail_changed
MfGames.Cryptography 14.7.0 14.7.0 false 14.7.0 false
MfGames.DI.AutofacExtensions 0.0.1 true 0.0.1 true
MfGames.Gallium 3.6.0 3.6.0 false 3.6.0 false
MfGames.IO 2.7.0 2.7.0 false 2.7.0 false
MfGames.Locking 2.5.1 2.5.1 false 2.5.1 false

Updates

Another useful feature is the ability to perform updates if the version number changed. This is used to define an updates element in the configuration file:

{
  "$schema": "https://mfgames.com/mfgames-conventional-commit-rs/schemas/v0.json",
  "defaults": {
    "updates": [
      { "script": "npm version {detail_version} --no-git-tag-version" },
      { "script": "git tag v{detail_version}" }
    ]
  }
}

As you can see from above, the command will change the NPM version and create a Git tag. If always_run is set to true, then it will run every time it is called, otherwise it will only run if the Git tag doesn't match the calculated tag.

The main reason for this is because then we don't have to write a hundred plugins for different systems. You can just write the script to do it, call another program, or just write some output. Anything a shell can do, this can do.

Inferred Package

One of the more annoying things is that we couldn't figure out the package name from the current context. For the most part, that didn't mean much since we could always provide --package package-name, but we found it useful enough that we made the tool figure out the patch from the directories attribute above. This also handles {package} substitutions, so it is generally useful.

Both the version and update commands also take an --all which means all packages in the project instead of --package package-name for an arbitrary package, or either of these to guess it from the directory.

Sane Defaults

Finally, we added a sane default if the //.config/mfgames-conventional-commit.json file is missing, or it doesn't have many settings. This assumes the entire directory is a single package called “default” and it uses a “vX.X.X” tag to represent versions.

Future Steps

This is a secondary project for us. Obvious, if we have an itch and it doesn't do something we want, we'll add it. If there are any issues that would be really useful or if you come up with something useful for the project (that is in scope), report or comment on it and we'll see about implementing it.

Metadata

Project

Categories: