IT

Static code check for a Gerrit refspec

I wanted to introduce static code check for our repo in Gerrit. Gerrit allows you to fetch the given refspec, then you may use git diff-tree to see what files are in the given change. The trouble is that you already need the repo cloned to do so, which is not that efficient for a larger repo. Fear not, the Gerrit ssh API is to the rescue. Let’s how to make it work.

Let’s say you set up a Jenkins job to be triggered from Gerrit whenever a patchset is created. Gerrit sends a bunch of parameter about the given commit to Jenkins, eg.

GERRIT_CHANGE_ID=I95e8231824c3d63d063b10c8a1d638cc42e3dd44
GERRIT_CHANGE_NUMBER=40175
GERRIT_PROJECT=myrepo
GERRIT_REFSPEC=refs/changes/75/40175/4
Now let’s get the files from the patchset using the Gerrit SSH API (you need the jq utility to process the json output):
ssh -p $GERRIT_PORT $GERRIT_HOST gerrit query –files –format JSON change:$GERRIT_CHANGE_NUMBER –current-patch-set | jq -r “select(.project==\”$GERRIT_PROJECT\”)|.currentPatchSet.files[].file”
The above command produces the following output:
/COMMIT_MSG
aaa.sh

bbb.groovy

Now we have to filter /COMMIT_MSG, since it’s not an actual file in the commit, rather the commit message which we are not interested in this time. So we have two files in the patchset: ‘aaa.sh’ and ‘bbb.groovy’.

Once we have the affected files we use git archive to get only these specific files even from a large repo:

git archive –format=tar –remote=ssh://${GERRIT_HOST}:${GERRIT_PORT}/${GERRIT_PROJECT} “$GERRIT_REFSPEC” “${FILES[@]}” | tar xf –

The FILES array is supposed to contain “aaa.sh” and “bbb.groovy” strings. Note that I omitted the –prefix parameter, so it dumps the files to the current directory, ie. to $WORKSPACE

Finally we run a docker container to do some checks on these files:

docker run –rm -u $(id -u):$(id -g) –name “${JOB_NAME}-${BUILD_NUMBER}” -v “${WORKSPACE}:${WORKSPACE}” -w “$WORKSPACE” -e PATTERN=”.*(sh|inc|groovy)” “static_check:latest”

Notice the PATTERN variables in which we may define what file extensions we want to include in the static code check. I recommend to make this a job variable.

The actual entrypoint in the container executes the following code:

#!/bin/bash

set -o nounset
set -o errexit
set -o pipefail

PATTERN=”${PATTERN:-.*(sh|inc|groovy)}”

error() {
echo “$*”
exit 1
}

check_for_trailing_whitespace() {
local f=”$1″

if grep ‘[[:blank:]]$’ “$f”; then
error “Trailing whitespace in the above lines”
fi
}

while read -r f; do
echo “Checking ${f}”

check_for_trailing_whitespace “$f”

if [[ “$f” =~ .sh$ || “$f” =~ .inc$ ]]; then
shellcheck “$f”
fi
done < <(find . -type f -regextype posix-extended -regex “$PATTERN”)

The above code check every file for trailing white characters, and runs shellcheck for each file with .sh or .inc extensions.