How to get github app installation id on pull request event?
Asked Answered
S

3

5

I am creating a build pipeline with Github which will be create a check-run on every pull-request to analyse the performance of my application. I have created and installed the Github app to my repo and generated the private key. I need to perform Authenticating as an installation to get an access-token.

But as per the docs to get an access-token for installation, first i have to get list of installations of that app and have to find the specific installation from that list. But i am clueless how to identify the specific installation_id of app for pull request event raised on particular repository that installed my app.

I don't know what i am missing.

Sedation answered 16/9, 2019 at 16:9 Comment(3)
Webhooks that are sent for a GitHub App contain this in the payload. developer.github.com/apps/quickstart-guides/…Regarding
But how does the payload contain installation id. Based on what condition, specific installation been passed as payload. I don't know ruby. i am writing in nodeJsSedation
The payload is JSON with a top-level key of installation and has an id. I highly recommend this framework in node that should save you from doing this yourself probot.github.ioRegarding
F
6

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,
)
Freeswimming answered 22/8, 2020 at 13:49 Comment(0)
B
2

Essentially, to be code agnostic, you can call the https://api.github.com/app/installations API method to get the latest installation. But, you'd have to be sure the latest one is the one you need, or that you have received the correct user/organization so that you can search through your results for the right one. Otherwise, you'll need to use the webhook (https://developer.github.com/apps/quickstart-guides/setting-up-your-development-environment/#authenticating-as-an-installation).

Bukovina answered 13/10, 2020 at 6:15 Comment(0)
D
2

On Github website after installing the app go to:

  • Settings
  • Developer Settings
  • Github Apps
  • Click on Edit next to the app
  • Advanced in left sidebar
  • Click on the notification for installation.created

There you find installation.id in the body of the response.

Dannica answered 5/10, 2022 at 15:36 Comment(1)
This will only be relevant if you have a single user of your application (highly unlikely for most github apps), as every repo or org you add to you organization will have a unique installation ID assigned. Rather then get it from the UI, its better to parse the payload posted to /api/github/hook for the installation -> ID value. If you use the one from the UI and have more than one repo/org granted under your App, you could start posting the API calls to the wrong repo/org.Rambutan

© 2022 - 2024 — McMap. All rights reserved.