Skip to content
Advertisement

Avoid early exit from command in gitlab CI script pipeline while still capturing exit status

I am trying to generate a badge from PyLint output in a Gitlab CI script. Eventually, the job should fail if PyLint has a non-zero exit code. But before it does so, I want the badge to be created. So I have tried the following:

before_script:
    - [...]
    - mkdir -p public
script:
    - pylint lib --disable R,missing-docstring,wrong-import-order --reports=y | tee public/pylint-report.txt
    - export SUCCESS=${PIPESTATUS[0]}
    - SCORE=$(tail -n 2 public/pylint-report.txt | grep -o -P "dd?.d+/d*" | head -1)
    - echo "PyLint score ${SCORE}"
    - python3.6 -m pybadges --left-text=PyLint --right-text=${SCORE} > public/pylint.svg
    - exit ${SUCCESS}
artifacts:
    when: always
    [...]

This works fine if the PyLint exit code is 0:

$ mkdir -p public
$ pylint lib --disable R,missing-docstring,wrong-import-order --reports=y | tee public/pylint-report.txt; export SUCCESS=${PIPESTATUS[0]}
[Pylint report output]
$ SCORE=$(tail -n 2 public/pylint-report.txt | grep -o -P "dd?.d+/d*" | head -1)
$ echo "PyLint score ${SCORE}"
PyLint score 10.00/10
$ python3.6 -m pybadges --left-text=PyLint --right-text=${SCORE} > public/pylint.svg
$ exit ${SUCCESS}
Uploading artifacts...
public/pylint-report.txt: found 1 matching files   
public/pylint.svg: found 1 matching files          
Uploading artifacts to coordinator... ok            id=XXX responseStatus=201 Created token=XXX
Job succeeded

However, when PyLint exits with non-zero, the script is aborted after the first line:

$ mkdir -p public
$ pylint lib --disable R,missing-docstring,wrong-import-order --reports=y | tee public/pylint-report.txt
[Pylint report output]
Uploading artifacts...
public/pylint-report.txt: found 1 matching files   
WARNING: public/pylint.svg: no matching files      
Uploading artifacts to coordinator... ok            id=XXX responseStatus=201 Created token=XXX
ERROR: Job failed: exit code 1

To clarify: I want the job to fail, but I want to make sure the script always runs all the lines. Only the exit command in the last line should determine the job status.

This runs in a container that uses the Bash.

I expected the tee command to always exit with 0 so that the first script line should never fail. But that does not seem to be the case.

I have tried appending a || true call to the first line, but then the following line, SUCCESS=${PIPESTATUS[0]}, is always 0; perhaps this refers to the root cause.

Also, I have tried to append the export call (now second line) to the first line, separated by a semicolon. Again, no difference even though I also expected the export call always to exit with 0.

My question is hence: why can the first line of the script exit with a non-zero code? How do I prevent this?

Or maybe: is there an easier way to achieve the same goal?

Advertisement

Answer

Gitlab sets a bunch of “helpful” shell options you don’t actually want. Among these is errexit, aka set -e, and pipefail (which is generally a good idea, but in conjunction with set -e means your script exits if any component of a pipeline fails).

To work around this one:

{ SUCCESS=0; pylint lib ...args... || SUCCESS=$?; } > >(tee public/pylint-report.txt)

We’re sitting SUCCESS directly here (no need for export), so you don’t need to refer to PIPESTATUS later. Branching on the return value of a command marks that command as “checked”, so it isn’t treated as a failure for purposes of errexit.


BTW, for background on set -e and why it’s something you really don’t want, see BashFAQ #105.

As another aside, all-caps variable names are used for variables meaningful to the shell or POSIX-specified tools, whereas names with at least one lowercase character are reserved for application use and guaranteed not to collide. See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, keeping in mind that setting a shell variable will overwrite any preexisting like-named environment variable.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement