Mritunjay Sharma

moon indicating dark mode
sun indicating light mode

How to automate code formatting for Python projects with GitHub Actions - A study

September 10, 2020

This is a beginner-friendly 💯 post that will tell you how easy it will become to conform to Python coding styles using autoyapf GitHub Action in your Python projects!

Introduction

To begin with - let's discuss first what are GitHub Actions and what magic they bring a 😃?

GitHub Actions

GitHub Actions is that piece of magic which enables you to automate, customize, and execute your software development workflows right in your repository.

But hey? What are words without actions? And so what are actions without workflows? Yes, you got it right! For GitHub Actions to work you need to use Workflows but what are Workflows? A workflow is a configurable automated process made up of one or more jobs. Now, these jobs can be like the accounting of the trigger for the GitHub action to work - like push on the main branch.

Workflow files use YAML syntax and must have either a .yml or .yaml file extension.

For GitHub Actions to work you must store workflow files in the .github/workflows directory of your repository.

Brief Introduction to building a GitHub Action

Note: In order to use an existing GitHub action all you require is just a Workflow YAML file in .github/workflows. The files mentioned in this part of the blog are required when you start building your own GitHub Actions 😄

In order to make almost any GitHub Action - you require at least these files:

  • Dockerfile — GitHub actions are of three types - Docker actions, Javascript actions and composite run steps action. In our case- we are going to use Docker Action and so Dockerfile is used to define the container environment that runs the action.

  • action.yml — This will define inputs, outputs, Entrypoint for our action, and other details like name of the action, description, author name, etc.

  • script — An action might require a script to perform a task, in our case we just need an Entrypoint script named as entrypoint.sh

  • README.md — This is optional, but if we want to publish our action on the Marketplace we need it. README.md file is useful to provide examples that are needed for the action to run without any hassles.

This is the basic skeleton of how the GitHub actions are built. If you want to start with making your own first action, check out this official Hello World tutorial

Let's move on to the main part of preparing and setting up the workflow for the autoyapf GitHub Action we are going to use.

What is autoyapf?

autoyapf is a GitHub action for yapf, an open-source tool that automatically formats Python code to conform to the PEP 8 style guide. It is in essence, the algorithm that takes the code and reformats it to the best formatting that conforms to the style guide, even if the original code didn't violate the style guide.

This action is designed to be used in conjunction with the 'push' trigger. In simple words - everytime the maintainer pushes the code from their own playground or merges a Pull Request from a contributor it will be formatted automatically with the PEP 8 Style guidelines using the yapf tool. This action is a win-win situation for both contributors and maintainers of projects involving Python!

Choosing the Project

The first step is always choosing the repository where you will be using the GitHub action. Since the GitHub action is made for python projects, I am going to use an open-source Python project that I created recently called sedpy

Preparing the Workflow

  • We'll go to our repository and click on the Actions tab. That will then reflect an option like set up a workflow yourself. Click it.

This will create a YAML file in the path: .github/workflows/main.yml

You can rename the file to whatever you prefer like, in this case, I will give it the name autoyapf.yml instead of main.yml.

Now, let's dive in the autoyapf.yml file:

name: Formatting python code
on:
push:
paths:
- '**.py'

What we have done until here is:

  • Given a name to the workflow: Formatting python code
  • Defined what event will cause the workflow to trigger. In this case, it is triggered when the python file is pushed in the repository.

Next step is to define the jobs, further, we added:

jobs:
autoyapf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
with:
ref: ${{ github.head_ref }}
- name: autoyapf
id: autoyapf
uses: mritunjaysharma394/autoyapf@v2
with:
args: --style pep8 --recursive --in-place .
- name: Check for modified files
id: git-check
run: echo ::set-output name=modified::$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)
- name: Push changes
if: steps.git-check.outputs.modified == 'true'
run: |
git config --global user.name 'Mritunjay Sharma'
git config --global user.email 'mritunjaysharma394@users.noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git commit -am "Automated autoyapf fixes"
git push

Here's a brief detail about almost everything the above snippet does:

  • The jobs that the workflow has to perform will run on the GitHub hosted runner which is ubuntu-latest in our case.
jobs:
autoyapf:
runs-on: ubuntu-latest
  • We define the steps of jobs next.
    • The first step checks out the repository where we want to workflow to execute. Since I am using it inside sedpy, it will check out this repository's master branch
steps:
- uses: actions/checkout@master
with:
ref: ${{ github.head_ref }}
  • In the next step - we checked out the GitHub action autoyapf which will perform all the formatting using yapf tool with the arguments supplied as: args: --style pep8 --recursive --in-place .
- name: autoyapf
id: autoyapf
uses: mritunjaysharma394/autoyapf@v2
with:
args: --style pep8 --recursive --in-place .
  • This step checks if the above step has made any changes in the code. If it has made any changes then the next step will push the changes to the sedpy repository.
- name: Check for modified files
id: git-check
run: echo ::set-output name=modified::$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)
  • This will check the condition as detailed above and we will push the changes using run utility that makes us use terminal-like commands in the .yml files.
- name: Push changes
if: steps.git-check.outputs.modified == 'true'
run: |
git config --global user.name 'Mritunjay Sharma'
git config --global user.email 'mritunjaysharma394@users.noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git commit -am "Automated autoyapf fixes"
git push

Woohoo! 💃 We have completed our workflow autoyapf.yml and the complete file looks something like this:

name: Formatting python code
on:
push:
paths:
- '**.py'
jobs:
autoyapf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
with:
ref: ${{ github.head_ref }}
- name: autoyapf
id: autoyapf
uses: mritunjaysharma394/autoyapf@v2
with:
args: --style pep8 --recursive --in-place .
- name: Check for modified files
id: git-check
run: echo ::set-output name=modified::$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)
- name: Push changes
if: steps.git-check.outputs.modified == 'true'
run: |
git config --global user.name 'Mritunjay Sharma'
git config --global user.email 'mritunjaysharma394@users.noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git commit -am "Automated autoyapf fixes"
git push

Words by Mritunjay Sharma.
Junior Year Computer Science Engineering Student| Based in Lucknow, India | Sometimes writing code and sometimes poems | Chaotic and Poetic | 20 y.o | Connect with me on Twitter :)