When you are working on your personal or work projects in Python, you usually want to have a way to enforce code standards. You can use tools like Flake8, PyLint or Ruff to lint your code. You might use Mypy to verify type checking. There are lots of other tools at your disposal. But it can be hard to remember to do that every time you want to create a pull request (PR) in GitHub or GitLab.
That is where continuous integration (CI) tools come in. GitHub has something called GitHub Actions that allow you to run tools or entire workflows on certain types of events.
In this article, you will learn how to create a GitHub Action that runs Ruff on a PR. You can learn more about Ruff in my introductory article.
Creating a Workflow
In the root folder of your code, you will need to create a folder called .github/workflows. Note the period at the beginning and the fact that you need a subfolder named workflows too. Inside of that, you will create a YAML file.
Since you are going to run Ruff to lint and format your Python files, you can call this YAML file ruff.yml. Put the following in your new file:
name: Ruff on: [workflow_dispatch, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Python uses: actions/setup-python@v4 with: python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip pip install ruff # Include `--format=github` to enable automatic inline annotations. - name: Run Ruff run: ruff check --output-format=github . continue-on-error: false - name: Run Ruff format run: ruff format --check . continue-on-error: false
Note: This example comes from my textual-cogs repo
Let’s talk about what this does. This line is pretty important:
on: [workflow_dispatch, pull_request]
It tells GitHub to run this workflow when there’s a pull request and also with “workflow_dispatch”. That second option lets you go to the Actions tab in your GitHub repo, select the workflow and run it manually. If you do not include that, you cannot run it manually at all. This is useful for testing purposes, but you can remove it if you do not need it.
The next part tells GitHub to run the build on ubuntu-linux:
jobs: build: runs-on: ubuntu-latest
If you have a GitHub subscription, you can also get Windows as a runner, which means that you can also run these actions on Windows in addition to Linux.
The steps section is the meat of this particular workflow:
steps: - uses: actions/checkout@v4 - name: Install Python uses: actions/setup-python@v4 with: python-version: "3.13" - name: Install dependencies run: | python -m pip install --upgrade pip pip install ruff # Include `--format=github` to enable automatic inline annotations. - name: Run Ruff run: ruff check --output-format=github . continue-on-error: false - name: Run Ruff format run: ruff format --check . continue-on-error: false
Here is uses a built-in checkout@v4 workflow, which is something that comes with GitHub. There are many others you can use to enhance your workflows and add new functionality. Next, you get setup with Python 3.13 and then you install your dependencies.
Once your dependencies are installed, you can run your tools. In this case, you are installing and running Ruff. For every PR, you run ruff check --output-format=github .
, which will do all sorts of linting on your Python code. If any errors are found, it will add inline comments with the error, which is what that --output-format
flag is for.
You also have a separate section to run Ruff format, which will format your code to follow the Black formatter (for the most part).
Wrapping Up
You can add lots of other tools to your workflows too. For example, you might add a Mypy workflow, or some test workflows to run on PR or perhaps before merging to your main branch. You might even want to check your code complexity before allowing a merge too!
With a little work, you will soon be able to use GitHub Actions to keep your code cleaner and make it more uniform too. Adding automated testing is even better! Give it a try and let me know what you think.