Automate Your Boring GitHub Repository With Python And Bash Script

Are you recently developing many hobby projects? And you are getting bored by creating GitHub repositories again and again? Yeah I know it’s kind of boring. But good news! We are going to use GitHub APIs with Python. So you don’t have to do the initial repeated tasks ever again. But let’s see, what we actually have to do when we will do that manually. First, we need to set up a project. It can be a PHP project or Python. It doesn’t matter. Then we had to initiate a git repository and commit the initial changes locally. After that, we used to visit our Github profile to create a repository. Then in local, we set the origin and push to master. Look, we should all have some tools or script which will be handy in this kind of case. Life gets easier. So let’s start.

Prerequisite

  • Python3
  • pip
  • venv
  • Github account
  • Github access token
  • requests~=2.23.0
  • python-dotenv~=0.13.0

Objectives

  1. Create a repository in GitHub
  2. Push local changes to the master branch
  3. Stay home and stay safe

Project folder and file structure

.
├── github
│   ├── __init__.py
│   ├── run.py
│   └── services.py
├── README.md
├── requirements.txt
├── settings.py
├── shell_scripts
│   ├── github_push.sh
└── utils
├── helpers.py
└── __init__.py
└──.env

Adding Shell Scripts

Now to add remote origin we need to add the below code in shell_scripts/github_push.sh.

#! /bin/bash

set -e

# shellcheck disable=SC2164
cd
cd "$1"
git remote add origin "$2"
git push origin master

Config .env file

Now we need to add some credentials in our .env file.

GITHUB_ACCESS_TOKEN={Your github access token}
GITHUB_USERNAME={Your github username}

To get GitHub access token visit here.

Config settings.py

Now let’s add some constants to use in our projects inside the settings.py file.

import os
from dotenv import load_dotenv


load_dotenv()

GITHUB_ACCESS_TOKEN = os.getenv("GITHUB_ACCESS_TOKEN")
GITHUB_USERNAME = os.getenv("GITHUB_USERNAME")

Helper methods

Now, let’s create some helper method to get a shell script from a directory and building a remote origin URL. To do that we will write those methods in utils/helpers.py file.

import os
from settings import GITHUB_USERNAME


def get_shell_script(file_name: str) -> dict:
    """
    Get shell script from shell_scripts folder
    :param file_name:
    :return:
    """
    try:
        # Get the root directory
        root_dir = os.path.dirname(
            os.path.realpath(__file__)).rsplit(os.sep, 1)[0]
        # Get the shell script, which will create the venv
        shell_script = os.path.join(root_dir, "shell_scripts", file_name)

        return dict(success=True, script=shell_script)
    except Exception as e:

        return dict(success=False, message=str(e))


def github_repo_remote_origin(repo_name: str) -> str:
    """

    :param repo_name:
    :return:
    """
    return "git@github.com:"+GITHUB_USERNAME+'/'+repo_name+'.git'

Developing a service for Github Automation

Let’s build a separate service for all the actions. But first, import all the necessary things.

import subprocess
import requests
from settings import GITHUB_ACCESS_TOKEN, GITHUB_USERNAME
from utils.helpers import get_shell_script

Now let’s create a class with its properties.

class GithubService:
    """
        Github service
    """

    __github_push_shell_script: str = "github_push.sh"
    __repo_creation_end_point: str = "https://api.github.com/user/repos"
    __headers = {
        'Authorization': 'token ' + GITHUB_ACCESS_TOKEN
    }

Let’s write an __init__  method with two arguments, one is repo_name and another is git_directory.

def __init__(self, repo_name, git_directory):
    """

    :param repo_name:
    :param git_directory:
    """
    self.repo_name = repo_name
    self.git_directory = git_directory

Now let’s write a method to get remote origin.

def __github_repo_remote_origin(self):
    """
    
    :return:
    """
    return "git@github.com:"+GITHUB_USERNAME+'/'+self.repo_name+'.git'

We need a method to create a Github repository. To do that we will need the help of python request. We will make a post request in __repo_creation_end_point with a repo name and it will create a repository in Github for us. Check out the PI documentation I have added below and you will find something like that in the repository section.

” alt=”” width=”630″ height=”630″ data-lazyloaded=”1″ data-placeholder-resp=”630×630″ data-src=”https://toptechytips.com/wp-content/uploads/2020/06/Screenshot-from-2020-06-17-22-01-04-300×300.png” data-srcset=”https://toptechytips.com/wp-content/uploads/2020/06/Screenshot-from-2020-06-17-22-01-04-300×300.png 300w, https://toptechytips.com/wp-content/uploads/2020/06/Screenshot-from-2020-06-17-22-01-04-150×150.png 150w” data-sizes=”(max-width: 630px) 100vw, 630px” />

def create_repo(self) -> dict:
    """

    :return:
    """
    try:
        with requests.Session() as s:
            s.headers.update(self.__headers)
            resp = s.post(self.__repo_creation_end_point, json={
                "name": self.repo_name
            })
            print(resp.content.decode())

            return dict(success=True,
                        message="Github repository has been created")
    except Exception as e:

        return dict(success=False, message=str(e))

Now we need to push our changes in Github right? To do that we need to add remote origin and push changes there. We will get our shell script first. Then we will run it in a subprocess.

def git_add_remote_origin_and_push_master(self):
    """
    Will add remote origin and push changes to github repo
    :return:
    """
    try:
        shell_script = get_shell_script(self.__github_push_shell_script)
        if not shell_script["success"]:
            return dict(success=False, message="Shell script not found")
        # execute and get the output of the shell script
        output = subprocess.check_output([
            shell_script["script"],
            str(self.git_directory),
            str(self.__github_repo_remote_origin()),
        ])
        print(output.decode())

        return dict(success=True, message="Git push successfull")
    except Exception as e:

        print(e)
        return dict(success=False, message="Git push failed",
                    error_message=str(e))

To run the whole process we need to write the below code in the github/run.py file.

from github.services import GithubService


def create_repo_and_push_local_changes(repo_name: str, git_directory: str) -> 
        dict:
    github_service = GithubService(repo_name, git_directory)
    repository_api_response = github_service.create_repo()
    if not repository_api_response["success"]:
        return repository_api_response
    push_response = github_service.git_add_remote_origin_and_push_master()
    if not push_response["success"]:
        return push_response

    return dict(success=True, message=dict(
        repository_api_response=repository_api_response["message"],
        push_response=push_response["message"],
    ))

If you check your Github you will find your repository there. One thing you have noticed that We have run shell scripts in the subprocess. The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions. In Linux, a subprocess is a child process, i.e. a process that has been launched by its parent to which it is a subprocess. An interactive shell is a command-line interpreter reading your input line by line and prompting you when a command is done.

Well, let me tell you about this tool. I have developed a tool to automate python projects. It will create a project in venv and do all the git and Github stuffs for me. I have shown you examples of Github API using python. We will discover more about the tool in the future. Till then write a tool for automating Github. You can do many actions with Github APIs. I have gave you all the thing you will need to know.

Happy coding!

Original tool Github repository: https://github.com/zim0101/jervis_the_bot 

Python subprocess module: https://docs.python.org/3/library/subprocess.html

Github API documentation: https://developer.github.com/v3/