Pix4D / cogito

Concourse resource for GitHub Commit Status and Google Chat notifications
MIT License
34 stars 14 forks source link
cicd concourse concourse-resource github-status go google-chat owner-platform-ci

cogito

Cogito (COncourse GIT status resOurce) is a Concourse resource to update the GitHub commit status during a build. The name is a humble homage to René Descartes.

It can also send a message to a chat system (currently supported: Google Chat). This allows to reduce the verbosity of a Concourse pipeline and especially to reduce the number of resource containers in a Concourse deployment, thus reducing load. Chat and GitHub commit status update can be used independently (see examples below).

Written in Go, it has the following characteristics:

Contributing and Development

This document explains how to use this resource. See CONTRIBUTING for how to build the Docker image, develop, test and contribute to this resource.

Please, before opening a PR, open a ticket to discuss your use case. This allows to better understand the why of a new feature and not to waste your time (and ours) developing a feature that for some reason doesn't fit well with the spirit of the project or could be implemented differently. This is in the spirit of Talk, then code.

We care about code quality, readability and tests, so please follow the current style and provide adequate test coverage. In case of doubts about how to tackle testing something, feel free to ask.

Semver, releases and Docker images

This project follows Semantic Versioning and has a CHANGELOG.

NOTE Following semver, no backwards compatibility is guaranteed as long as the major version is 0.

Releases are tagged in the git repository with the semver format vMAJOR.MINOR.PATCH (note the v prefix). The corresponding Docker image has tag MAJOR.MINOR.PATCH and is available from DockerHub.

Which Docker tag to use?

Examples

Only GitHub commit status

See also pipelines/cogito.yml for a bigger example and for how to use YAML anchors to reduce as much as possible YAML verbosity.

resource_types:
- name: cogito
  type: registry-image
  check_every: 24h
  source:
    repository: pix4d/cogito

resources:
- name: gh-status
  type: cogito
  check_every: never
  source:
    owner: ((github-owner))
    repo: ((your-repo-name))
    access_token: ((github-PAT))

- name: the-repo
  type: git
  source:
    uri: https://github.com/((github-owner))/((your-repo-name))
    branch: ((branch))

jobs:
  - name: autocat
    on_success:
      put: gh-status
      inputs: [the-repo]
      params: {state: success}
    on_failure:
      put: gh-status
      inputs: [the-repo]
      params: {state: failure}
    on_error:
      put: gh-status
      inputs: [the-repo]
      params: {state: error}
    on_abort:
      put: gh-status
      inputs: [repo.git]
      params: {state: abort}
    plan:
      - get: the-repo
        trigger: true
      - put: gh-status
        inputs: [the-repo]
        params: {state: pending}
      - task: maybe-fail
        config:
          platform: linux
          image_resource:
            type: registry-image
            source: { repository: alpine }
          run:
            path: /bin/sh
            args:
              - -c
              - |
                set -o errexit
                echo "Hello world!"

GitHub commit status plus chat notification

The absolute minimum is adding key gchat_webhook to the source configuration of the previous example:

- name: gh-status
  type: cogito
  check_every: never
  source:
    owner: ((github-owner))
    repo: ((your-repo-name))
    access_token: ((github-PAT))
    gchat_webhook: ((gchat_webhook))

Chat notification only

The absolute minimum is setting keys gchat_webhook and sinks in the source configuration:

- name: chat-notify
  type: cogito
  check_every: never
  source:
    sinks:
      - gchat
    gchat_webhook: ((gchat_webhook))

Effects

Build states mapping

There is only a partial matching between Concourse and GitHub Commit status API, so we map as good as we can according to this table:

Concourse
State
Color and meaning GitHub
Commit status API
Chat notification
running 🟡 - running pending pending
success 🟢 - task exited 0 success success
failure 🔴 - task exited non 0 failure failure
error 🟠 - any other error besides failure or abort (pipeline configuration error, network error, timeout, ...) error error
abort 🟤 - human-initiated abort error abort

The colors are taken from the Concourse UI and are replicated to the chat message.

Effects on GitHub

With reference to the GitHub Commit status API, the POST parameters (state, target_url, description, context) are set by Cogito and rendered by GitHub as follows:

Screenshot of GitHub UI

Effects on Google Chat

Screenshot of Google Chat UI

Source Configuration

GitHub commit status only

Required keys

Optional keys

GitHub commit status plus chat notifications

Required keys

Optional keys

Chat notification only

Required keys

Optional keys

Suggestions

We suggest to set a long interval for check_interval, for example 24 hours, as shown in the example above. This helps to reduce the number of check containers in a busy Concourse deployment and, for this resource, has no adverse effects.

The check step

No-op. Will always return the same version, dummy.

The get step

No-op.

The put step

Sets the GitHub commit status for a given commit, following the GitHub Commit status API. The same commit can have multiple statuses, differentiated by parameter context.

If the source block has the optional key gchat_webhook, then it will also send a message to the configured chat space, based on the state parameter.

Required params

Optional params for GitHub commit status

Optional params for chat

Note on the put inputs

If using only GitHub commit status (no chat), the put step requires only one "put inputs". For example:

on_success:
  put: gh-status
  inputs: [the-repo] # name of the git resource corresponding to the GitHub repo to be updated.
  params: {state: success}

If using both GitHub Commit status and the chat_message_file parameter, the put step requires only two "put inputs". For example:

on_success:
  put: gh-status
  # the-repo: git resource; the-message-dir: "output" of a previous task
  inputs: [the-repo, the-message-dir]
  params:
    state: success
    chat_message_file: the-message-dir/msg.txt

If using send to chat only and the chat_message_file parameter, the put step requires only one "put inputs". For example:

on_success:
  put: chat-only
  # the-message-dir: "output" of a previous task
  inputs: [the-message-dir]
  params:
    state: success
    chat_message_file: the-message-dir/msg.txt

If using send to chat only without chat_message_file parameter, inputs may be left empty.

on_success:
  put: gh-status
  inputs: []
  params:
    state: success
    chat_message: "This is the custom chat message."

The reasons of this strictness is to help you have an efficient pipeline, since if the "put inputs" list is not set explicitly, then Concourse will stream all inputs used by the job to this resource, which can have a big performance impact. From the "put inputs" documentation:

inputs: [string]

Optional. If specified, only the listed artifacts will be provided to the container. If not specified, all artifacts will be provided.

GitHub OAuth token

Follow the instructions at GitHub personal access token to create a personal access token.

Give to it the absolute minimum permissions to get the job done. This resource only needs the repo:status scope, as explained at GitHub Commit status API.

NOTE: The token is security-sensitive. Treat it as you would treat a password. Do not encode it in the pipeline YAML and do not store it in a YAML file. Use one of the Concourse-supported credentials managers, see Concourse credential managers.

See also the section Integration tests for how to securely store the token to run the end-to-end tests.

Caveat: GitHub rate limiting

From GitHub REST API:

Rate limiting

For API requests using Basic Authentication or OAuth, you can make up to 5000 requests per hour. All OAuth applications authorized by a user share the same quota of 5000 requests per hour when they authenticate with different tokens owned by the same user.

For unauthenticated requests, the rate limit allows for up to 60 requests per hour. Unauthenticated requests are associated with the originating IP address, and not the user making requests.

GitHub resets the limit once per hour (no sliding window). If rate limited, cogito will wait up to 15 minutes for the limit to clear, or fail immediately if it would have to wait more. The error message in the output of the put step will mention the cause.

License

This code is licensed according to the MIT license (see file LICENSE).