5

I'm writing a script to execute tasks before my end to end tests. One of the steps is to select the branch where those tests were written. Sometimes the script changes between different branches so I need the script to update itself before the actual execution.

Is it possible for a bash script in my git repository to update itself and execute the new version only?

In summary: When I execute script.sh I want it to check git if a new version is available and if so, download this new version and execute it while the old version simply dies.

douglaslps
  • 8,068
  • 2
  • 35
  • 54

1 Answers1

12

This is the script I came up with:

#!/bin/bash

SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
SCRIPTNAME="$0"
ARGS="$@"
BRANCH="Your_git_branch"

self_update() {
    cd $SCRIPTPATH
    git fetch

    [ -n $(git diff --name-only origin/$BRANCH | grep $SCRIPTNAME) ] && {
        echo "Found a new version of me, updating myself..."
        git pull --force
        git checkout $BRANCH
        git pull --force
        echo "Running the new version..."
        exec "$SCRIPTNAME" "$@"

        # Now exit this old instance
        exit 1
    }
    echo "Already the latest version."
}

main() {
   echo "Running"
}

self_update
main
douglaslps
  • 8,068
  • 2
  • 35
  • 54
  • 3
    Use `exec $SCRIPTNAME "$@"` to replace the current version with the new version. In principle, you should use quotes around `"$SCRIPTNAME"` too. The assignment of `ARGS="$@"` loses the structure of the argument list — it matters if there are spaces in the arguments. Alternatively, in Bash, use `ARGS=( "$@" )` and then `exec "$SCRIPTNAME" "${ARGS[@]}"`. Report the error and exit if you fail to exec the new version. – Jonathan Leffler Feb 12 '16 at 15:18
  • Maybe its just me, but good practice I would use curly brackets when calling your variables that have spaces in them. Example ${SCRIPTNAME} and ${SCRIPT}. Makes for a cleaner script, especially when you have identical variables (ex SCRIPTNAME and SCRIPT). Current script written doesn't have these issues, but if you had an extremely robust script this can be very beneficial. – IT_User Feb 12 '16 at 16:37
  • 3
    @bluerojo: The use or non-use of `${VAR}` vs `$VAR` has no effect on the handling of spaces. The primary benefits of the `${VAR}` notation are its use with arrays, with the specialized substitutions (for example, `${VAR:-default}`, etc), and in longer substitutions where their absence would lead to the wrong result (`${PREFIX}_main_${SUFFIX}` vs `$PREFIX_main_$SUFFIX`). To a first approximation, to preserve spacing, you need double quotes (`"${SPACED_OUT}"`). The full story is more complex and nuanced than fits in a single comment, of course. – Jonathan Leffler Feb 12 '16 at 16:47
  • Note that the comment '# Now exit this old instance' is not accurate. The following `exit 0` indicates success, but is only executed _if_ the `exec` failed. That's why I said "report the error and exit if you fail to exec". If you reach that `exit 0`, your self-update option didn't work correctly (maybe the new version from `git` deleted the script?), and you have problems. It is good that you exit; it is not necessarily a good idea to exit and report success (use `exit 1` at a bare minimum). The `exec` completely replaces the current (old) version of the script with the new version. – Jonathan Leffler Feb 12 '16 at 16:50
  • Thank you for that description Jonathan. Greatly appreciated – IT_User Feb 12 '16 at 18:19
  • https://stackoverflow.com/a/59745320/874188 points out several bugs in this script. – tripleee Jan 15 '20 at 04:53