https://phili.pe/posts/using-grep-for-simple-ci-tasks/
[e29f0]
PHILI.PE
* About
* Posts
* GitHub
* Twitter
Grep one-liners as CI tasks Leveraging grep's exit status and output
for simple code checks
January 8, 2022
The ubiquitous grep utility can go a long way in enforcing simple
rules in a codebase. Plenty of options and readable output combined
with a smart exit status allow for simple one-liners in your
continuous integration setup that are easy to write and maintain.
The other day my team encountered an issue that we wanted to prevent
via a CI task. We needed a quick way to ensure that our automated
Android release builds would not contain any missing translations.
These are typically stored in strings.xml files using XML
tags.
This grep command was all we needed to quickly^1 find such offenses:
$ grep --recursive --line-number --include=strings.xml '>\(""\| *\)$'
The output reads rather nicely and tells us precisely where the
offenses are, also thanks to the additional --line-number option:
./app/src/main/res/values/strings.xml:23:
./app/src/main/res/values-fr/strings.xml:147: ""
A cool thing about grep is that it has a smart exit status. From GNU
grep's man page:
Normally the exit status is 0 if a line is selected, 1 if no
lines were selected, and 2 if an error occurred.
Thus, as long as we have offenses we get an exit status 0, denoting
success. Once we fix the offenses no lines will be matched by grep
and the exit status will be 1, denoting that it was unsuccessful.
Since we want to fail the CI task in our grep one-liner, we need to
invert this exit status. Thus, when no offenses are found, we need to
exit with 0, otherwise with 1.
Shell script allows a pipeline of one or more commands such as cmdA
or cmdA | cmdB to be prefixed with a ! (followed by a space), i.e. !
cmdA or ! cmdA | cmdB. This alters the exit status as described in
the Shell Command Language reference:
If the pipeline does not begin with the ! reserved word, the exit
status shall be the exit status of the last command specified in
the pipeline. Otherwise, the exit status shall be the logical NOT
of the exit status of the last command. That is, if the last
command returns zero, the exit status shall be 1; if the last
command returns greater than zero, the exit status shall be zero.
Thus, to get the desired inverted exit status for our CI task we just
need to prefix grep with !.
If we want to additionally print a success message when no offenses
where found, we can do so with an echo "message" which we combine
with a logical && operator.
Equipped with this knowledge, we conclude with a generic one-liner
that fails the task in case offenses are found and prints a success
message otherwise:
! grep [option...] [patterns] [file...] && echo "No offenses found"
1. I personally prefer ripgrep as it is much faster than grep, but
usually that is not available on CI machines. -
By Philipe Fatio