[GH-ISSUE #1134] Equality and build metadata #5805

Open
opened 2026-06-15 12:18:14 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @edouardhue on GitHub (Jul 4, 2025).
Original GitHub issue: https://github.com/semver/semver/issues/1134

When build metadata was added to SemVer, its existence was justified with such examples (citing #77):

For example, folks might use it for cache busting, layering custom behavior on top of SemVer, bookkeeping to tie when and where a build was made, etc.

SemVer 2.0 also states this at chapters 10 and 11:

  • "Precedence refers to how versions are compared to each other when ordered."
  • "Build metadata MUST be ignored when determining version precedence."

The spec does not say anything about equality out of precedence calculation. Build metadata must be ignored when determining precedence, but what about determining equality? Should 1.0.0+cafe be equal to 1.0.0+beef? If yes, what about the ability of performing cache busting and such?

Most tools today seem to consider that what is said about precedence also holds for equality, and as just ignore build metadata (as hinted in Masterminds/semver#271). In Helm or NPM, expressing a dependency to a version such as 1.0.0+cafe is just the same as 1.0.0. If the repository contains several builds for the given version, the actual resolved dependency is at best specific to the tool and well-documented, at worse unpredictable. One cannot expect consistent behaviour between tools.

It is in many situations useful to be able to reference a specific build number for a given version, especially during development and testing. One does not want to increment the version or pre-release version just for the sake of testing a different build.

I would like to see added to the spec a chapter about equality so that SemVer-compatible dependency management tools operate in a consistent way. Such rules would state that two versions are equal when they have the same component set and each of their component are equal, from major to build. E.g:

  • 1.0.0+cafe == 1.0.0 is false: if build metadata is defined on the left-hand, don't ignore it and perform an exact match;
  • 1.0.0+cafe == 1.0.0+cafe is true: obviously;
  • 1.0.0+cafe == 1.0.0+beef is false: two versions equal but for build metadata are not equal;
  • 1.0.0 == 1.0.0 remains true.

It remains up to the tools to provide other comparison operators or ranges, in the likes of NPM's ^ and ~, for which precedence rules take over equality rules. For backward compatibility, it could also be made such that, when expressing a dependency without indicating a build number, precedence rules apply, but when the build number is expressed, strict equality rules apply.

Originally created by @edouardhue on GitHub (Jul 4, 2025). Original GitHub issue: https://github.com/semver/semver/issues/1134 When build metadata was added to SemVer, its existence was justified with such examples (citing #77): > For example, folks might use it for cache busting, layering custom behavior on top of SemVer, bookkeeping to tie when and where a build was made, etc. SemVer 2.0 also states this at chapters 10 and 11: * "Precedence refers to how versions are compared to each other when ordered." * "Build metadata MUST be ignored when determining version precedence." The spec does not say anything about equality out of precedence calculation. Build metadata must be ignored when determining precedence, but what about determining equality? Should `1.0.0+cafe` be equal to `1.0.0+beef`? If yes, what about the ability of performing cache busting and such? Most tools today seem to consider that what is said about precedence also holds for equality, and as just ignore build metadata (as hinted in Masterminds/semver#271). In Helm or NPM, expressing a dependency to a version such as `1.0.0+cafe` is just the same as `1.0.0`. If the repository contains several builds for the given version, the actual resolved dependency is at best specific to the tool and well-documented, at worse unpredictable. One cannot expect consistent behaviour between tools. It is in many situations useful to be able to reference a specific build number for a given version, especially during development and testing. One does not want to increment the version or pre-release version just for the sake of testing a different build. I would like to see added to the spec a chapter about equality so that SemVer-compatible dependency management tools operate in a consistent way. Such rules would state that two versions are equal when they have the same component set and each of their component are equal, from major to build. E.g: * `1.0.0+cafe == 1.0.0` is `false`: if build metadata is defined on the left-hand, don't ignore it and perform an exact match; * `1.0.0+cafe == 1.0.0+cafe` is `true`: obviously; * `1.0.0+cafe == 1.0.0+beef` is `false`: two versions equal but for build metadata are not equal; * `1.0.0 == 1.0.0` remains `true`. It remains up to the tools to provide other comparison operators or ranges, in the likes of NPM's `^` and `~`, for which precedence rules take over equality rules. For backward compatibility, it could also be made such that, when expressing a dependency without indicating a build number, precedence rules apply, but when the build number is expressed, strict equality rules apply.
Author
Owner

@ljharb commented on GitHub (Aug 13, 2025):

equality is just a full comparison tho - no two different semver strings are equal. npm certainly elides the build metadata as much as it can, and treats them the same, but that's because npm predates build metadata and doing so was the least turbulent path forward.

<!-- gh-comment-id:3184776165 --> @ljharb commented on GitHub (Aug 13, 2025): equality is just a full comparison tho - no two different semver strings are equal. npm certainly elides the build metadata as much as it can, and treats them the same, but that's because npm predates build metadata and doing so was the least turbulent path forward.
Author
Owner

@jwdonahue commented on GitHub (Mar 19, 2026):

I think clause 10 is too restrictive with regard to metadata:

... Build metadata MUST be ignored when determining version precedence. Thus two versions that differ only in the build metadata, have the same precedence.

I think it is there to make SemVer 2, backward compatible with SemVer1, but it is not well written. I think it should read something like:

... Build metadata MAY be ignored when determining version precedence.

I have implemented tooling that takes meta into account. It's required for non-trivial parallel CI/CD systems.

It is unfortunate as well, that the spec constrains sorting of the prerelease tag. The way that it is written, essentially implies that -dev is higher than -beta, but -beta is higher than -Dev. I don't think enough thought went into these rules.

It remains up to the tools to provide other comparison operators or ranges, in the likes of NPM's ^ and ~

Sort order != selector. The spec does not cover selectors, nor should it.

<!-- gh-comment-id:4089317253 --> @jwdonahue commented on GitHub (Mar 19, 2026): I think clause 10 is too restrictive with regard to metadata: > ... Build metadata MUST be ignored when determining version precedence. Thus two versions that differ only in the build metadata, have the same precedence. I think it is there to make SemVer 2, backward compatible with SemVer1, but it is not well written. I think it should read something like: > ... Build metadata MAY be ignored when determining version precedence. I have implemented tooling that takes meta into account. It's required for non-trivial parallel CI/CD systems. It is unfortunate as well, that the spec constrains sorting of the prerelease tag. The way that it is written, essentially implies that -dev is higher than -beta, but -beta is higher than -Dev. I don't think enough thought went into these rules. > It remains up to the tools to provide other comparison operators or ranges, in the likes of NPM's ^ and ~ Sort order != selector. The spec does not cover selectors, nor should it.
Author
Owner

@ljharb commented on GitHub (Mar 19, 2026):

I have implemented tooling that takes meta into account

Sounds like that's tooling that's not semver-compliant, then?

<!-- gh-comment-id:4091823337 --> @ljharb commented on GitHub (Mar 19, 2026): > I have implemented tooling that takes meta into account Sounds like that's tooling that's not semver-compliant, then?
Author
Owner

@jwdonahue commented on GitHub (Mar 20, 2026):

Actually, it is 100% compliant. Nothing in the spec, says you can't keep and use build meta for whatever use you want. It's the one part of the version string that is nearly opaque wrt the spec. We can store multiple packages that match in everything but the meta tags. If you don't add matching criteria for meta, you get the first one that matches, regardless of the meta, or can can ask it to give you the last one that matches. You can ask it for a list of packages that match a range of values in just the meta string. Mind you, non of that was ever considered for public facing services, bu it was built on an early non-public version of the backend that Azure uses for their Universal Packages and other content addressable storage.

<!-- gh-comment-id:4095234517 --> @jwdonahue commented on GitHub (Mar 20, 2026): Actually, it is 100% compliant. Nothing in the spec, says you can't keep and use build meta for whatever use you want. It's the one part of the version string that is nearly opaque wrt the spec. We can store multiple packages that match in everything but the meta tags. If you don't add matching criteria for meta, you get the first one that matches, regardless of the meta, or can can ask it to give you the last one that matches. You can ask it for a list of packages that match a range of values in just the meta string. Mind you, non of that was ever considered for public facing services, bu it was built on an early non-public version of the backend that Azure uses for their Universal Packages and other content addressable storage.
Author
Owner

@cyberbossone-coder commented on GitHub (May 8, 2026):

/^(0|[1-9]\d*).(0|[1-9]\d*).(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-])(?:.(?:0|[1-9]\d|\d*[a-zA-Z-][0-9a-zA-Z-]))))?(?:+([0-9a-zA-Z-]+(?:.[0-9a-zA-Z-]+)*))?$/gm

<!-- gh-comment-id:4406375943 --> @cyberbossone-coder commented on GitHub (May 8, 2026): /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/gm
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/semver#5805