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 codebaseFaster 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
- Create the
.github/workflows
directory at the root of your repository. - Inside the directory, create a file
lint.yml
. Go to Add file and then write the directory as shown in the image below
After this, commit the changes for it to take effect.
- 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 .
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()
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
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.
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()
This is what we get from the workflow
Now click on the failed workflow to troubleshoot
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
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
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
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
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
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 - <<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>
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
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)
Thanks for sharing.
I believed you loved it. Keep in touch for the next ones
Hi Angel Oduro-Temeng Twumasi,
Thanks for sharing
My pleasure Angelo (That's my nick name actually Ha).
I believe you learnt something