# Implementing Github actions in your Python project

### Introduction

Automation is the key to streamlining your work processes for any software development project. GitHub actions allow you to add your software development lifecycle workflows directly to your repository.

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository or deploy merged pull requests to production.

All in all, it's a great tool for integrating your project with CI/CD pipeline. And it's great to know how to integrate it into your project.

In this article, we will implement a GitHub workflow in a python project and see it in action.

### Create a simple python project

Let's create a simple python-project by running the following commands.

```bash
mkdir simple-calculator
cd simple-calculator
```

Let's activate the virtual environment in your project.

```bash
python3 -m venv .env
source .env/bin/activate
```

Add a file calculator.py to your project and add the following content to it

```python

class Calculator(object):
    def __init__(self, *args):
        super(Calculator, self).__init__(*args)

    def addition(self, a, b):
        return a + b

    def substraction(self, a, b):
        return a - b

    def multiplication(self, a, b):
        return a * b

    def division(self, a, b):
        return a / b
```

### Add tests to your project

Now we need to add tests for four projects.

We first need to install **pytest** in your project.

```bash
pip install pytest
```

Now Let's add tests for our `calculator.py` methods. We can add a directory called `tests` and inside the directory create one file called `test_calculator.py` .

We also need to add an \_\_init\_\_.py inside the `tests` directory to run the tests successfully.

We can add the following content in our `test_calculator.py` file.

```python

import pytest
from calculator import Calculator


@pytest.fixture
def calculator():
    return Calculator()


class TestClass:

    def test_addition(self, calculator):
        assert 7 == calculator.addition(3, 4)

    def test_substract(self, calculator):
        assert -1 == calculator.substraction(3, 4)

    def test_multiplication(self, calculator):
        assert 12 == calculator.multiplication(3, 4)

    def test_division(self, calculator):
        assert 2 == calculator.division(4, 2)
```

Now if you run `pytest` command on your project directory you should see all the testcases are passed.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1672991424644/fae4349d-3877-4196-961f-8ad9373986f5.png align="center")

### Add flake8 to it for linting

Flake8 is a popular lint wrapper for python. We will use **flake8** in our project for linting.

If you want to have more knowledge about integrating **flake8** in your python project, please visit this article - [https://techblog.akashojha.com/invoke-flake8-in-python-project](https://techblog.akashojha.com/invoke-flake8-in-python-project).

Let's install flake8 in our project

```bash
pip install flake8
```

Now Let's run flake8 against our project once.

```bash
flake8 . --count --max-complexity=10 --max-line-length=127 --statistics
```

If you don't have any errors, you should have an output `0`. Otherwise, if you're getting some errors please try to resolve this first by going through the warning logs.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1672992445883/9f19afe1-c811-4ec7-9590-c5c70ea6ed9b.png align="center")

For example, You can see in the above image that I had 1 error when I first ran the `flake8` command in my terminal. Then I resolved it and got 0 errors.

### Add github-worflow to your project

Now that your basic project is set up let's dive into the implementation of github-workflows for your project.

Let's create the `.github/workflows` directory.

```bash
mkdir -p .github/workflows
```

This is the location where all your workflows will be added.

Let's create our first workflow inside this directory. We need to add the workflow in a YAML file. So, Let's create `linting-testing.yaml` file and let's add the following content to that file.

```yaml
name: simple-calculator-linter-testing workflow

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.8", "3.9", "3.10"]

    steps:
      - uses: actions/checkout@v3
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install flake8 pytest
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
      - name: Lint with flake8
        run: |
          # stop the build if there are Python syntax errors or undefined names
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
      - name: Test with pytest
        run: |
          pytest
```

Let's go over the syntax details of our workflow-YAML file one by one.

| Syntax keys | Description |
| --- | --- |
| name | You can add a suitable name for your workflow here. |
| on | Here you need to define which events you will trigger the workflow. In the above scenario, it will trigger whenever someone pushes any changes to the repository. |
| jobs | A set of tasks sequentially defined under `simple-calculator-linter-testing workflow` |
| build | It's the name of our first and only job |
| runs-on | Configures the job to run on the latest version of an Ubuntu Linux runner. This means that the job will execute on a fresh `ubuntu-latest` virtual machine hosted by GitHub. |
| strategy | `strategy` is used for matrix strategy for your jobs. A matrix strategy lets you use variables in a single job definition to automatically create multiple job runs that are based on the combinations of the variables. |
| matrix | `strategy.matrix` is to define a matrix of different job configurations. In the above example, the above matrix defines a variable `python-version` with the values`["3.8", "3.9", "3.10"]` .  
In this example, the workflow will run 3 jobs, one for each `python-version` variables. Each job will access the `python-version` value through the `matrix.python-version` context and pass the value as `python-version` to the `actions/setup-python@v4` action. |
| steps | Groups together all the steps that run in the `build` job. Each item nested under this section is a separate action or shell script. |
| steps.name | The `name` keyword under `steps` is just for defining the name of that step. |
| uses | The `uses` keyword specifies that this step will run `v3` of the `actions/checkout@v3` action. This is an action that checks out your repository onto the runner, allowing you to run scripts or other actions against your code (such as build and test tools). You should use the checkout action any time your workflow will run against the repository's code.  
In the next section of `uses` keyword, the `actions/setup-python@v4` action to install the specified versions of `Python`. This puts a specific version of Python preinstalled in our path. |
| with.python-version | Here the value should be the specific version of Python. In this scenario, a total of 3 jobs will run with python versions - 3.8, 3.9, 3.10 as defined under the `strategy.matrix` . |
| run | The `run` keyword tells the job to execute a command on the runner. In this case, you are using `python -m pip install --upgrade pip` to install pip and using `pip` to install `flake8 pytest.` In the next section `run` keyword, we are using `flake8` and `pytest` for checking linting errors and testing respectively. |

I hope I'm able to give you the overall context of the workflow file. Please visit the official documentation link to get a detailed understanding of the workflow - [https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions)

### Add .gitignore and requirements.txt

Let's create a `.gitignore` file and the content of this link into the file - [https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore](https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore)

And then create a requirements.txt file and add the python package dependencies into that file by running this command.

```bash
pip freeze > requirements.txt
```

### Push your project to github

Now that your project is set up, we need to push your code into a GitHub repository to see GitHub actions running a CI pipeline.

You need to have a GitHub account beforehand. If you don't have an account, please create one by signing up through the link - [https://github.com/signup](https://github.com/signup).

Let's create a repository called `simple-calculator` in your GitHub account.

And then run the following commands in the terminal inside your project directory path. Please replace the &lt;username&gt; with your **GitHub username**.

```bash
echo "# simple-calculator" >> README.md
git init
git add -A
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:<username>/simple-calculator.git
git push -u origin main
```

Now if you open your project and go into the **Actions** section, you should see all your jobs should be successful.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673029510097/35268e7f-ee68-4bf3-a7a9-61656568ae3a.png align="center")

As you can see in the above image, 3 jobs have run successfully for 3 python versions(3.8, 3.9, 3.10). You should also be able to see something similar in your project.

### Conclusion

We are able to implement GitHub action in our simple python project. Hopefully, you will be using GitHub actions in your next project.

Github link for this project - [https://github.com/ojhakash/simple-calculator](https://github.com/ojhakash/simple-calculator)

Happy Learning!!
