From 20028cb53f340a300b460b423e43f0eac13bcd9a Mon Sep 17 00:00:00 2001 From: ranger6 Date: Mon, 14 Dec 2020 15:12:33 +0100 Subject: [PATCH] Add "bump prerel" with an incrementing numeric portion. The basic approach is that currently disallowed usages of "bump prerel" can be used to introduce numerical bumping of a trailing numeric portion of a PRERELEASE string. Previously, "bump prerel" required an argument that was simply used as the new PRERELEASE string; with any BUILD part removed. The argument needed to be a correct PRERELEASE string. In particular, it could not contain a trailing dot. Here, a trailing dot denotes a numeric portion that is either created or updated. "Updated" means "incremented by one". The previous functionality (simple replacement) remains. If a trailing dot is present, the "" argument is used as a prototype and the new functionality is invoked. Note that any BUILD part is always removed. For example, here a PRERELEASE field with an initial value is created: semver bump prerel rc. 1.0.1 => 1.0.1-rc1 If the prototype matches the existing PRERELEASE part, then the numeric portion is incremented: semver bump prerel rc. 1.0.1-rc1 => 1.0.1-rc2 This is important for scripting: the same prototype argument can be used for initial and subsequent applications, removing the need for different flows or external state. If the existing PRERELEASE does not match the prototype, the prototype is used as in the first example above. Note that it is possible to add (or increment) a numerical portion as a separate identifier within the PRERELEASE field using two dots: semver bump prerel beta.. 2.2.0 => 2.2.0-beta.1 Using a separate identifier is "safer" in that it avoids false version comparisons when, for example, the numeric portion increments from 9 to 10. 6.4.0-beta9 is "greater" than 6.4.0-beta10 given the semantic versioning comparison rules while 6.4.0-beta.9 is "less" than 6.4.0-beta.10 Previously, "bump prerel" required two arguments (new PRERELEASE and existing version). With this new functionality, "bump prerel" with a single argument (existing version) implies adding or incrementing a trailing numerical portion based solely on the existing (or empty) PRERELEASE field: if the numerical portion exists, it is incremented; if not, it is added starting at one. Some examples: semver bump prerel 5.2.1-rc2 => 5.2.1-rc3 semver bump prerel 5.2.1-beta => 5.2.1-beta1 semver bump prerel 5.2.1 => 5.2.1-1 Also: the semver tool version string is now set to 3.2.0 This new functionality is backwards compatible with 3.1.0 The USAGE string and README.md have been updated. Examples demonstrating the new functionality have been added. As well, minor corrections have been made to the README and it is (again) in sync with the USAGE string. During the README and USAGE updates, the "diff" command documentation was completed. New unit tests exercising the bump prerel functionality have been added in "tests/bump_prerel.bats". All pass. Manual inspection indicates that all new/changed code paths are tested. The previous test suite passes (no regression) except for the error cases that have now become valid. These tests have been removed. Version 3.2.0 targeted: in semver code and README.md badge. --- README.md | 37 +++++++--- src/semver | 99 +++++++++++++++++++++++-- test/bump_prerel.bats | 159 +++++++++++++++++++++++++++++++++++++++++ test/semver_2.0.0.bats | 5 -- 4 files changed, 281 insertions(+), 19 deletions(-) create mode 100644 test/bump_prerel.bats diff --git a/README.md b/README.md index 4e44da9..95f5131 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,19 @@ The semver shell utility ======================== semver is a little tool to manipulate version bumping in a project that -follows the [semver 2.x][semver] specification. Its use are: +follows the [semver 2.x][semver] specification. Its uses are: - bump version - - compare version + - compare versions - extract specific version part + - identify most significant difference between two versions It can be combined with `git` pre-commit hooks to guarantee correct versioning. [semver]: https://github.com/mojombo/semver [![Build Status](https://travis-ci.org/fsaintjacques/semver-tool.svg?branch=master)](https://travis-ci.org/fsaintjacques/semver-tool) -[![Stable Version](https://img.shields.io/github/tag/fsaintjacques/semver-tool.svg)](https://github.com/fsaintjacques/semver-tool/tree/3.0.0) +[![Stable Version](https://img.shields.io/github/tag/fsaintjacques/semver-tool.svg)](https://github.com/fsaintjacques/semver-tool/tree/3.2.0) [![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg?style=flat)](https://github.com/fsaintjacques/semver-tool/blob/develop/LICENSE) @@ -22,8 +23,9 @@ usage ``` Usage: - semver bump (major|minor|patch|release|prerel |build ) + semver bump (major|minor|patch|release|prerel []|build ) semver compare + semver diff semver get (major|minor|patch|release|prerel|build) semver --help semver --version @@ -43,7 +45,8 @@ Arguments: See definition. - A string as defined by PRERELEASE above. + A string as defined by PRERELEASE above. Or, it can be a PRERELEASE + prototype string (or empty) followed by a dot. A string as defined by BUILD above. @@ -54,7 +57,10 @@ Options: Commands: bump Bump by one of major, minor, patch; zeroing or removing subsequent parts. "bump prerel" sets the PRERELEASE part and - removes any BUILD part. "bump build" sets the BUILD part. + removes any BUILD part. A trailing dot in the argument + introduces an incrementing numeric field which is added or + bumped. If no argument is provided, an incrementing numeric + field is introduced/bumped. "bump build" sets the BUILD part. "bump release" removes any PRERELEASE or BUILD parts. The bumped version is written to stdout. @@ -88,8 +94,14 @@ Basic bumping operations 1.0.0 $ semver bump patch 1.0.0 1.0.1 - $ semver bump prerel rc1.1.0 1.0.1 - 1.0.1-rc1.1.0 + $ semver bump prerel rc.1 1.0.1 + 1.0.1-rc.1 + $ semver bump prerel rc.. 1.2.0-beta2 + 1.2.0-rc.1 + $ semver bump prerel 1.0.1-rc.1+build4423 + 1.0.1-rc.2 + $ semver bump prerel beta. 1.1.0-beta2 + 1.1.0-beta3 $ semver bump build build.051 1.0.1-rc1.1.0 1.0.1-rc1.1.0+build.051 $ semver bump release v0.1.0-SNAPSHOT @@ -110,6 +122,15 @@ Comparing version for scripting $ semver compare 10.1.4-rc4 10.4.2-1234 -1 +Find most significant difference + + $ semver diff 1.0.1-rc1.1.0+build.051 1.0.1 + prerelease + $ semver diff 10.1.4 10.1.4 + + $ semver diff 10.1.4-rc4 10.4.2-rc1 + minor + Extract version part $ semver get patch 1.2.3 diff --git a/src/semver b/src/semver index cbc6337..ef80e82 100755 --- a/src/semver +++ b/src/semver @@ -14,12 +14,13 @@ SEMVER_REGEX="\ (\\+${FIELD}(\\.${FIELD})*)?$" PROG=semver -PROG_VERSION="3.1.0" +PROG_VERSION="3.2.0" USAGE="\ Usage: - $PROG bump (major|minor|patch|release|prerel |build ) + $PROG bump (major|minor|patch|release|prerel []|build ) $PROG compare + $PROG diff $PROG get (major|minor|patch|release|prerel|build) $PROG --help $PROG --version @@ -39,7 +40,8 @@ Arguments: See definition. - A string as defined by PRERELEASE above. + A string as defined by PRERELEASE above. Or, it can be a PRERELEASE + prototype string (or empty) followed by a dot. A string as defined by BUILD above. @@ -50,7 +52,10 @@ Options: Commands: bump Bump by one of major, minor, patch; zeroing or removing subsequent parts. \"bump prerel\" sets the PRERELEASE part and - removes any BUILD part. \"bump build\" sets the BUILD part. + removes any BUILD part. A trailing dot in the argument + introduces an incrementing numeric field which is added or + bumped. If no argument is provided, an incrementing numeric + field is introduced/bumped. \"bump build\" sets the BUILD part. \"bump release\" removes any PRERELEASE or BUILD parts. The bumped version is written to stdout. @@ -189,12 +194,94 @@ function compare_version { compare_fields left right } +# render_prerel -- return a prerel field with a trailing numeric string +# usage: render_prerel numeric [prefix-string] +# +function render_prerel { + if [ -z "$2" ] + then + echo "${1}" + else + echo "${2}${1}" + fi +} + +# extract_prerel -- extract prefix and trailing numeric portions of a pre-release part +# usage: extract_prerel prerel prerel_parts +# The prefix and trailing numeric parts are returned in "prerel_parts". +# +PREFIX_ALPHANUM='[.0-9A-Za-z-]*[.A-Za-z-]' +DIGITS='[0-9][0-9]*' +EXTRACT_REGEX="^(${PREFIX_ALPHANUM})*(${DIGITS})$" + +function extract_prerel { + local prefix; local numeric; + + if [[ "$1" =~ $EXTRACT_REGEX ]] + then # found prefix and trailing numeric parts + prefix="${BASH_REMATCH[1]}" + numeric="${BASH_REMATCH[2]}" + else # no numeric part + prefix="${1}" + numeric= + fi + + eval "$2=(\"$prefix\" \"$numeric\")" +} + +# bump_prerel -- return the new pre-release part based on previous pre-release part +# and prototype for bump +# usage: bump_prerel proto previous +# +function bump_prerel { + local proto; local prev_prefix; local prev_numeric; + + # case one: no trailing dot in prototype => simply replace previous with proto + if [[ ! ( "$1" =~ \.$ ) ]] + then + echo "$1" + return + fi + + proto="${1%.}" # discard trailing dot marker from prototype + + extract_prerel "${2#-}" prerel_parts # extract parts of previous pre-release +# shellcheck disable=SC2154 + prev_prefix="${prerel_parts[0]}" + prev_numeric="${prerel_parts[1]}" + + # case two: bump or append numeric to previous pre-release part + if [ "$proto" == "+" ] # dummy "+" indicates no prototype argument provided + then + if [ -n "$prev_numeric" ] + then + : $(( ++prev_numeric )) # previous pre-release is already numbered, bump it + render_prerel "$prev_numeric" "$prev_prefix" + else + render_prerel 1 "$prev_prefix" # append starting number + fi + return + fi + + # case three: set, bump, or append using prototype prefix + if [ "$prev_prefix" != "$proto" ] + then + render_prerel 1 "$proto" # proto not same pre-release; set and start at '1' + elif [ -n "$prev_numeric" ] + then + : $(( ++prev_numeric )) # pre-release is numbered; bump it + render_prerel "$prev_numeric" "$prev_prefix" + else + render_prerel 1 "$prev_prefix" # start pre-release at number '1' + fi +} + function command_bump { local new; local version; local sub_version; local command; case $# in 2) case $1 in - major|minor|patch|release) command=$1; version=$2;; + major|minor|patch|prerel|release) command=$1; sub_version="+."; version=$2;; *) usage_help;; esac ;; 3) case $1 in @@ -217,7 +304,7 @@ function command_bump { minor) new="${major}.$((minor + 1)).0";; patch) new="${major}.${minor}.$((patch + 1))";; release) new="${major}.${minor}.${patch}";; - prerel) new=$(validate_version "${major}.${minor}.${patch}-${sub_version}");; + prerel) new=$(validate_version "${major}.${minor}.${patch}-$(bump_prerel "$sub_version" "$prere")");; build) new=$(validate_version "${major}.${minor}.${patch}${prere}+${sub_version}");; *) usage_help ;; esac diff --git a/test/bump_prerel.bats b/test/bump_prerel.bats new file mode 100644 index 0000000..3b16cdc --- /dev/null +++ b/test/bump_prerel.bats @@ -0,0 +1,159 @@ +# "bats" test script for semver-tool. +# Tests number bumping of pre-release part. +# +# see: https://github.com/bats-core/bats-core +# see: https://hub.docker.com/r/bats/bats +# +# N.B. The script assumes that the bats intepreter is invoked from the +# root directory of the semver-tool source tree. +# +# examples: +# run all .bats scripts in "test": +# cd $SEMVER_HOME ; bats test +# +# run all .bats scripts in "test" using docker: +# docker run --rm -v "$(pwd):/mnt" -w /mnt bats/bats:latest test + +SEMVER="src/semver" + +# sanity tests: should pass in all versions + +@test "basic bump prerel (set)" { + result="$($SEMVER bump prerel rc.1 0.2.1)" + [ "$result" = "0.2.1-rc.1" ] +} + +@test "basic bump prerel (replace and strip pre-release and build metadata)" { + result="$($SEMVER bump prerel rc.1 0.2.1-0.2+b13)" + [ "$result" = "0.2.1-rc.1" ] +} + +@test "basic bump prerel (strip build metadata)" { + result="$($SEMVER bump prerel rc.1 0.2.1+b13)" + [ "$result" = "0.2.1-rc.1" ] +} + +# test bump pre-release using the explicit prefix numbering scheme + +@test "bump prerel (add numeric id)" { + result="$($SEMVER bump prerel . 0.2.1)" + [ "$result" = "0.2.1-1" ] +} + +@test "bump prerel (replace with numeric id)" { + result="$($SEMVER bump prerel . 0.2.1-alpha)" + [ "$result" = "0.2.1-1" ] +} + +@test "bump prerel (inc numeric id)" { + result="$($SEMVER bump prerel . 0.2.1-1)" + [ "$result" = "0.2.1-2" ] +} + +@test "bump prerel (add new pre-release part)" { + result="$($SEMVER bump prerel rc. 0.2.1)" + [ "$result" = "0.2.1-rc1" ] +} + +@test "bump prerel (add new pre-release part with separated numeric id)" { + result="$($SEMVER bump prerel rc.. 0.2.1)" + [ "$result" = "0.2.1-rc.1" ] +} + +@test "bump prerel (add numeric id to existing pre-release, similar prefix)" { + result="$($SEMVER bump prerel rc.v. 0.2.1-rc.2)" + [ "$result" = "0.2.1-rc.v1" ] +} + +@test "bump prerel (add numeric id to existing pre-release, similar trailing id)" { + result="$($SEMVER bump prerel rc.3. 0.2.1-rc.3)" + [ "$result" = "0.2.1-rc.31" ] +} + +@test "bump prerel (add numeric id to existing pre-release, similar trailing id - with dot)" { + result="$($SEMVER bump prerel rc.3.. 0.2.1-rc.3)" + [ "$result" = "0.2.1-rc.3.1" ] +} + +@test "bump prerel (add numeric id to existing pre-release)" { + result="$($SEMVER bump prerel rc. 0.2.1-rc)" + [ "$result" = "0.2.1-rc1" ] +} + +@test "bump prerel (replace with new pre-release part)" { + result="$($SEMVER bump prerel rc. 0.2.1-alpha)" + [ "$result" = "0.2.1-rc1" ] +} + +@test "bump prerel (inc numeric id in pre-release part)" { + result="$($SEMVER bump prerel rc. 0.2.1-rc1)" + [ "$result" = "0.2.1-rc2" ] +} + +@test "bump prerel (inc numeric id in pre-release part with dot)" { + result="$($SEMVER bump prerel rc.. 0.2.1-rc.1)" + [ "$result" = "0.2.1-rc.2" ] +} + +@test "bump prerel (inc numeric id in pre-release part, multiple ids)" { + result="$($SEMVER bump prerel v6.rc. 0.2.1-v6.rc1)" + [ "$result" = "0.2.1-v6.rc2" ] +} + +@test "bump prerel (inc numeric id in pre-release part with dot, multiple ids)" { + result="$($SEMVER bump prerel 4.rc.. 0.2.1-4.rc.1)" + [ "$result" = "0.2.1-4.rc.2" ] +} + +# error checking tests, explicit prefix + +@test "bump prerel (inc numeric id in pre-release part, bad pre-release arg)" { + run $SEMVER bump prerel .rc. 0.2.1-rc.1 + [ "$status" -eq 1 ] +} + +@test "bump prerel (inc numeric id in pre-release part, bad version)" { + run $SEMVER bump prerel rc. 0.2.1-.rc.1 + [ "$status" -eq 1 ] +} + +@test "bump prerel (inc numeric id in pre-release part, 2-dot pre-release arg)" { + run $SEMVER bump prerel .. 0.2.1-rc.1 + [ "$status" -eq 1 ] +} + +@test "bump prerel (add numeric id in pre-release part, 2-dot pre-release arg)" { + run $SEMVER bump prerel .. 0.2.1 + [ "$status" -eq 1 ] +} + + +# test bump pre-release using the implicit numbering scheme + +@test "bump prerel (inc numeric id in pre-release part, no-arg)" { + result="$($SEMVER bump prerel 0.2.1-rc.1)" + [ "$result" = "0.2.1-rc.2" ] +} + +@test "bump prerel (add numeric id, no-arg)" { + result="$($SEMVER bump prerel 0.2.1)" + [ "$result" = "0.2.1-1" ] +} + +@test "bump prerel (append numeric id to pre-release part, no-arg)" { + result="$($SEMVER bump prerel 0.2.1-alpha)" + [ "$result" = "0.2.1-alpha1" ] +} + +@test "bump prerel (inc numeric id, no-arg)" { + result="$($SEMVER bump prerel 0.2.1-1)" + [ "$result" = "0.2.1-2" ] +} + +# error checking tests, implicit prefix + +@test "bump prerel (add numeric id in pre-release part, bad version)" { + run $SEMVER bump prerel 0.2.1-rc. + [ "$status" -eq 1 ] +} + diff --git a/test/semver_2.0.0.bats b/test/semver_2.0.0.bats index 3ada3d9..a086a1e 100644 --- a/test/semver_2.0.0.bats +++ b/test/semver_2.0.0.bats @@ -171,11 +171,6 @@ SEMVER="src/semver" [ "$status" -eq 1 ] } -@test "bump empty identifier in pre-release (trailing)" { - run $SEMVER bump prerel "x.7.z.92." "1.0.0" - [ "$status" -eq 1 ] -} - @test "bump pre-release to invalid version" { run $SEMVER bump prerel "x.7.z.92" "1.00.0" [ "$status" -eq 1 ]