Comment pull requests from Jenkins

Rationale TL;DR

Let’s suppose you would like to comment a PR from a Jenkins pipeline.

The solution

Using the pipeline-github plugin

We can install the pipeline-github plugin. This plugin will provide you additional Groovy methods you can consume from your pipeline. You can find that plugin in its site.

With that plugin, you can use the commenter with a block like this:

stage('TerraformPipeline - Comment the pr') {
    when {
        expression {
            env.CHANGE_ID != null
        }
    }
    steps {
        script {
            pullRequest.comment('Pull request seems to validate just fine.')
        }
    }
}

In this case, I need to check whether the build was triggered because of a PR or not.

Using the cloudposse/github-commenter docker container

Another option would be to use this docker image. I’ve just integrated it into a Jenkins shared library. The method I use in my library looks like this:

#!/usr/bin/env groovy

import org.gusi.Constants

def call(String GITHUB_TOKEN, String REPO, String COMMENT_TYPE, String CHANGE_ID, String REGEX, String FILE) {
    OUTPUT = sh(script: """docker run -i --rm -e GITHUB_TOKEN=${GITHUB_TOKEN} \
                             -e GITHUB_OWNER=gustauperez \ 
                             -e GITHUB_REPO=${REPO}\
                             -e GITHUB_COMMENT_TYPE=${COMMENT_TYPE} \
                             -e GITHUB_PR_ISSUE_NUMBER=${CHANGE_ID} \
                             -e GITHUB_DELETE_COMMENT_REGEX=\"${REGEX}\" \
                             -e GITHUB_COMMENT_FORMAT=\"My comment:<br/>\" \
                             -e GITHUB_COMMENT=\"\$(cat ${WORKSPACE}/${FILE})\" cloudposse/github-commenter:latest """, returnStdout:true).trim()
    return OUTPUT
 }

In my pipelines, I call that method with:

prCommenter(GITHUB_TOKEN, "terraform_infra", "pr", env.CHANGE_ID, "Refreshing Terraform state in-memory prior to plan..", "terraform.out")

Take into account the GITHUB_DELETE_COMMENT_REGEX argument. That one is important because it will remove any previous comment maching that regex pattern. That works cool because you can run multiple times the pipeline and avoid having multiple comments that probably wouldn’t interest you at all.

Select a field depending on another field value

Rationale TL;DR

Let’s suppose you want to query a value depending on the value of another field in the json structure.

The solution

jq to the rescue

Let’s suppose we have to deal with a JSON like this:

{
  "zones": [
    {
      "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "name": "gusi.me",
      "ttl": 86400,
      "registrar": "",
      "legacy_dns_host": "",
      "legacy_ns": [
        ...
      ],
      "ns": [
        ...
      ],
      ...
      "records_count": 6
    },
    {
      "id": "yyyyyyyyyyyyyyyyyyyyyyyy",
      "name": "gusi.wtf",
      "ttl": 86400,
      "registrar": "",
      "legacy_dns_host": "",
      "legacy_ns": [],
      "ns": [
        ....
      ],
      ...
      "records_count": 4
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "per_page": 100,
      "previous_page": 1,
      "next_page": 1,
      "last_page": 1,
      "total_entries": 2
    }
  }
}

Let’s suppose we want to query the id value of the zone gusi.me. That would mean that we need to query all the values of the zones array and fetch the id field of the element whose zone is gusi.me. To select that specific we’d use:

echo $json | jq -r '.zones[] | select(.name=="gusi.me")

This will iterate over the zones array, it would then select the element whose name field is gusi.me. That would return this:

{
  "id": "HTiekcmw2cscFZAyNdrnYF",
  "name": "gusi.me",
  ...
  "ns": [
  ...
  ],
  ...
  "records_count": 6
}

Now, we can pipe the resulting json and query for the id field:

echo $json | |  jq -r '.zones[] | select(.name=="gusi.me") | .id'

Note that the | is not a bash pipe but a jq pipe. Internally jq allow us to pipe the resulting json and query it.

Git merge and conflicting files

Rationale TL;DR

You made a merge from master and you found many conflicting files. After working with them, you don’t know which files are still conflicting.

The solution

Git to the rescue

Easy-peasy, you can list those files still unmerged. Something like this would work:

git diff --name-only --diff-filter=U

This would list only the name of files in the diff that are not yet merged (those conflicting on this case). From the man git-diff, the possible values of diff-filter are:

A Added
C Copied
D Deleted
M Modified
R Renamed
T have their type (mode) changed
U Unmerged
X Unknown
B have had their pairing Broken
* All-or-none