This post describes how I made the GitHub integration of my GSoC project. The main motive of this feature was, when the user is done editing the XML document, he can submit his changes through a pull request in the license-xml
repository with just a click of a button.
This post will be helpful if you want to learn about:
Since the pull request had to be made on behalf of the user, that’s why I choose to go with the GitHub OAuth App instead of general GitHub Apps. When the user registers using these OAuth apps, GitHub provide us with a unique access token, using which we access the API on behalf of that user and make the Pull Request. A simpler way of doing this was to make a bot account, generate API access token for it and use it to make all the pull requests but that could be very easily spammed and there would be no way to know who actually made the pull request.
The OAuth feature can be added to a Django website using the social-auth-app-django
package. This is very easy to use, supports both Python 2 and 3 and also has a good documentation. Install it using pip install social-auth-app-django
. In the settings.py
file, add this package to the INSTALLED_APPS
and also update the configurations such as MIDDLEWARE_CLASSES
, context_processors
, AUTHENTICATION_BACKENDS
. Migrate the database to add all the tables of this package. For a descriptive info about the configs refer to the docs
After this go to your GitHub settings and from the Developer Settings, make a new OAuth App. Fill up all the required entries. The callback URL is the address where the app will redirect after the user is done with the authentication. If you want for testing purpose use http://localhost:8000/oauth/complete/github
else replace the localhost with your site’s domain name (can be changed later). Once your app is made, copy the Client ID
and Client Secret
and assign them to SOCIAL_AUTH_GITHUB_KEY
and SOCIAL_AUTH_GITHUB_SECRET
variable in settings.py
file.
By default the OAuth App only gives access to user’s info, if we want more access then we can request access permission from users by defining scopes for our GitHub App. For making the pull request we would need access to the users repos so add the SOCIAL_AUTH_GITHUB_SCOPE
variable to settings.py
file and set it to ['public_repo']
.
Your basic GitHub OAuth is ready, you can add more features to the authentication process by using Pipelines. Pipelines allow us to add our custom functions in the default authentication process of the social-auth-app-django
. In my case I wanted to store user’s info in one more table in the database, so made a function which did that and then added it to the pipeline.
The requests
package can be used to make different type of API requests to the GitHub API. All the requests must include the following headers, where token is the user token that is generated after authentication with OAuth App:
{
"Accept":"application/vnd.github.machine-man-preview+json",
"Authorization":"bearer "+token,
"Content-Type":"application/json",
}
To make the pull request I followed the following workflow:
SHA
of the upstream/master using the get
request at https://api.github.com/repos/:owner/:repo/git/refs/head/master
and then send a patch
request at https://api.github.com/repos/:username/:repo/git/refs/head/master
with the SHA
included in the request body. Again send a get
request at https://api.github.com/repos/:username/:repo/git/refs/head/master
and store the SHA
in a variable as this will be used later.post
request at https://api.github.com/repos/:username/:repo/git/refs
with the following body (SHA
is the one that we stored above):
{
"ref":"refs/heads/"+branchName,
"sha":sha,
}
From the response of this request copy the SHA
of the new branch created.
The way I integrated this feature into the XML Editor, was by first authenticating the user to the GitHub App, then providing him with a form which included Branch Name
, File Name
,Commit Message
, Pull Request Title
, Pull Request Body
. After the user has filled valid info in the form then sending an Ajax Request to a Django View which would handel the pull request process and return the results (error or success). While making the pull request I also checked if the given branch name exists, if so then appending some random text to the name. Also I checked if the given file name already exists, if it exists then use the Update File API else use the Create File API.
You can have a look at the project here. Please post your doubts and questions in the comments below.