If your CI/CD doesn't work with GitHub's webhooks (because of the firewall, for instance), you can do something like this:
#!/bin/bash
HEADER=$( echo -n {\"alg\":\"RS256\"} | base64 | tr -d '=' )
PAYLOAD=$( echo -n \{\"iat\":$(date -u '+%s'),\"exp\":$(( $( date -u '+%s' ) + 600 )),\"iss\":$GITHUB_APP_ID\} | base64 | tr -d '\n' | tr -d '=' | tr / _ | tr + - )
SIGNATURE=$( echo -n "$HEADER.$PAYLOAD" | openssl dgst -sha256 -sign ./private_key -binary | openssl base64 | tr -d '\n' | tr -d '=' | tr / _ | tr + - )
TOKEN=$HEADER.$PAYLOAD.$SIGNATURE
INSTALLATION_ID=$( curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.github.v3+json" https://api.github.com/app/installations | jq .[0].id )
INSTALLATION_TOKEN=$( curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.github.v3+json" -d '{"permissions":{ "checks":"write"}}' https://api.github.com/app/installations/$INSTALLATION_ID/access_tokens | jq .token | tr -d '"' )
echo $INSTALLATION_TOKEN
Or in Python:
# Implementation of: https://docs.github.com/en/developers/apps/authenticating-with-github-apps#authenticating-as-a-github-app
# Inspired by https://gist.github.com/pelson/47c0c89a3522ed8da5cc305afc2562b0
# TL;DR
# Only GitHub App has an access to annotations API. In order to access this API, we need to
# generate a token based on GitHub App's private key and ID. The token (aka JWT) is valid for only 10 min.
import json
import os
import time
import jwt
import requests
from cryptography.hazmat.backends import default_backend
path_to_github_app_key = os.environ["PATH_TO_GITHUB_APP_KEY"]
cert_bytes = open(path_to_github_app_key, "r").read().encode()
github_app_id = os.environ["GITHUB_APP_ID"]
private_key = default_backend().load_pem_private_key(cert_bytes, None)
time_since_epoch_in_seconds = int(time.time())
payload = {
# issued at time, 60 seconds in the past to allow for clock drift
"iat": time_since_epoch_in_seconds - 60,
# JWT expiration time (10 minute maximum)
"exp": time_since_epoch_in_seconds + (10 * 60),
# GitHub App's identifier
"iss": github_app_id,
}
encoded_payload = jwt.encode(payload, private_key, algorithm="RS256")
headers = {
"Authorization": "Bearer {}".format(encoded_payload),
"Accept": "application/vnd.github.v3+json",
}
resp = requests.get("https://api.github.com/app/installations", headers=headers)
installation_id = json.loads(resp.content.decode())[0]["id"]
data = '{"permissions":{ "checks":"write"}}'
resp = requests.post(
"https://api.github.com/app/installations/"
+ str(installation_id)
+ "/access_tokens",
headers=headers,
data=data,
)
installation_token = json.loads(resp.content.decode())["token"]
print(installation_token)
It took me a while, but this code works. You need to have GITHUB_APP_ID
and file private_key
containing private key generated by GitHub.
Then you can edit check runs like this:
curl -s -H "Authorization: token $GITHUB_APP_INSTALLATION_TOKEN" -H "application/vnd.github.v3+json" -d @body.json https://api.github.com/<username/organization>/$GITHUB_REPO/check-runs
Where body.json
is a JSON file containing the following data: https://developer.github.com/v3/checks/runs/
For example:
# Create check "Linter" and set its status to "queued".
# https://developer.github.com/v3/checks/runs/
import json
import os
import requests
github_repo = os.environ["GITHUB_REPO"]
github_token = os.environ["GITHUB_APP_INSTALLATION_TOKEN"]
head_sha = os.environ["COMMIT_SHA"]
check = {
"name": "Linter",
"head_sha": head_sha,
"status": "queued",
"output": {
"title": "Waiting...",
"summary": "Check code with linter",
"text": "Execute 'golangci-lint run'",
},
}
data = json.dumps(check)
headers = {
"Authorization": "token " + github_token,
"Accept": "application/vnd.github.v3+json",
}
# Send request to GitHub.
requests.post(
"https://api.github.com/repos/<username/organization>/" + github_repo + "/check-runs",
headers=headers,
data=data,
)
installation
and has anid
. I highly recommend this framework in node that should save you from doing this yourself probot.github.io – Regarding