I have a Hudson job that periodically merges changes from an upstream bazaar repository.
Currently, when there are no changes upstream, Hudson reports this job as a failure because the bzr commit command returns with an error. My script looks like something this:
bzr branch lp:~lorinh/project/my-local-branch
cd my-local-branch
REV_UPSTREAM=`bzr version-info lp:project --custom --template="{revno}"`
bzr merge lp:project
bzr commit -m "merged upstream version ${REV_UPSTREAM}"
./run_tests.sh
bzr push lp:~lorinh/project/my-local-branch
If there are no changes to merge, the Hudson console output looks something like this:
+ bzr branch lp:~lorinh/project/my-local-branch
Branched 807 revision(s).
+ bzr merge lp:project
Nothing to do.
+ bzr commit -m merged upstream version 733
Committing to: /var/lib/hudson/jobs/merge-upstream/workspace/myproject/
aborting commit write group: PointlessCommit(No changes to commit)
bzr: ERROR: No changes to commit. Use --unchanged to commit anyhow.
Sending e-mails to: me@example.com
Finished: FAILURE
The problem is that I don't want Hudson to report this as a failure. How do I modify my commands so the script terminates at the failed commit, but it isn't interpreted by Hudson as an error? I tried changing the commit command to:
bzr commit -m "merged upstream version ${REV_UPSTREAM}" || exit
But that didn't work.
(Note: I realize I could use Hudson's "Poll SCM" instead of "Build periodically". However, with bazaar, if somebody does a push with local commits that were done before the most recent modifications, then Hudson won't detect a change to the repository.)
You were very close! Here's the corrected version of what you were trying:
bzr commit -m "merged upstream version ${REV_UPSTREAM}" || exit 0
This now does what you asked for, but isn't perfect. I'll get to that later.
Note the tiny important change from your version - we are now being explicit that we should exit with code 0 (success), if the bzr command does not do so. In your version, exit (with no argument) will terminate your script but return the exit code of the last command executed - in this case the bzr commit.
More about exit
How do we find out about this behaviour of exit? The exit
command is a shell built-in - to find documentation on it we use the help command:
help exit
Which on my machine tells me:
exit: exit [n]
Exit the shell.
Exits the shell with a status of N. If N is omitted, the exit status
is that of the last command executed.
Here's a decent tutorial on exit and exit codes in the bash shell
Hudson and exit codes
Hudson follows this common convention of interpreting exit code 0 as success, and any other code as failure. It will flag your build as a failure if the build script it executes exits with a non-zero code.
Why your script is stopping after the bzr commit
If, as you say, you have the following and your script is stopping after the bzr commit...
bzr commit -m "merged upstream version ${REV_UPSTREAM}"
./run_tests.sh
... I suspect your script has an instruction such as set -e
or is being invoked with something like bash -e build_script.sh
Either of these tells the shell to exit immediately if a command exits with a non-zero status, and to pass along that same "failure" exit code. (There are subtleties - see footnote 1).
Disabling exit-on-error
While this behaviour of exiting on error is extremely useful, sometimes we'd like to disable it temporarily. You've found one way, in
bzr commit whatever || true
We can also disable the error-checking with set +e.
Here's a pattern you may find useful. In it we will:
set +e
)bzr commit whatever
set -e
)Let's implement that. Again we'll exit 0 (success) if the bzr command failed.
set +e
bzr commit whatever
commit_status=$?
set -e
if [[ "$commit_status" != "0" ]]; then
echo "bzr commit finds nothing to do. Build will stop, with success"
exit 0
fi
echo "On we go with the rest of the build script..."
Note that we bracket as little as possible with set +e / set -e. If we have typos in our script in that section, they won't stop the script and there'll be chaos. Read the section "Avoiding set -e" in the post "Insufficiently known POSIX shell features" for more ideas.
What's wrong with foo || exit 0
?
As I mentioned earlier, there's a problem with our first proposed solution. We've said that when bzr commit
is non-zero (i.e. it doesn't commit normally) we'll always stop and indicate success. This will happen even if bzr commit
fails for some other reason (and with some other non-zero exit code): perhaps you've made a typo in your command invocation, or bzr can't connect to the repo.
In at least some of these cases, you'd probably want the build to be flagged as a failure so you can do something about it.
Towards a better solution
We want to be specific about which non-zero exit codes we expect from bzr, and what we'll do about each.
If you look back at the set +e / set -e pattern above, it shouldn't be difficult to expand the conditional logic (if) above into something that can deal with a number of specific exit codes from bzr, and with a catch-all for unanticipated exit codes which (I suggest) fails the build and reports the offending code and command.
To find out the exit codes for any command, read the docs or run the command and then run echo $?
as the next command. $? holds the exit code of the previous command.
Footnote 1: The exit-on-error behaviour switched with set -e has some subtleties you'll need to read up on, concerning behaviour when commands are in pipelines, conditional statements and other constructs.