2

I am trying to implement a solution to increment a version. Here's what I have come up with:

#!/bin/sh -x

VAR=1.0.1 # retrieved from Gitlab API

case $1 in
patch)
    TAG=${VAR%.*}.$((${VAR##*.} + 1))
    ;;

major)
    TAG=$((${VAR%%.*} + 1)).0.0
    ;;

*)
    tmp=${VAR%.*}
    minor=${tmp#*.}
    TAG=${VAR%%.*}.$((minor + 1)).0
    ;;
esac

echo $TAG

major & patch work as expected; however, I'm facing problems incrementing minor.

When bumping 1.0.1, the minor should be 1.1.0; however, my code produces 1.2.0. What am I doing wrong?

Some more info, the script is executed inside a GitlabCI pipeline.

Edit: Updated the code with the suggested answer from @jhnc

hdhruna
  • 865
  • 6
  • 15
  • I suggest to take a look at: `var=1.2.3; IFS="." read a b c <<< "$var"; echo "$a $b $c"` – Cyrus Mar 18 '21 at 17:13
  • Unfortunately can't use IFS `/bin/sh: script.sh: line 12: syntax error: unexpected redirection` – hdhruna Mar 18 '21 at 17:28
  • 2
    You tagged your question with `bash` and not with `sh`. – Cyrus Mar 18 '21 at 18:00
  • @hdhruna, ...to be clear: that's like tagging for C++ but then requiring answers to work with a C compiler. They're two different languages, and not compatible. (If your script is run with `sh yourscript`, that means it's parsed and executed as a sh script, not a bash script, even if it _does_ start with `#!/bin/bash`). – Charles Duffy Mar 18 '21 at 23:07
  • 1
    @hdhruna, ...also, `IFS` works fine with `sh`. It's `<<<` -- not `IFS` -- that's bash-only syntax. – Charles Duffy Mar 18 '21 at 23:08

2 Answers2

9

I would just:

#!/bin/sh

var=1.0.1
IFS=. read -r version minor patch <<EOF
$var
EOF

case "$1" in
patch) tag="$version.$minor.$((patch+1))"; ;;
major) tag="$((version+1)).0.0"; ;;
*)     tag="$version.$((minor+1)).0"; ;;
esac

echo $tag

Works on alpine with ash.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    @KamilCuk, since there were no examples, that was one of the reasons I chose to write the code this way. – hdhruna Mar 19 '21 at 05:46
1

Your computation for incrementing minor is:

TAG=${VAR%%.*}.$((${VAR##*.} + 1)).0

However, ${VAR##*.} gives the patch value, not the minor value, and thus your incorrect result.

To extract minor, you can use a temporary variable:

tmp=${VAR%.*}
minor=${tmp#*.}
TAG=${VAR%%.*}.$((minor + 1)).0

Alternatively, it may be more readable to set IFS and split the string on dots:

TAG=$(
   echo "$VAR" | while IFS=. read major minor patch; do
       # add sanity-checks here
       case "$1" in
          patch)
             echo "$major.$minor.$((patch+1))"
             ;;
          major)
             echo "$((major+1).0.0"
             ;;
          minor|*)
             echo "$major.$((minor+1)).0" 
             ;;
       esac
   done
)

You should probably add sanity-checks to ensure VAR is actually of form "digits dot digits dot digits".

jhnc
  • 11,310
  • 1
  • 9
  • 26
  • 2
    I think I prefer KamilCuk's IFS/<< method to mine here. There's only one variable to split, so using a while loop feels wrong and the subshell/echo malarkey adds unnecessary overhead. – jhnc Mar 19 '21 at 06:00
  • 1
    @jhnc, ...what _is_ the purpose of the `while` loop, btw? I'd think you could write `echo "$VAR" | { IFS=. read major minor patch; case "$1" in ...;; esac; }`, with no `while`, though there is indeed still subshell overhead. – Charles Duffy Mar 19 '21 at 19:19
  • @CharlesDuffy I rarely (never?) use `read` for just one line and so I typed a `while` loop on autopilot :-) Didn't even realise until I saw the other answer. – jhnc Mar 20 '21 at 16:35