Prev |  | Next

## 3. NUT versioning

### 3.1. Historic note

Historically, the Network UPS Tools project release and interim iterations code base versions were identified by three or four numeric components, roughly following the Semantic Versioning (SEMVER) traditions, later codified as a formal standard at semver.org:

- NUT uses the GNU autotools suite for recipe orchestration, and the version string is specified in the `configure.ac` file an `AC_INIT` macro, which further generates variables like `PACKAGE_VERSION`, `PACKAGE_URL` and others used (substituted) in the actual code base, often via `nut_version.h` file.
- NUT releases were identified by a `MAJOR.MINOR.PATCH` triplet, which was not strictly following the standard in that while the "major" part did reflect architectural/design changes, and "minor" part reflected some significant development milestones or API changes, the "patch" part did not correspond to post-release fixes but reflected iterative development, of which releases were the better-reviewed snapshots. Such a triplet was only spelled in `AC_INIT` for the commit tagged as that release.
- Until NUT v2.6.x, the odd values of the `MINOR` component meant development code trees, and even values meant the stable tree. This approach was skipped (or effectively abandoned, as over a decade passed) with the move to Git branches for development — as numerous NUT v2.7.x based releases were produced, and development continued into NUT v2.8.x.
- NUT development versions were specified by the next commit after a release was published, spelling an `AC_INIT` macro argument like `MAJOR.MINOR.PATCH.1` to provide a fourth component — which is logically "greater than" `MAJOR.MINOR.PATCH` for comparisons, so that the developed version can formally be installed over a preceding release (e.g. from custom testing packages).

This configuration with a single development version (`.1`) was not very helpful for the faster pace of iterations, especially as the Git workflow and pull requests were adopted as the way of iterating the NUT development. This did not really help identify the build being tested by CI or reported by a community member, nor quickly determine if one custom build is "newer" than another (e.g. can be a recommended upgrade from the previously installed snapshot to check if some bug was fixed).

Some experiments were done adding the `git describe` output to version banners reported by programs. These provide the number of commits since the most-recent known git tag, as well as the git hash of the NUT sources involved. This made the builds better identifiable, but did not help compare the feature branches and the main trunk of development: any code committed after a release has its own count of commits since that tag, this one number does not really suffice.

### 3.2. Current NUT SEMVER definition

Since NUT v2.8.3, the definition which goes into `AC_INIT` and further into the code was extended in a manner similar to what `git describe` produces, but with added numbers after the common triplet of semantically versioned numbers: `X.Y.Z(.T(.B(-C+H(+R))))` or `X.Y.Z(.T(.B(-R)))`

- Standard semver (used in releases):

  ### Note

  Historically NUT did not diligently follow the standard semver triplet, primarily because a snapshot of trunk is tested and released, and work moves on with the PATCH part (rarely MINOR one) incremented; no actual patches are released to some sustaining track of an older release lineage. There were large re-designs that got MAJOR up to 2, though.

  - `X`: MAJOR - incompatible API changes
  - `Y`: MINOR - major new features and/or API evolution
  - `Z`: PATCH - bug fixes (and new features like added drivers)

- Extended semver (for snapshots of trunk):

  - T: (optional) Commits on trunk since previous release tag

- Extended semver (for features branched off trunk):

  - B: (optional) Commits on branch since nearest ancestor which is on trunk

The optional suffix (only for commits which are not git tags themselves) is provided by `git describe`:

- C: Commits on branch since previous release tag
- H: (Short) Git hash (prefixed by "g" character) of the described commit

The pre-release information (if provided/known) would either follow the optional suffix detailed above, or it would be the suffix itself:

- R: If this commit has a non-release tag, it can be optionally reported so we know that a commit some 1234 iterations after release N is also a release candidate for N+1. Note that any dash in that tag value will be replaced by a plus, e.g. `2.8.2.2878.1-2879+g882dd4b00+v2.8.3+rc6`

The numeric part of NUT SEMVER definition mostly follows https://semver.org/ except that for development iterations the base version may have up to five dot-separated numeric components (SEMVER triplet for base release, and additional data described above). Unlike standard semver provisions for "pre-release versions" (separated by a minus sign after the triplet), which are "less than" that release for comparisons, the fourth and fifth components (if present) are "greater than" that release and any preceding development iterations made after it.

Helper script `tools/gitlog2version.sh` is used to determine the project version from packager-provided override files (or equivalents provided by `make dist` in a snapshot/release tarball), git metadata from the current workspace, or built-in fallback defaults.

Occasionally there may be tagged pre-releases, which follow the standard semver markup, like `v2.8.0-rc3` (in git), however they would be converted to NUT SEMVER here (as a number of commits since previous release) by default.

A special case, which can mostly be seen in CI builds, concerns shallow git checkouts — when the currently built commit history is "grafted" (perhaps down to there being only one commit in the whole git index), and we do not know of any intersections with git tags or development trunk. In this case, the script may not only fall back to a built-in version following a legacy `X.Y.Z.1` pattern for non-release builds (like `2.8.3.1`), but would also append a `-0-g{HASH}` suffix to the long values (`VER5`, `VER50`, `DECS5`, `DESC50`) yielding e.g. `2.8.3.1.0-0+gb3f21f9` for a build from a shallow git checkout of commit `b3f21f9` of a code base which otherwise fell back to `2.8.3.1` hard-coded in its copy of the script (bumped by a maintainer as part of the release process).

### 3.3. Using gitlog2version.sh

The script can be controlled by environment variables, including some sourced from configuration files. It identifies a number of data items, and reports the one specified by `NUT_VERSION_QUERY` on `stdout`.

### Note

It does not currently have a query to report "everything" in a manner that can be processed by `eval` in calling shell scripts (or `Makefile` rule implementations).

Table 1. Environment variables that can be used for external configuration

Variable

Description

    Example

`abs_top_srcdir`

Top source directory

    `/home/abuild/nut`

`abs_top_builddir`

Top build directory (defaults to `abs_top_srcdir`)

    `/home/abuild/.builds/linux/nut`

`NUT_VERSION_FORCED`

Set `NUT_VERSION_DEFAULT` (extended NUT SEMVER, may be just a triplet) to this value and enforce `NUT_VERSION_PREFER_GIT=false`. Usually sourced from `${abs_top_srcdir}/VERSION_FORCED` (if present)

    `2.8.2.2379` `2.8.3-rc3`
                             `2.8.2.2878.3-2881+g45029249f+v2.8.3+rc6`

`NUT_VERSION_FORCED_SEMVER`

Set `SEMVER` (exactly a triplet) to this value regardless of `NUT_VERSION_PREFER_GIT` setting. Usually sourced from `${abs_top_srcdir}/VERSION_FORCED_SEMVER` (if present)

    `2.8.3`

`NUT_VERSION_DEFAULT`

Usually sourced from either ‘${abs_top_builddir}/VERSION_DEFAULT` (if present) or `${abs_top_srcdir}/VERSION_DEFAULT` (if present), in which case the script also defaults `NUT_VERSION_PREFER_GIT=false` (unless it is already specified as `true` or `${abs_top_srcdir}/.git` exists). If no value was provided, a hard-coded value is used (updated as part of maintainers’ release rituals).

    `2.8.2.2379.2-2381+g1faa9945d`

`NUT_VERSION_PREFER_GIT`

If not provided by caller, or sourced files, or defaulted with `NUT_VERSION_FORCED` or `NUT_VERSION_DEFAULT` as described above, as a `false` value, then becomes `true` if `${abs_top_srcdir}/.git` exists or `false` otherwise (tarball builds)

    `true`

`NUT_WEBSITE`

Default website URL, extended for historic sub-sites for a release

    `https://www.networkupstools.org/`

`NUT_VERSION_GIT_TRUNK`

Git branch name to use for calculation of current codebase distance from main development (as known in local workspace index); by default, the newest branch named like `master` is located (any competition is same or ancestor)

    `origin/master`

`NUT_VERSION_GIT_ALL_TAGS`

If `true`, consider usual (not "annotated") tags too

    `false`

`NUT_VERSION_GIT_ALWAYS_DESC`

If `true`, tell git to return just a commit hash if no tag was matched in index.

    `false`

Table 2. Intermediate variables in Git workspace processing

Variable

Description

    Example (development and release)

`DESC`

Originates from `git describe`, filtered for releases (`vX.Y.Z`) and ignoring various `rc`, `alpha`, `beta` etc. tags. This yields the tag name, followed by number of commits added to current `HEAD` history since that tag, and the current commit hash. In the resulting string, the git hash is separated by a "plus" sign (as semver build metadata) rather than the "minus" returned by the tool.

    `v2.8.2-2381+g1faa9945d`

`TAG`

Nearest (annotated by default) tag preceding the `HEAD` in history: the part of `DESC` before the commit count and hash.

    `v2.8.2`

`BASE`

The `git merge-base` of current commit and `NUT_VERSION_GIT_TRUNK` (see above). How much of the known trunk history is in current HEAD? This may be "all of it" when we are on that branch or PR made from its tip, "some of it" if looking at a historic snapshot, or "nothing" if looking at the tagged commit (it is the merge base for itself and any of its descendants)

    `e9a48c9afeb4e06c758a3f4215977445c0f64780`

`SUFFIX`

Commit count since the tag and hash of the `HEAD` commit; empty e.g. when `HEAD` is the tagged commit

    `-2381+g1faa9945d`

`VER5`

Full 5-component version, note we strip leading `v` from the expected `TAG` value

    `2.8.2.2379.2`

`DESC5`

Full 5-component version `VER5` concatenated with `SUFFIX`

    `2.8.2.2379.2-2381+g1faa9945d`

`VER50`

`VER5` without trailing `.0` in fifth or fourth component

- dev: `2.8.2.2379.2`
- trunk: `2.8.2.2379.0` ⇒ `2.8.2.2379`
- release: `2.8.2.0.0` ⇒ `2.8.2`

`DESC50`

`VER50` concatenated with `SUFFIX`

- release: `2.8.2-2381-g1faa9945d`

`SEMVER`

Exactly three leading numeric components. Either `NUT_VERSION_FORCED_SEMVER` (if provided by caller or configuration files), or derived from `VER5` (removing fourth and fifth numbers)

    `2.8.0`

Table 3. Intermediate variables in default (non-git — tarball or forced) processing

Variable

Description

    Example (development and release)

`NUT_VERSION_DEFAULT_DOTS`

Processed from `NUT_VERSION_DEFAULT` (see above) to count just the dot characters

- dev: `....`
- trunk: `...`
- release: `..`

`NUT_VERSION_DEFAULT5_DOTS`

Grows from `NUT_VERSION_DEFAULT_DOTS`, used to construct `NUT_VERSION_DEFAULT5`

    `....`

`NUT_VERSION_DEFAULT5`

Constructed from `NUT_VERSION_DEFAULT`, adding `.0` numeric components as needed, to have at least 5 of them

    `2.8.2.0.0`

`NUT_VERSION_DEFAULT3_DOTS`

Decreases from `NUT_VERSION_DEFAULT_DOTS`, used to construct `NUT_VERSION_DEFAULT3`

    `..`

`NUT_VERSION_DEFAULT3`

Constructed from `NUT_VERSION_DEFAULT`, adding `.0` numeric components as needed or dropping extras, to have exactly 3 of them

    `2.8.0`

`SUFFIX` | Empty, unless `NUT_VERSION_DEFAULT` had a suffix for pre-release information roughly matching the `-(rc | alpha
beta)[0-9]*` regular expression | `""` `-rc6` | `VER5`
Full 5-component version, `NUT_VERSION_DEFAULT5` | `2.8.2.2379.2` | `DESC5`
Constructed as `${VER5}${SUFFIX}` | `2.8.2.2379.2` `2.8.3.0.0-rc6` | `VER50`
`NUT_VERSION_DEFAULT` as provided by caller or defaulted, may be with or without trailing `.0` in fifth or fourth components | `2.8.2.1` | `DESC50`
Constructed as `${VER50}${SUFFIX}` | `2.8.2.1` `2.8.3-rc6` | `BASE`
Empty (no known common commits with no trunk) | `""` | `SEMVER`
Exactly three leading numeric components. Either `NUT_VERSION_FORCED_SEMVER` (if provided by caller or configuration files), or `NUT_VERSION_DEFAULT3` (see above) | `2.8.0` | `TAG`

The majority of identified values can be reported for debugging to `stderr`, currently as a single line (wrapped for readability in the sample below):

    :; ./tools/gitlog2version.sh
    SEMVER=2.8.2;
    TRUNK='master';
    BASE='e9a48c9afeb4e06c758a3f4215977445c0f64780';
    DESC='v2.8.2-2381+g1faa9945d'
    => TAG='v2.8.2' + SUFFIX='-2381+g1faa9945d'
    => VER5='2.8.2.2379.2'
    => VER50='2.8.2.2379.2'
    => DESC50='2.8.2.2379.2-2381+g1faa9945d'

Table 4. Values reported via `NUT_VERSION_QUERY`

`NUT_VERSION_QUERY`

Description

    Example (development and release)

`DESC5`

Full 5-component version (concatenated with `SUFFIX` for git)

- dev: `2.8.2.2379.2-2381+g1faa9945d`
- snapshot tarball: `2.8.2.2379.2`

`DESC50`

3-to-5 non-zero component version (concatenated with `SUFFIX` for git)

- dev: `2.8.2.2381-2381+g1faa9945d`
- snapshot tarball: `2.8.2.1`

`VER5`

Full 5-component version

- dev: `2.8.2.2379.2`
- snapshot tarball: `2.8.2.1.0`

`VER50`

3-to-5 non-zero component version

- dev: `2.8.2.2379.2`
- release tarball: `2.8.0`

`SEMVER`

Exactly three leading numeric components

    `2.8.2`

`IS_RELEASE`

`true` if `SEMVER`==`VER50`, `false` otherwise

- dev: `false`
- rel: `true`

`IS_PRERELEASE`

`true` if `SUFFIX_PRERELEASE` is not empty, `false` otherwise

- dev: `false`
- rel/RC: `true`

`TAG`

GIT: Nearest (annotated by default) tag preceding the `HEAD` in history. DEFAULT: Constructed from `SEMVER`

    `v2.8.2`

`TAG_PRERELEASE` | GIT: if the `HEAD` itself has a tag matching the `-(rc | alpha
beta)[0-9]*` regular expression. DEFAULT: Constructed from `NUT_VERSION_DEFAULT3` and `SUFFIX_PRERELEASE`. Empty for not-pre-releases. | `v2.8.2-rc3` `""` | `TRUNK`
GIT: Branch name used for calculation of current codebase distance from main development. DEFAULT: empty. | `master` | `SUFFIX`

GIT: Commit count since the tag and hash of the `HEAD` commit DEFAULT: empty for non-prerelease `NUT_VERSION_DEFAULT` values, or either value of `SUFFIX_PRERELEASE` with a leading dash for `NUT_VERSION_DEFAULT` values without git offset info (e.g. `2.8.3.5-rc6` ⇒ `-rc6`), or the whole tail with git and pre-release tag info.

* dev: `-2381+g1faa9945d` * RC git: `-2381+g1faa9945d+v2.8.3+rc6` * RC default: `-rc6`

`SUFFIX_PRERELEASE`

GIT: Constructed from `TAG_PRERELEASE` replacing any dash with a plus character. DEFAULT: empty unless `NUT_VERSION_DEFAULT` has a suffix matching the `-(rc | alpha | beta)[0-9]*` regular expression, or git info followed by the pre-release tag. NOTE: No leading dash in this value (unlike `SUFFIX`).

* RC git: `v2.8.3+rc6` * RC default: `rc6`

`BASE`

    GIT: Newest common commit of development `TRUNK` and the `HEAD`
                             commit (their `git merge-base`).
                             DEFAULT: empty.

`e9a48c9afeb4e06c758a3f4215977445c0f64780`

`URL`

    Clarify the project website URL -- particularly historically
                             frozen snapshots made for releases

* dev: `https://www.networkupstools.org/` (default development) * rel: `https://www.networkupstools.org/historic/v2.8.2/index.html`

`UPDATE_FILE`

    Used in `autogen.sh` and top-level `Makefile.am` to
                             update the `VERSION_DEFAULT` file that goes into "dist"
                             tarballs; prints its contents

`NUT_VERSION_DEFAULT='2.8.2.2379.2-2381+g1faa9945d'`

`UPDATE_FILE_GIT_RELEASE`

    Used in maintainer rituals (requires git) to
                             update the `VERSION_FORCED` and `VERSION_FORCED_SEMVER`
                             files that go into "dist" tarballs; prints their contents

`NUT_VERSION_FORCED='2.8.2.2878.3-2881+g45029249f+v2.8.3+rc6'` `NUT_VERSION_FORCED_SEMVER='2.8.3'`

default

    Report `DESC50`

### 3.4. Variables propagated by configure.ac

Table 5. Values reported via `NUT_VERSION_QUERY`

Variable

Description

    Example (development and release)

`PACKAGE_VERSION`

Argument to `AC_INIT` determined by `NUT_VERSION_QUERY=VER50 gitlog2version.sh`

- dev: `2.8.2.695.1`
- trunk: `2.8.2.695`
- release: `2.8.2`

`PACKAGE_URL`

Argument to `AC_INIT` determined by `NUT_VERSION_QUERY=URL gitlog2version.sh`

- dev/trunk: `https://www.networkupstools.org/`
- release: `https://www.networkupstools.org/historic/v2.8.2/index.html`

`NUT_WEBSITE_BASE`

Derived from `PACKAGE_URL` without a trailing slash nor `index.html` (prefixed to documentation file URLs, etc.)

- dev/trunk: `https://www.networkupstools.org`
- release: `https://www.networkupstools.org/historic/v2.8.2`

`NUT_SOURCE_GITREV`

Determined by `NUT_VERSION_QUERY=DESC50 gitlog2version.sh`

    `2.8.2.695.1-696+g0e00f0777`

`NUT_SOURCE_GITREV_SEMVER`

Determined by `NUT_VERSION_QUERY=SEMVER gitlog2version.sh`

    `2.8.2`

`NUT_SOURCE_GITREV_NUMERIC`

Determined by `NUT_SOURCE_GITREV` leaving only the numbers, e.g. for PyPI uploads (currently without the total commit count)

    `2.8.2.695.1'`

`NUT_SOURCE_GITREV_IS_RELEASE`

Determined by `NUT_VERSION_QUERY=IS_RELEASE gitlog2version.sh`

    `true` or `false`

`NUT_SOURCE_GITREV_IS_PRERELEASE`

Determined by `NUT_VERSION_QUERY=IS_PRERELEASE gitlog2version.sh`

    `true` or `false`

`NUT_SOURCE_GITREV_DEVREL`

String determined by `NUT_SOURCE_GITREV_IS_RELEASE`

    `"release"` or `"development iteration"`

### 3.5. Variables propagated by nut_version.h

Table 6. Values encoded via `include/nut_version.h`, generated by `include/Makefile.am`

Variable

Description

    Example (development and release)

`#define NUT_VERSION_MACRO "$NUT_VERSION"`

Determined by default `gitlog2version.sh` (no `NUT_VERSION_QUERY`) at the moment of latest build, or (as fallback) `PACKAGE_VERSION` set during the last run of `configure` script

    `2.8.2.695.1`

`#define NUT_VERSION_SEMVER_MACRO "$GITREV_SEMVER"`

Determined by `NUT_VERSION_QUERY=SEMVER gitlog2version.sh` at the moment of latest build, or (as fallback) `NUT_SOURCE_GITREV_SEMVER` set during the last run of `configure` script

    `2.8.2`

`#define NUT_VERSION_IS_RELEASE <0-or-1>`

Determined by `NUT_VERSION_QUERY=IS_RELEASE gitlog2version.sh` (falls back to `false` if that query fails)

- `1` if `$GITREV_IS_RELEASE`
- `0` otherwise

`#define NUT_VERSION_IS_PRERELEASE <0-or-1>`

Determined by `NUT_VERSION_QUERY=IS_PRERELEASE gitlog2version.sh` (falls back to `false` if that query fails)

- `1` if `$GITREV_IS_PRERELEASE`
- `0` otherwise

### 3.6. Use in C code

#### common-nut_version.c

- The `NUT_VERSION_MACRO` is used in `common/common-nut_version.c` and further made known to all code base as a static string `UPS_VERSION` linked via `libcommon*.la` internal libraries.

- Method `describe_NUT_VERSION_once()` prepares the string which combines the `NUT_VERSION_MACRO` with comments that it is either a `release` or a `(development iteration after $NUT_VERSION_SEMVER_MACRO)`, based on the value of `NUT_VERSION_IS_RELEASE`.

  It is used from a number of other methods, such as `print_banner_once()`, `nut_report_config_flags()`, and so ends up in version reports of programs via their `help()`/`usage()` methods. * Method `suggest_doc_links()` prepares a uniform bit of text for driver and tool programs to report in their `help()`/`usage()` methods, to refer to their manual page under the `NUT_WEBSITE_BASE`.

#### Man pages

- Manual pages and other documentation consume the `PACKAGE_VERSION`, `PACKAGE_VERSION` and `NUT_WEBSITE_BASE` as `asciidoc` attributes when rendering HTML/PDF/man document formats.
- The `NUT_WEBSITE_BASE` is also substituted instead of literal `https://www.networkupstools.org/*` which follows a `home page:` prefix (so that the pages rendered for a release refer to the historic website).

#### systemd and SMF manifests

Service manifests include references to documentation for the tools they wrap, including published pages under the `NUT_WEBSITE_BASE` for the development or historic variants of the NUT website.

#### NUT-Monitor (Python UI) and PyNUTClient

- The `PACKAGE_VERSION` and `NUT_WEBSITE_BASE` are reported in the About dialog.
- Version information is propagated into PyPI packages for the `PyNUTClient` module.

Prev |  | Next
| Home |