DEV Community

Angel Oduro-Temeng Twumasi
Angel Oduro-Temeng Twumasi

Posted on • Edited on

How to Set Up GitHub Actions for Continuous Integration

Introduction

In the fast-paced world of software development, ensuring that code is consistently tested and integrated can be a challenging task. This is where Continuous Integration (CI) comes to play.

CI is a development practice that requires developers to integrate code into a shared repository frequently.

CI automates the process of building, testing and integrating code changes regularly, helping developers to catch issues early and maintain a stable codebase.

GitHub Actions, a powerful feature introduced by GitHub, has revolutionized the way developers automate their workflows. We will explore how to use this tool for setting up CI in your projects.

Here's what we will cover

  • Understanding CI and it's benefits
  • Introduction to GitHub Actions
  • Building CI workflows with Github Actions
  • Advanced Techniques and Integration with External Tools

Benefits of Continuous Integration

There are several benefits to Continuous Integration. These include

  • Early Detection of Bugs
    Continuous Integration facilitates the early detection of bugs by automatically running tests with each code integration, allowing developers to identify and fix issues before they escalate, resulting in more stable software releases.

  • Improved Code Quality
    By ensuring consistent coding standards and practices through automated testing and integration, code quality is checked. This reduces technical debt and ensures the maintainability of the codebase

  • Faster Feedback Loop
    With Continuous Integration, developers receive rapid feedback on their code, enabling them to iterate quickly and address issues promptly leading to faster development cycles.

These are some of the benefits derived from Continuous Integration. Now let's look at using GitHub actions for CI.

Introduction to GitHub Actions

GitHub Actions serves as a platform for continuous integration and continuous delivery (CI/CD), enabling you to automate the process of building, testing, and deploying your software pipeline.

Here are some uses of GitHub Actions

  • You can set up workflows that automatically build and test each pull request submitted to your repository.
  • Deploy successfully merged pull requests to your production environment.
  • Actions can also be used to package or release projects into the pipeline.

GitHub Actions goes beyond just DevOps and lets you run workflows when other events happen in your repository. For example, you can run a workflow to automatically add the appropriate labels whenever someone creates a new issue in your repository. It has gained popularity for its simplicity and the fact that it is integrated directory into your repository.

Now let's look at the components that make up a GitHub workflow

Understanding a Github Action Workflow

A workflow is a configurable automated process that will run one or more jobs. A GitHub Actions workflow is defined by a YAML file typically located in the .github/workflows directory within your repository.

Components of a workflow

Triggers
These define the event that trigger the workflow. Example, the workflow can start when a user:

  • Creates a pull request
  • Opens an issue
  • Pushes a commit to the repository etc

Jobs
These specify the individual tasks to be executed as part of the workflow. Each job runs sequentially or in parallel and can include multiple steps.

Steps
Define the individual actions or commands to be executed within a job. Steps can include tasks like checking out code, running tests, or deploying artifacts.

Actions
Actions are reusable units of code that perform specific tasks within a workflow. They can be authored by GitHub or by the community and can be easily integrated into workflows. An action can pull your git repository from GitHub, set up the correct toolchain for your build environment

Runners
A runner is a server that runs your workflows when they're triggered. Each runner can run a single job at a time. GitHub provides three runners to run your workflows: Ubuntu Linux, Microsoft Windows and macOS.

Build an example workflow

In this section we would set up a simple workflow that runs linters for Python using pycodestyle. This will help ensure that your Python code adheres to PEP 8 style guidelines and is free of common errors.

Step-By-Step Guide

  1. Create the .github/workflows directory at the root of your repository.
  2. Inside the directory, create a file lint.yml. Go to Add file and then write the directory as shown in the image below

Creating the directory and the file

After this, commit the changes for it to take effect.

  1. Open the lint.yml file and add the following code


name: Lint Python Code

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.x'

    - name: Install Pycodestyle
      run: |
        pip install pycodestyle

    - name: Lint Python files with Pycodestyle
      run: |
        pycodestyle .


Enter fullscreen mode Exit fullscreen mode

Explanation of the code

  • name: Defines the name of the workflow as "Lint Python Code".
  • on: Triggers the workflow on push or pull request events to the main branch. Hence, anytime there's a pull request to merge code or pushing to main branch, this workflow would be triggered.
  • jobs: Defines a single job named lint that executes the linting steps.
  • runs-on: Specifies the environment which would run the workflow, in our case, ubuntu-latest.
  • steps: This section defines the sequential steps within the job:
    • Checkout Code: Uses the actions/checkout action to check out the repository code.
    • Set up Python: Uses the actions/setup-python action to set up a Python environment.
    • Install Pycodestyle: Installs the Pycodestyle linter for Python files.
    • Lint Python Files: Runs Pycodestyle on the entire repository to check for PEP 8 compliance.

This workflow ensures that every time code is pushed or a pull request is created, Pycodestyle is automatically run to check for style issues in Python code.

Now let's create a simple python file hello.py in our repository with the following code



#!/usr/bin/python3
"""
This is a function file
"""


def hello():
    print("hello World")


if __name__ == '__main__':
    hello()


Enter fullscreen mode Exit fullscreen mode

Now commit and push this code to your repository. The workflow would automatically be triggered since we specified a push in the trigger. You would see something equivalent to the image below in the actions tab

Full set of job hisory

Now when there's an error in the workflow (which normally reflects an error in the codebase), it would be marked red otherwise, it would check and you'll get something like the image below.

Checked Image

Debugging a workflow

Here's another python code which should fail in the workflow. We would then learn how to debug or troubleshoot a workflow.



#!/usr/bin/python3
"""
This is a function file
"""

def hello():
    print("hello World")

def new():
  print("This is me")

if __name__ == '__main__':
    hello()
    new()


Enter fullscreen mode Exit fullscreen mode

This is what we get from the workflow

History of workflow

Now click on the failed workflow to troubleshoot

Description of failed workflow

The logs rightly shows what needs to be done to get the linter to check correctly.



./python_with_error.py:6:1: E302 expected 2 blank lines, found 1
./python_with_error.py:9:1: E302 expected 2 blank lines, found 1
./python_with_error.py:10:3: E111 indentation is not a multiple of 4
./python_with_error.py:12:1: E305 expected 2 blank lines after class or function definition, found 1


Enter fullscreen mode Exit fullscreen mode

As shown above, there needs to be blank lines added and indentation issues. This gives you a sense of what exactly didn't allow the code to flow in the pipeline.

After fixing the issues with pycodestyle, you should have the equivalent of these

Fixed Issue

Checked issue

Advanced CI Workflow Techniques

There are other useful configurations when building a workflow. Let's look at three of them.

Variables

Variables store non-sensitive information that can change depending on the environment or the context of the workflow.

To add a new variable, navigate to Settings > Secrets and Variables > Actions

Variables

Add Variables

Use Cases

  • Setting environment-specific configurations, such as the target environment (development, staging, production).
  • Storing file paths or URLs that might differ between environments.

Secrets

Secrets store sensitive information securely, such as API keys, passwords, and tokens. They ensure that this information is not exposed in the workflow configuration or logs.

To add a new secret, navigate to Settings > Secrets and Variables > Actions

Secrets

Add Secrets

Use Cases

  • Stores access tokens for third-party services like AWS, Docker Hub, or email services.
  • Database passwords or credentials for accessing secure services.

Webhooks

Webhooks trigger actions in response to specific events, such as sending notifications to external services.

To add a new webhook, navigate to Settings > Webhooks

Webhooks

Add Webhook

Use Cases

  • Sending notifications to Slack, email, or SMS services when a build fails or succeeds.
  • Initiating deployments to environments such as staging or production when certain conditions are met.

Now let's add a notification to our workflow such that, a SMS is automatically sent to us when our pipeline fails.

Edit the yml file with the following code snippet



name: CI Pipeline

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest

<span class="na">steps</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout code</span>
  <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python</span>
  <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v4</span>
  <span class="na">with</span><span class="pi">:</span>
    <span class="na">python-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.x'</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
  <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">pip install requests pycodestyle</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Lint Python files with Pycodestyle</span>
  <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">pycodestyle .</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Send SMS Notification</span>
  <span class="na">if</span><span class="pi">:</span> <span class="s">failure()</span>
  <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">python - &lt;&lt;EOF</span>

    <span class="s">import requests</span>
    <span class="s">import json</span>

    <span class="s">quicksend_url = "https://uellosend.com/quicksend/"</span>

    <span class="s">data = {</span>
        <span class="s">'api_key': '${{ secrets.SMS_API_KEY }}',</span>
        <span class="s">'sender_id': 'GitHub',</span>
        <span class="s">'message': 'CI Pipeline has failed, check the details',</span>
        <span class="s">'recipient': '${{ secrets.SMS_RECIPIENT }}'</span>
    <span class="s">}</span>

    <span class="s">headers = {'Content-type': 'application/json'}</span>

    <span class="s">response = requests.post(quicksend_url, headers=headers, json=data)</span>

    <span class="s">with open('sms_response.json', 'w') as f:</span>
        <span class="s">json.dump(response.json(), f, indent=4)</span>
    <span class="s">EOF</span>
Enter fullscreen mode Exit fullscreen mode
Enter fullscreen mode Exit fullscreen mode




Explanation of Code

  • We check for a failure of the task to be run then we send aSMS to the user.
  • We use UelloSend for the SMS service.
  • Secrets are configured for the services in the secrets file within the repository.

Below is the SMS that would be sent should the run or job fail

Image of the error

Helpful links

Conclusion

In this article, we explored how GitHub Actions can streamline and enhance your Continuous Integration (CI) workflows. By leveraging GitHub Actions, you can automate various aspects of your development process, ensuring code quality and improving collaboration among team members.

By implementing these techniques, you can create robust CI workflows that not only automate code checks and tests but also keep you informed about the status of your builds in real-time. This enhances the overall development process, making it more efficient and reliable.

What did you think? Share your thoughts and feedback in the comments below!

Your input helps me deliver insightful and informative content in the future. Let's keep the conversation going!

Connect with me:

Happy learning 🚀

Top comments (4)

Collapse
 
mehedihasan2810 profile image
Mehedi Hasan

Thanks for sharing.

Collapse
 
angelotheman profile image
Angel Oduro-Temeng Twumasi

I believed you loved it. Keep in touch for the next ones

Collapse
 
jangelodev profile image
João Angelo

Hi Angel Oduro-Temeng Twumasi,
Thanks for sharing

Collapse
 
angelotheman profile image
Angel Oduro-Temeng Twumasi

My pleasure Angelo (That's my nick name actually Ha).

I believe you learnt something