Github Actions with ChatOps to write Beautiful Python Code

Igor Zhivilo
Israeli Tech Radar
Published in
6 min readApr 15, 2023

Hi, I’m DevOps Engineer at Tikal Knowledge.

In this tutorial, I will show how to:

  1. Trigger Github Actions Workflow using PR comments like ‘/format’ (ChatOps). I will use slash-command-dispatch for this.

2. Format the python code with PEP8 as part of PR and re-commit it again.

Github Actions Published guides:

PEP 8 — Style Guide for Python Code

https://peps.python.org/pep-0008/

This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.

ChatOps

“ChatOps” is a term widely credited to GitHub, referring to the practice of performing operations by typing commands in chat messaging applications.

While GitHub Actions has many ways to trigger workflows based on events that occur in a repository, it doesn’t have a particularly straightforward way to manually trigger a workflow.

Slash Command Dispatch

A GitHub action that facilitates “ChatOps” by creating dispatch events for slash commands.

How does it work?

The action runs in issue_comment event workflows and checks the first line of comments for slash commands. When a valid command is found it creates a repository dispatch event that includes a payload containing full details of the command and its context. It also supports creating workflow dispatch events with defined input parameters.

Let’s start

I will start from the first part.

Trigger Github Actions Workflow using PR comments like ‘/format’ (ChatOps). I will use slash-command-dispatch for this.

Why create dispatch events?

“ChatOps” with slash commands can work in a basic way by parsing the commands during issue_comment events and immediately processing the command. In repositories with a lot of activity, the workflow queue will get backed up very quickly trying to handle new issue_comment events and process the commands themselves.

Dispatching commands to be processed elsewhere keeps the workflow queue moving quickly. It essentially enables parallel processing of workflows.

An additional benefit of dispatching is that it allows non-sensitive workloads to be run in public repositories to save using private repository GitHub Action minutes.

Read more: https://github.com/peter-evans/slash-command-dispatch

Adding ‘Slash Command Dispatch’ workflow.

.github/workflows/sh-dispatcher.yaml

name: Slash Command Dispatch
on:
issue_comment:
types: [created]
jobs:
slashCommandDispatch:
runs-on: ubuntu-latest
steps:
- name: Slash Command Dispatch
uses: peter-evans/slash-command-dispatch@v3
with:
token: ${{ secrets.GH_PAT }}
commands: |
format
reactions: eyes

For ‘Slash Command Dispatch’ I used ‘secrets.GH_PAT’, I explained in previous posts: https://github.com/warolv/github-actions-series/blob/master/scale-runners.md how to generate PAT, please read it.

After PAT generated, need to add it as a secret (GH_PAT) of your GitHub repo.

Go to repo’s settings -> ‘Secrets and variables’ -> ‘actions’ -> ‘New repository secret’.

And the command itself:

.github/workflows/format-command.yml

name: format
on:
repository_dispatch:
types: [format-command]
jobs:
format:
runs-on: ubuntu-latest
steps:
- name: Run format command
run: |
echo 'format command executed!'

Verifying ‘format’ command is executed on PR’s comment

I will verify first that ‘/format’ command triggered on PR’s comments and ‘format command executed!’ being seen in the console.

For verification need to create the PR, in my example:

git checkout -b test-chatops-commands

Do some change, I changed output for echo in my case,

from ‘echo ‘format command executed!’’ to ‘echo ‘Format command executed!’’

git commit -am “Push changes”

Open PR:

Now add comment ‘/format’.

You can see ‘format command executed!’ , so it works :-) !

Next step will be to use ‘black’ tool for the command.

You can find example of usage here.

Using ‘black’ to format python code

https://pypi.org/project/black/

Black is the uncompromising Python code formatter. By using it, you agree to cede control over minutiae of hand-formatting. In return, Black gives you speed, determinism, and freedom from pycodestyle nagging about formatting. You will save time and mental energy for more important matters.

To use ‘black’ I will rewrite command workflow:

.github/workflows/format-command.yaml

name: format-command
on:
repository_dispatch:
types: [format-command]
jobs:
format:
runs-on: ubuntu-latest
steps:
# Checkout the pull request branch
- uses: actions/checkout@v3
with:
token: ${{ secrets.GH_PAT }}
repository: ${{ github.event.client_payload.pull_request.head.repo.full_name }}
ref: ${{ github.event.client_payload.pull_request.head.ref }}

# Setup Python environment
- uses: actions/setup-python@v3

# Install black
- name: Install black
run: pip install black

# Execute black and commit the change to the PR branch
- name: Commit to the PR branch
run: |
black .
git config --global user.name 'actions-bot'
git config --global user.email '58130806+actions-bot@users.noreply.github.com'
git commit -am "[black-command] fixes"
git push

Now I will create another PR to test ‘/format’ workflow.

As part of PR I added python code to test ‘black’ with this code:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# lambdadelete.py
# It is an example that handles Lambda functions on AWS.
# It uses Client API (low-level) of Boto3.
# Delete a Lambda function.
# You must provide 1 parameter:
# FUNCTION_NAME = Lambda function name


import sys
import boto3
import botocore

def main():

REGION= 'eu-west-1' # AWS region

# Make a list of command line arguments, omitting the [0] element
# which is the script itself.
args= sys.argv[1:]
if len(args) < 1:
print('Not enough parameters.\n'\
'Proper Usage is: python lambdadelete.py <FUNCTION_NAME>')
sys.exit(1)

function_name= args[0]
print('Lambda function name: ' + function_name)

# Create a Lambda Client
lambda_client = boto3.client('lambda', region_name=REGION)

# Delete Lambda function
try:
print('Deleting function ...')

response= lambda_client.delete_function(
FunctionName=function_name)
print('Response:')
print(response)
print('\nDeleted')

except botocore.exceptions.ClientError as e:
if e.response['Error']['Code'] == "ResourceNotFoundException":
print("Error: Function Not Found!!")
elif e.response['Error']['Code'] == "AccessDeniedException":
print("Error: Access Denied!!")
elif e.response['Error']['Code'] == "ValidationException":
print("Error: Name Not Valid!!")
else:
raise

return


# This is the standard boilerplate that calls the main() function.
if __name__ == '__main__':
main()

You can see above new commit was pushed to this PR.

It working great as you can see:-)

You can find all the code | workflows and python example code in my repo:

git@github.com:warolv/github-actions-series.git
cd format-command

Conclusion

In this tutorial, I explained how to trigger Github Actions workflow using PR comments and format the python code with PEP8.

Thank you for reading, I hope you enjoyed it, see you in the next post.

If you want to be notified when the next post of this tutorial is published, please follow me here on medium, Twitter (@warolv) and my YT channel.

--

--