Github

A wrapper for Github’s APIs and a collection of scripts.

In order to use it, one needs to give it an authentication token. The token is usually given using the GITHUB_API_TOKEN environmental variable. You can optionally initialize the API with a given token:

>>> from skare3_tools import github
>>> github.init(token='c7hvg6pqi3fhqwv0wvlgp4mk9agwbqk1gxc331iz')  # this is optional

REST Interface (V3)

A thin wrapper for Github’s REST API (V3).

It is intended to be easy to extend. It does not impose much structure on top of what is shown in their online documentation, and it should be easy to see the correspondence between both.

As an example, this is how one gets a list of releases and pull requests for a repository:

>>> from skare3_tools import github
>>> repo = github.Repository('sot/Chandra.Maneuver')
>>> releases = repo.releases()
>>> for release in releases:
...     print(release['name'])
Release 3.7.2
Release 3.7.1 with one test fix for 32 bit platform compat.
Version 3.7
Version 0.6
Version 0.05
>>> prs = repo.pull_requests.create(title='Make namespace package native',
...                                 head='namespace',
...                                 base='master',
...                                 body='The description goes here')
>>> prs = repo.pull_requests()
>>> for pr in prs:
...     print(f"PR #{pr['number']}: {pr['title']}")
PR #1: Make namespace package native

It is also possible to use the API directly, in case there is no appropriate high-level method:

>>> from skare3_tools import github
>>> last_tag = github.GITHUB_API_V3.get('/repos/sot/Chandra.Maneuver/releases/latest').json()
>>> last_tag['tag_name']
'3.7.2'

Getting and Editing Content

Getting content:

>>> from skare3_tools.github import github
>>> r = github.Repository('sot/test-actions')
>>> c = r.contents('README.md')
>>> c['content']
'# test-actions\n\nA realistic package with which to test GitHub actions...'

Editing content:

>>> from skare3_tools.github import github
>>> r = github.Repository('sot/test-actions')
>>> content = """
... # test-actions
...
... A realistic package with which to test GitHub actions and play around.
... """
>>> r.contents.edit('README.md', message='changing readme', content=content)

Repository Dispatch

A typical use is to dispatch an event to cause an action. For example, the docs for some repositories are deployed when a repository_dispatch event of type ‘build-docs’ is triggered. This can be seen in the corresponding workflow:

name: Deploy Docs
on:
  repository_dispatch:
    types:
    - build-docs

In this case, one can do:

>>> from skare3_tools import github
>>> r = github.Repository('sot/skare3_tools')  # changing the repository name accordingly!
>>> r.dispatch_event(event_type='build-docs')

GraphQL Interface (V4)

This is a thin wrapper for Github’s GraphQL API (V4).

This module does not build the query for you. This is because the possibilities afforded by GraphQL are large and it makes no sense to re-invent them. The easiest way to assemble a new query is to use Github’s GraphQL Explorer. For example, to get the homepage URL of a repository:

>>> from skare3_tools.github import graphql
>>> query = """{
...   repository(name: "test-actions", owner: "sot") {
...     homepageUrl
...     id
...   }
... }"""
>>> response = graphql.GITHUB_API(query)
>>> response
{'data': {'repository': {'homepageUrl': None,
'id': 'MDEwOlJlcG9zaXRvcnkyMDkwMjE1NDQ='}}}

and to set the homepage URL of a repository:

>>> from skare3_tools.github import graphql
>>> query = """mutation {
...   updateRepository(input: {repositoryId: "MDEwOlJlcG9zaXRvcnkyMDkwMjE1NDQ=",
...                             homepageUrl: "https://github.com/sot/test-actions"})
...   {
...     repository {
...       id
...       homepageUrl
...     }
...   }
... }
... """
>>> response = graphql.GITHUB_API(query)
>>> response
{'data': {'updateRepository': {'repository': {'id': 'MDEwOlJlcG9zaXRvcnkyMDkwMjE1NDQ=',
'homepageUrl': 'https://github.com/sot/test-actions'}}}}

Given the flexibility of the GraphQL interface, this module includes a small collection of common queries. Each query in the collection is a string that should be used as a jinja2 template. For example, to get a list all pull requests of a repository:

>>> import jinja2
>>> from skare3_tools.github import graphql
>>> query = jinja2.Template(graphql.REPO_PR_QUERY).render(owner='sot', name='Quaternion')
>>> response = graphql.GITHUB_API(query)
>>> response['data']['repository']['pullRequests']['nodes'][0]
{'baseRefName': 'master',
 'headRefName': 'modernize',
 'title': 'Add delta quaternion method and other package modernization',
 'number': 2,
 'state': 'MERGED'}

THere is a (possibly incomplete) list of queries (with the template parameters in parentheses):

  • REPO_ISSUES_QUERY(name, owner, label)

  • REPO_PR_QUERY(name, owner)

  • ORG_QUERY(owner)

  • REPO_QUERY(owner, name)

Github Scripts

Some of these scripts are superseded by Github’s own CLI, while some provide functionality specific to Ska. At the very least, the exemplify the usage of the API.

skare3-create-issue

Create a github issue

usage: skare3-create-issue [-h] --repository REPOSITORY
                           [--latest-release LATEST_RELEASE] [--title TITLE]
                           [--body BODY] [--label LABEL] [--token TOKEN]

Named Arguments

--repository

repository name. Example: sot/chandra_aca

--latest-release

Repository name (owner/repo). The latest release of this repodetermines the title and body of issue. Title/body are ignored.

--title

Issue Title. Ignored if –latest-release is given

--body

Issue Description. Ignored if –latest-release is given

--label

Default: []

--token, -t

Github token, or name of file that contains token

skare3-create-pr

Create a pull request

usage: skare3-create-pr [-h] --repository REPOSITORY --title TITLE --head HEAD
                        --base BASE --body BODY [--token TOKEN]

Named Arguments

--repository

repository name. Example: sot/chandra_aca

--title
--head

branch you are merging from

--base

branch you are merging to

--body
--token, -t

Github token, or name of file that contains token

skare3-merge-pr

Create a pull request

usage: __main__.py [-h] --repository REPOSITORY [--pull-number PULL_NUMBER]
                   [--head HEAD] [--base BASE] [--commit-title COMMIT_TITLE]
                   [--commit-message COMMIT_MESSAGE] [--sha SHA]
                   [--merge-method MERGE_METHOD] [--token TOKEN]

Named Arguments

--repository

repository name. Example: sot/chandra_aca

--pull-number

pull number

--head

branch you are merging from

--base

branch you are merging to

--commit-title

Optional. Filled from PR.

--commit-message

Optional. Filled from PR.

--sha

SHA that pull request head must match to allow merge.

--merge-method

Merge method to use. Possible values are merge, squash or rebase.

--token, -t

Github token, or name of file that contains token

skare3-release-merge-info

skare3-add-secrets

A script to add encrypted secrets to Github repositories.

At some point, Github’s API did not have an endpoint for setting secrets. This script circumvented this limitation by using Selenium with Python together with ChromeDriver <https://sites.google.com/a/chromium.org/chromedriver>. While this is not strictly needed anymore, it is left because it is an example on how to deal with cases like this.

To use it, you need to have Chrome installed, install Selenium, and you need to get a version of ChromeDriver matching your Chrome version. I suppose it could work with other browsers but I have not tried it.

A valid YAML file looks like this:

SECRET_NAME: the secret value

SECRET_TWO: |

another value asdf

usage: skare3-add-secrets [-h] [--secrets SECRETS] --user USER [--no-quit]
                          repositories [repositories ...]

Positional Arguments

repositories

Named Arguments

--secrets

JSON file with all secrets

Default: “secrets.json”

--user

Github user name

--no-quit

Do not close chrome browser at the end

Default: True