I got this working. First off, you can't use the GitHub Checks API with a user/password combo, nor with a token. You MUST set up a GitHub app to be able to generate the proper tokens. Here's the process I used:
- set up a new GitHub app at https://github.com/organizations/YOUR_ORG/settings/apps/new. You can also do this from a user account, provided that the user account is listed as an owner of every repo for which you want to write status checks. (If you mess this up (like I did) you can use the "transfer app" option to move it to the right place.) The only required field should be the homepage URL, and you can put in a bogus one. Your app should be private, not public. Install that app.
- GitHub will save a PRIVATE KEY to your computer; save it somewhere that your build machine can access it (we'll say /path/to/key.pem. You will also need your app ID from https://github.com/organizations/YOUR_ORG/settings/apps/your-app-name
- Generate a JWT, which consists of 3 base64-encoded chunks separated by . characters. For each of these chunks, delete all newlines and =, change all / to _, and change all + to -.
- The first chunk is {{ {"alg":"RS256"}
}} , base64-encoded. Literally eyJhbGciOiJSUzI1NiJ9
-
- The second chunk is the base64-encoded result of a JSON object with 3 fields: iat, which holds the current unix epochal time (what you'd get from date -u '+%s'), exp, the expiration time (maximum of 600 more than iat, i.e. 10 minutes), and the issuer iss which should be set to your app ID
- The 3rd chunk is calculated with the shell as follows, as I'm not sure how to accomplish it in a pure-jenkins-groovy way:
echo "$CHUNK1.$CHUNK2" | openssl dgst -sha256 -sign /path/to/key.pem -binary | openssl base64
Note that openssl base64 will split the signature over several lines, so just strip out the newlines
- With the JWT, we can get the installation ID.
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/app/installations
This should return a JSON array with only one installation. Get its installation ID (the ["id"] field), which will be an integer e.g. {{ 8675309 }}.
- With the installation ID, we can get an installation token. This will have specific permissions for accesing the checks API on specific repositories (by default, all repositories – so we leave this blank).
curl -s -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.YOUR_JWT_TOKEN.etc" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d '{"permissions":{ "checks":"write"}}' https://api.github.com/app/installations/8675309/access_tokens
This will give you a response with a ["token"] field, something like v1.fedcba9876543210.
- With the installation token, we can do a quick sanity check that we can "see" the repositories we want to access.
curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" https://api.github.com/installation/repositories
. You should see all your repositories in there
- Finally, with the installation token, we can upload an actual check run (assuming you have permissions to access YOUR_ORG/YOUR_REPO!
curl -s -H "Authorization: token v1.fedcba9876543210" -H "Accept: application/vnd.github.antiope-preview+json" -H "Accept: application/vnd.github.machine-man-preview+json" -d @check-run-definition.json https://api.github.com/repos/YOUR_ORG/YOUR_REPO/check-runs
Where check-run-definition.json contains
{
"name": "my check name",
"head_sha": "SOME_ACTUAL_SHA",
"external_id": "42",
"details_url": "http://jenkins.your-org.com/foo",
"started_at": "2019-12-18T07:57:00-05:00",
"completed_at": "2019-12-18T07:58:00-05:00",
"conclusion": "failure",
"output": {
"title": "My Output title",
"summary": "my output summary _checking_ `markdown` **support**",
"text": "my output text _checking_ `markdown` **support**",
"annotations": [
{
"path": "Jenkinsfile",
"start_line": 1,
"end_line": 1,
"start_column": 3,
"end_column": 4,
"annotation_level": "warning",
"message": "my message _checking_ `markdown` **support** (it isn't)",
"title": "my title _checking_ `markdown` **support** (it isn't)",
"raw_details": "my raw details _checking_ `markdown` **support** (it isn't)"
}
]
}
}
Results: |