27

I am making a presubmit script. It looks like this:

function presubmit() {
  gradle test android
  gradle test ios
  gradle test server
  git push origin master
}

I want the function to exit, if any of the tests fail so it won't push a bug to git. How?

Xi 张熹
  • 8,556
  • 16
  • 48
  • 74
  • http://www.tldp.org/LDP/abs/html/exit-status.html – Mohit Sharma Nov 04 '16 at 18:51
  • You could take a look at [this](http://stackoverflow.com/questions/30078281/raise-error-in-bash-script/30078353#30078353) – ForceBru Nov 04 '16 at 18:51
  • 1
    `gradle ... || return`? – Cyrus Nov 04 '16 at 18:59
  • 2
    Everyone is assuming that `gradle` will have non zero exit on test failure. It might not be the case. I don't see documentation that says gradle will return non zero on test failure. It should be noted that the function is calling `gradle` to test a certain OS. `gradle` itself will not fail, but the tests it conducts may not pass. – alvits Nov 04 '16 at 20:08

5 Answers5

18

1. Use subshell ( .. ) with set -e; to make it more concise, you can do this:

build() {( set -e        # Fail early
  build_cmd_step_1
  build_cmd_step_2
  build_cmd_step_3
  ...
)}

Then, the function will fail on the first failure and you can intercept the exit status:

build
exit_status=$?
if [ ${exit_status} -ne 0 ]; then
  echo "We have error - build failed!"
  exit "${exit_status}"
fi

2. Alternatively, the && \ chaining inside a function is also good (https://stackoverflow.com/a/51913013/1375784), though it might get bad if you have a bigger function.

Both methods can be good, depending on your use case (in some cases using subshells may cause some unwanted side effects)

hek2mgl
  • 133,888
  • 21
  • 210
  • 235
awinecki
  • 191
  • 2
  • 4
  • 3
    be sure to NOT run `build` together with the test as it would de-activate `set -e`. So `if ! build; then...` or even `if [ build -ne 0 ]; then ..` would not work as expected. – frans Feb 03 '20 at 09:45
  • 1
    it also won't work as part of a pipeline, such as `build || echo "build failed"` – ZombieDev Jun 15 '20 at 14:57
  • Note that when used inside a function or in a script that will be sourced, `return "${exit_status}"` is often more appropriate than `exit "${exit_status}"`. https://stackoverflow.com/questions/4419952/difference-between-return-and-exit-in-bash-functions – Jasha May 12 '21 at 00:23
16

The way I do it is to add && \ after every command in the function (except the last one).

function presubmit() {
    gradle test android && \
    gradle test ios && \
    gradle test server && \
    git push origin master
}
Drew Chapin
  • 7,031
  • 5
  • 51
  • 77
  • 2
    This looks like to be the cleanest solution as using `set -e` in a function in ~/.bashrc causes the terminal to quit. – Jaakko Aug 31 '18 at 07:32
4

You can do this:

# declare a wrapper function for gradle
gradle() {
   command gradle "$@" || exit 1
}

presubmit() {
  gradle test android
  gradle test ios
  gradle test server
  git push origin master
}

declare -xf presubmit gradle

Call the function in a subshell as:

( presubmit )
chepner
  • 389,128
  • 51
  • 403
  • 529
anubhava
  • 664,788
  • 59
  • 469
  • 547
3

I would make script more granulated:

#!/bin/bash

function test() {
  gradle test android
  gradle test ios
  gradle test server
}
function push() {
  git push origin master
}
# this subshell runs similar to try/catch
(
  # this flag will make to exit from current subshell on any error inside test or push
  set -e
  test
  push
)
# you catch errors with this if
if [ $? -ne 0 ]; then
  echo "We have error"
  exit $?
fi

We track error only inside test and push. You can add more actions outside of subshell where test and push run. You can also this way add different scope for errors (let consider it as try/catch)

Kostanos
  • 7,911
  • 3
  • 39
  • 59
1

Usually when I call a function and want an error message incase it fails I do this:

presubmit || { echo 'presubmit failed' ; exit 1; }

By adding the || flag, it will determine whether which expression is TRUE.

Hope this helps :)

MichaelMMeskhi
  • 551
  • 7
  • 23
  • The OP wants `presubmit` to exit early, before `git push` is executed. This just shows how to do something additional should `presubmit` exit with a non-zero exit status. – chepner Nov 04 '16 at 20:20