Skip to content

Commit

Permalink
Add "bump prerel" with an incrementing numeric portion.
Browse files Browse the repository at this point in the history
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
"<prerel>" 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.
  • Loading branch information
ranger6 committed Jan 13, 2021
1 parent 13370ad commit 20028cb
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 19 deletions.
37 changes: 29 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand All @@ -22,8 +23,9 @@ usage

```
Usage:
semver bump (major|minor|patch|release|prerel <prerel>|build <build>) <version>
semver bump (major|minor|patch|release|prerel [<prerel>]|build <build>) <version>
semver compare <version> <other_version>
semver diff <version> <other_version>
semver get (major|minor|patch|release|prerel|build) <version>
semver --help
semver --version
Expand All @@ -43,7 +45,8 @@ Arguments:
<other_version> See <version> definition.
<prerel> A string as defined by PRERELEASE above.
<prerel> A string as defined by PRERELEASE above. Or, it can be a PRERELEASE
prototype string (or empty) followed by a dot.
<build> A string as defined by BUILD above.
Expand All @@ -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 <prerel> argument
introduces an incrementing numeric field which is added or
bumped. If no <prerel> 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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
99 changes: 93 additions & 6 deletions src/semver
Original file line number Diff line number Diff line change
Expand Up @@ -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 <prerel>|build <build>) <version>
$PROG bump (major|minor|patch|release|prerel [<prerel>]|build <build>) <version>
$PROG compare <version> <other_version>
$PROG diff <version> <other_version>
$PROG get (major|minor|patch|release|prerel|build) <version>
$PROG --help
$PROG --version
Expand All @@ -39,7 +40,8 @@ Arguments:
<other_version> See <version> definition.
<prerel> A string as defined by PRERELEASE above.
<prerel> A string as defined by PRERELEASE above. Or, it can be a PRERELEASE
prototype string (or empty) followed by a dot.
<build> A string as defined by BUILD above.
Expand All @@ -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 <prerel> argument
introduces an incrementing numeric field which is added or
bumped. If no <prerel> 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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
159 changes: 159 additions & 0 deletions test/bump_prerel.bats
Original file line number Diff line number Diff line change
@@ -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 ]
}

Loading

0 comments on commit 20028cb

Please sign in to comment.