Report Build Status to GitLab/GitHub
This example will introduce you to a workflow that can be leveraged to update a pipeline’s build status on a remote GitLab or GitHub repository within your source project’s CI pipeline:
We will be using the
GitLab/GitHub
API to post the build status to a specific commit.
This can easily be accomplished with newer version of curl
and
GitLab’s file CI/CD variable type.
In this example secrets are stored during the life of the job, limited to a
user’s permissions, and will not be visable in either the job log or
to other users on multi-tenant systems.
For GitLab you will require Pesronal Access Token
(or Project Access Token
if targeting a self-hosted instance) with a Developer
role in your
target project and an API
scope.
Add this to your source projec as a CI/CD variable:
We strongly advise that the GITLAB_CURL_HEADERS variable is limited to specific environment to prevent including it in every job in your pipeline.
Finally once you’ve updated your project’s CI/CD settings and added
the necessary script you should incorporate it into your pipeline
(the .gitlab-ci.yml
file). The example jobs/stages below are envisioned
as additions to already existing stages using the
.pre and .post
functionality.
1.report-status:
2 variables:
3 STATUS_GITLAB: https://gitlab.com
4 STATUS_PROJECT: 123456
5 STATUS_NAME: code
6 script:
7 # For complete details on the GitLab API please see:
8 # https://docs.gitlab.com/ee/api/commits.html#post-the-build-status-to-a-commit
9 - curl -X POST -H @${GITLAB_CURL_HEADERS} ${STATUS_GITLAB}/api/v4/projects/${STATUS_PROJECT}/statuses/${CI_COMMIT_SHA}?state=${CI_JOB_NAME}\&name=${STATUS_NAME}\&target_url=${CI_PIPELINE_URL}
10 environment:
11 name: reporting-gitlab
12 dependencies: []
13
14pending:
15 stage: .pre
16 extends:
17 - .report-status
18
19success:
20 stage: .post
21 extends:
22 - .report-status
23
24failure:
25 stage: .post
26 extends:
27 - .report-status
28 rules:
29 - when: on_failure
For GitHub you will require an
access token.
with the repo:status
scope.
Add this to your source projec as a CI/CD variable:
We strongly advise that the GITHUB_CURL_HEADERS variable is limited to specific environment to prevent including it in every job in your pipeline.
Finally once you’ve updated your project’s CI/CD settings and added
the necessary script you should incorporate it into your pipeline
(the .gitlab-ci.yml
file). The example jobs/stages below are envisioned
as additions to already existing stages using the
.pre and .post
functionality.
1.report-status:
2 variables:
3 STATUS_PROJECT: group/project
4 STATUS_NAME: example
5 script:
6 # For complete details on the GitHub API please see:
7 # https://developer.github.com/v3/repos/statuses
8 - curl -X POST -H @${GITHUB_CURL_HEADERS} https://api.github.com/repos/${STATUS_PROJECT}/statuses/${CI_COMMIT_SHA}?state=${CI_JOB_NAME}\&context=${STATUS_NAME}\&target_url=${CI_PIPELINE_URL}
9 environment:
10 name: reporting-github
11 dependencies: []
12
13pending:
14 stage: .pre
15 extends:
16 - .report-status
17
18success:
19 stage: .post
20 extends:
21 - .report-status
22
23failed:
24 stage: .post
25 extends:
26 - .report-status
27 rules:
28 - when: on_failure
After completion trigger a pipeline from the target project and review the results on your source project:
Note that in either case you will need to ensure that the GitLab CI project
pipelines are publicly available or that necessary reviewers have access
to the instance for the link (CI_PIPELINE_URL
) to function. Else they
will only be able to observe the status with not additioanl context.
Python Scripts
If using an older deployment with Curl prior v7.55 it is advisable to utlize a Python script on multi-tenant resources:
1#!/usr/bin/env python3
2
3import os
4import requests
5
6# In order to improve the readability of this example we've seperated
7# obtaining the target environment variables from the request.
8# Note that any variables prefixed by "BUILDSTATUS_" are established
9# by the project maintainer, else they are provided by the runner.
10
11project = os.getenv('BUILDSTATUS_PROJECT')
12api = os.getenv('BUILDSTATUS_APIURL')
13token = os.getenv('BUILDSTATUS_TOKEN')
14name = os.getenv('BUILDSTATUS_JOB')
15
16sha = os.getenv('CI_COMMIT_SHA')
17state = os.getenv('CI_JOB_NAME')
18url = os.getenv('CI_PIPELINE_URL')
19
20# https://docs.gitlab.com/ee/api/commits.html#post-the-build-status-to-a-commit
21# Load and identify requirement from environment.
22status = {
23 'id': project,
24 'sha': sha,
25 'state': state,
26 'name': name,
27 'target_url': url
28}
29r = requests.post("{}/projects/{}/statuses/{}".format(api, project, sha),
30 headers={'PRIVATE-TOKEN': token},
31 data=status)
32if r.status_code != 201:
33 print(r.text)
34 exit(1)
In the above script we identify all variables from the environment. You may recognize that we have used several predefined variables that are provided to each CI job as well as several of our own:
BUILDSTATUS_PROJECT: The upstream project ID.
BUILDSTATUS_APIURL: The sites API url (e.g. https://gitlab.com/api/v4)
BUILDSTATUS_TOKEN: An
api
scoped personal access token stored in the source GitLab as a masked CI/CD variable.BUILDSTATUS_JOB: Name of the job as it will appear on the source project.
1#!/usr/bin/env python3
2
3import json
4import os
5import requests
6
7# In order to improve the readability of this example we've seperated
8# obtaining the target environment variables from the request.
9# Note that any variables prefixed by "BUILDSTATUS_" are established
10# by the project maintainer, else they are provided by the runner.
11
12owner = os.getenv('BUILDSTATUS_OWNER')
13project = os.getenv('BUILDSTATUS_PROJECT')
14api = os.getenv('BUILDSTATUS_APIURL')
15token = os.getenv('BUILDSTATUS_TOKEN')
16name = os.getenv('BUILDSTATUS_JOB')
17
18sha = os.getenv('CI_COMMIT_SHA')
19state = os.getenv('CI_JOB_NAME')
20url = os.getenv('CI_PIPELINE_URL')
21
22# https://developer.github.com/v3/repos/statuses/
23# Load and identify requirement from environment.
24status = {
25 'state': state,
26 'target_url': url,
27 'context': name
28}
29
30r = requests.post("{}/repos/{}/{}/statuses/{}".format(api, owner, project, sha),
31 headers={'Authorization': 'token {}'.format(token)},
32 data=json.dumps(status))
33if r.status_code != 201:
34 print(r.text)
35 exit(1)
In the above script we identify all variables from the environment. You may recognize that we have used several predefined variables that are provided to each CI job as well as several of our own:
BUILDSTATUS_OWNER: Project owner/group as it appears in the repository’s url.
BUILDSTATUS_PROJECT: Project name as it appears in the repository’s url.
BUILDSTATUS_APIURL: The sites API url (e.g. https://api.github.com)
BUILDSTATUS_TOKEN: A
repo:status
scoped access token stored in the source GitLab as a masked CI/CD variable.BUILDSTATUS_JOB: Name of the job as it will appear on the source project.