Versio allows you to perform some simple dependency management inside of a monorepo. If you use implicit versioning in your monorepo, chaining can save you a lot of time.
Imagine a scenario where some 3rd party Nodejs library upgrades to a new
version. If your core project depends on that library, you might update
your core package.json
to upgrade to the new version. Or maybe you
just run npm update
and let it update your package-lock.json
. Either
way, you'll end up bumping your core version number, and re-releasing
the new core library with the new library.
But then, maybe you have a top-level application that uses your core lib. You'll do the same thing: change the core version in your app's dependency list to match, bump the app's own version, and re-release it.
Of course, once you do that, you might also have a Helm chart that deploys your app to a kubernetes cluster. And, you need to do the same thing again: update the version number used by the Deployment resource to match the new application release, then bump the chart's own version, and re-release it.
And so on, and so forth, to your Dockerfiles, other sub-projects, packging projects, Terraform modules, and beyond.
Now, imagine that your core project, top-level app, your chart, and other related projects all lived in the same monorepo, and that the release numbers are updated only when you merge to the release branch using conventional commits. Running this dependency process would mean:
- You change your core library
package.json
, and commit it with a message like "fix: update 3rd party". - Your changes are merged to release, and then CI/CD (eventually) pops out a new release number for the core lib.
- You use the new release number in your top-level app, committing with a message like "fix: update core lib"
- Your changes are merged to release, and then CI/CD releases that.
- etc, etc.
Obviously, this involves a lot of commits in the same monorepo, and a lot of waiting for your pipeline to generate releases.
If you're managing your monorepo via Versio, then you can list explicit dependencies between the projects, so that all versions get automatically updated during the same release. This is called version chaining.
Here's a snippet from .versio.yaml
that has version chaining:
projects:
- name: proj_1
id: 1
root: "proj_1"
version:
file: "package.json"
json: "version"
- name: proj_2
id: 2
root: "proj_2"
depends:
1:
size: patch
files:
- file: "package.json"
json: 'dependencies.@myorg/core'
version:
file: "package.json"
json: "version"
You can see that the depends
property of proj_2
lists a dependency
to project 1. The size: patch
means that any time proj_1
changes,
proj_2
gets at least a patch-level increment to its own version (other
options are none
, minor
, major
, or match
). Also, it lists the
location in files that need to change to match the new version of
proj_1
, which have the same format as the version
property of files.
Now, when Versio performs a release: if it detects that the version number of proj_1 is changing, it will write the new proj_1 version number in proj_2's package.json. It will also increment the patch number of proj_2's version, even if proj_2 contained no other changes. If proj_2 had a changelog, it would be updated to show the dependency on proj_1 as a reason for the version bump.
When writing depends files, you don't need to write the exact version string: You can instead use a liquid template to edit how you want to write the value. The context of the template has just a single value, "v", which is the depended-on version string. For example, you can write a two-digit NPM version syntax:
depends:
1:
size: patch
files:
- file: "package.json"
json: 'dependencies.@myorg/core'
format: '{% assign a = v | split "." %}^{{a[0]}}.{{a[1]}}'