How do I properly apply semantic versioning if I have more than three components in my version?

  softwareengineering

I am preparing a software package for internal consumption in my organization. It will be published on our internal non-public npm feed.

The used technology (npm) requires me to adhere to semantic versioning by format, though I would also like to adhere to it by idea, which is what this question aims at.

The content of the software package is not our own – it is a proprietary 3rd party software that we are licensing. We receive the JavaScript files by the producing entity for use in our products, and no npm package is provided. The necessity for packaging these files into an npm package is based on the desire to keep our internal development and build process streamlined.

How is the software versioned? The 3rd party software already adheres to semantic versioning; it is versioned with a 3-component scheme that matches the major.minor.patch idea.

How do we translate this into our package versions? This is where I see the problem: Intuitively, it would be best to just take over the 3rd party software’s version and be happy. This has the advantage of making it obvious to consumers of the 3rd party package which version of the 3rd party software they are using, which release notes apply, and so on.

However, in a few situations, it is necessary to do “technical changes” to the package, in particular by reconfiguring things in the package.json file. These would cause an increase in the package version, as well, but there appears to be no component left for it.

Example Timeline:

  • 3rd party software version 2.4.1
  • 3rd party software version 2.4.2
  • 3rd party software version 2.4.2 with change 1 to package.json
  • 3rd party software version 2.4.3
  • 3rd party software version 2.4.3 with change 1 to package.json
  • 3rd party software version 2.4.3 with change 2 to package.json

Considered workarounds:

  • I have considered using the prerelease part of the semantic versioning number for this, but then each and every version of the package would technically have to be using a prerelease version, as at the time of upgrading to the new version of the 3rd party software, we do not yet know whether we will have to fix something about package.json.
  • The build number does not help, as by the SemVer definition, it is to be ignored in determining version precedence and serves only for information.
  • I could rely on the 3rd party software never releasing more than x patches and us never needing more than y package.json updates for any version. Then, I could encode two numbers in the patch number based on digits. E.g. 2.4.2003 to denote 3rd party software release 2.4.2, package.json update 3. This would result in slightly awkward looking version numbers, most of which end in 000.

Is there a common way to resolve this within the definitions of semantic versioning, or is this a situation where semantic versioning just does not fit with our needs and we must define on our own what the version numbers for our package mean?

13

The linux world solves this same issue by not strictly following SemVer, having a pkgver and a pkgrel; eg 1.2.3-1 and 1.2.3-2 have the same content but have different packaging informations, or package release. Those are not prereleases, just different repackaging.

Technically, it is possible to have four-segment non-semver package versions that ignore non-compliant parts in semver (M.m.p.r)

Personally, If everything else fails, I would just add the re-packaging appending the release to end of the patch (M.m.pr) 1.2.30, 1.2.31 etc.

3

From the standpoint of SemVer, I would recommend using the build metadata component of the semantic version to capture these changes. Beyond that, I would recommend using a timestamp of the format YYYYMMDD or YYYYMMDDHHMM or even YYYYMMDDHHMMSS to capture the time of creating the package. This would give you clear and unambiguous sorting for the package. If you receive 2.4.1 from your supplier and have two builds with differing metadata, you would be able to tell that 2.4.1-20230629 is an earlier build than 2.4.1-20230720.

However, the contents of package.json are supposed to be parseable by node-semver and node-semver doesn’t (as far as I’m able to tell) handle build metadata. To work around this, you can use the prerelease tags instead of build metadata. That is, separate the date from the version using - instead of +. However, your dependent applications would need to explicitly allow for prerelease versions.

Other options are more specific to the Node ecosystem and may be violating some rules of Semantic Versioning.

Another option would be to use prerelease versions to test your build process against the latest version from your supplier. When your supplier provides you with 2.4.1, you would start to produce versions of the format 2.4.1-YYYYMMDDHHMM. Once you are satisfied that your build process is correct, you can build 2.4.1 and publish that to your repository. This would handle ensuring that errors in your build process, such as ensuring that your TypeScript definition files are properly included, are accounted for. You could also ensure that your other metadata is correct, but once you publish 2.4.1, you would be locked in until your supplier published 2.4.2 or later.

Depending on your tooling, though, you may have another option. You could unpublish and republish your package. Some tooling prevents publishing the same version twice, even if it has been unpublished, however. Depending on how often you wanted to unpublish and republish, you could avoid publishing versions with prerelease versions, or you could use prerelease versions, publish a release version when you are satisfied, and opt to unpublish and republish if the change was important enough.

My recommended approach would be the prerelease version approach. Use prerelease versions to test your build and packaging. Once you publish the build, lock down your changes until your supplier releases a new version. Errors in the build metadata won’t stop anyone from using the dependency and you can fix them for future releases, using the prerelease versions there to test those metadata changes.

3

LEAVE A COMMENT