Jul 29, 2023

Should you consider migrating CI/CD pipelines from Azure DevOps to GitHub Actions?

As engineering teams look for ways to streamline their processes and enhance their development workflow, the adoption of CI/CD (Continuous Integration/Continuous Delivery) pipelines has become quite the norm. Azure DevOps was one of the first platforms to offer YAML based CI/CD templates and quickly became the favorite tool amongst a lot of developers. After Microsoft's acquisition of Github, they quickly introduced the much-desired enterprise-grade CI/CD tooling in GitHub Actions, and it quickly rose to popularity. While Azure DevOps remains a feature-rich tool, with GitHub's continuous upgrades and versatile features, an increasing number of developers are migrating from Azure DevOps to GitHub Actions.

With the increasing popularity and capabilities of GitHub Actions (GHA), many teams are considering migrating their CI/CD pipelines from Azure DevOps (ADO) to GitHub Actions. This article aims to provide an outline of the advantages of GHA, and a general roadmap for the migration process, highlighting key considerations and steps.

Why Migrate to GitHub Actions?

GitHub Actions has seen rapid adoption since its introduction due to its tight integration with GitHub repositories and its event-driven model. With this model, workflows can be triggered on almost any GitHub event, enabling highly flexible and customizable automation.

While both ADO and GHA are YAML using relatively similar YAML templates , certain characteristics and features may make GHA a more appealing choice for most teams or projects. Here are a few key advantages of GitHub Actions over Azure DevOps.

Marketplace and Custom Actions

GitHub Actions marketplace is more extensive and active, with thousands of actions available for various tasks. This contrasts with Azure DevOps, where task extensions are less numerous and community contribution is not as robust.

Ease of Integration

GitHub Actions marketplace hosts a plethora of actions, ranging from setting up different environments (like actions/setup-node for Node.js, actions/setup-python for Python) to deploying code to different cloud providers (like aws-actions/aws-s3-sync for syncing a directory to an S3 bucket). In comparison, Azure DevOps does offer an extensions marketplace, but it's not as comprehensive or user-friendly as GitHub's.

For instance, consider the action of deploying a Docker image to a Kubernetes cluster. The GitHub marketplace has an action like azure/k8s-deploy, which simplifies the process to a few lines of YAML.

While Azure DevOps can achieve similar results, it usually requires a more complex series of tasks to accomplish the same goal.

Community Contributions

GitHub Actions benefits greatly from being built on the most popular platform for open-source software. Many actions in the marketplace are contributed by the community and go beyond mere CI/CD tasks, including actions for automating the management of issues, pull requests, and project boards. For example, the actions/stale action marks issues and pull requests that have not seen activity for a specified amount of time as stale, encouraging more effective project management.

Triggers and Event-Driven Workflows

GitHub Actions supports a broader range of event triggers. Any event that happens within a GitHub repository can be used to trigger a workflow. On the other hand, Azure DevOps primarily focuses on push and pull request events for CI/CD workflows.

Pull Request Triggers

The workflow runs whenever a pull request is opened, synchronized, or closed.

For example, with ADO, you have to do a  lot of PowerShell magic to trigger a pipeline for a pull request closure.

With GHA, there are pre-build actions, and conditions to do this quite easily.

Issue Comments Triggers

The workflow runs when a comment is added to an issue in the repository.

While ADO also has support for comment-based triggers, this is only supported if you are using GitHub as the source repository. ( in most instances, if you are using ADO Pipelines, you are likely to be using ADO Git repos, so this feature is actually useless).

With GHA, this is straightforward to do.

Release Triggers

In Azure DevOps, this is typically done using Release Pipelines, which are designed to handle the deployment of your application. You can create a release manually, or you can configure the release pipeline to automatically create a new release when it detects a new build artifact (which could be created by a build pipeline when you create a GitHub release, for example).

In GitHub Actions, you can trigger a workflow directly from your YAML file when a release is created by using the release event. You can further specify the types of release events that will trigger the workflow, such as created, edited, published, etc.

Parallelism and Matrix Builds

Both platforms support running jobs in parallel and matrix builds. However, GitHub Actions offers a more intuitive syntax and configuration for matrix builds.

Azure DevOps has the concept of a "Matrix Strategy" that allows you to run jobs in parallel with different configurations. Here is an example:

GitHub Actions also supports a similar feature, also known as "Matrix Strategy". However, GitHub Actions provides a more flexible approach, allowing you to combine different sets of variables. Here is an example:

Runner Environment

GitHub Actions and Azure DevOps both offer self-hosted runners, allowing you to run workflows on your own hardware and in your own environment. Self-hosted runners can be used when you have specific requirements that aren't met by the hosted runners, like when you need a specific OS or tool that's not available on the hosted runners, or when your code needs to access resources that are only available in your local network.

However, GitHub Actions goes a step further by offering the ability to run actions in Docker containers on their hosted runners. This means that you can create a Dockerfile to specify the exact environment that your workflow needs, and this Dockerfile will be built and run on the GitHub-hosted runner.

Here's an example of how this work:

First, you create a Dockerfile that specifies the exact environment your workflow requires. For instance, if your workflow requires `Node.js` and a specific version of the AWS CLI, your Dockerfile might look like this:

Next, you define a custom action in the same repository as your workflow. This action uses the Dockerfile you just created. The `action.yml` for this custom action would look like this:

Finally, you use this action in your workflow:

In this setup, the custom-job job will run on a GitHub-hosted runner, but the action will run in the Docker container that you've specified. This gives you the ability to specify the exact environment that your action needs, without the need to set up and manage your own self-hosted runner.

Integrations

The seamless integration of GitHub Actions with the wider GitHub ecosystem is one of its key advantages. GitHub Actions natively supports a host of features that can be used to automate, customize and optimize software workflows. Here are a few examples:

GitHub Advanced Security

GitHub Advanced Security extends the capabilities of GitHub Actions by offering features such as code scanning and secret scanning. Code scanning can be automated as a step within a GitHub Actions workflow, scanning the codebase for potential security vulnerabilities whenever a change is made.

Dependabot

Dependabot is an automated tool that checks for outdated dependencies in your repositories and automatically opens pull requests to update them. It can be configured directly from GitHub, and it integrates seamlessly with GitHub Actions workflows.

You can even set up a workflow to automatically merge Dependabot PRs once checks pass:

In summary, the deep integration of GitHub Actions with the wider GitHub platform provides a unified experience that allows development teams to automate their workflows directly within their source control environment, leveraging advanced security features, automated dependency management, and a vast array of community-contributed actions.

Should you decide to migrate your Azure DevOps Pipelines GitHub Actions, below are some high-level actions to follow.

Step-by-Step Migration Guide

Step 1: Assess Your Current Azure Pipelines

Before diving into the migration, it's important to understand your existing Azure Pipelines in detail.

This analysis will provide a blueprint for recreating your pipelines in GitHub Actions.

Step 2: Understand GitHub Actions Structure

GitHub Actions are composed of workflows that respond to events on your GitHub repository. Each workflow consists of one or more jobs, which are sets of steps that perform individual tasks. Steps can run commands or actions, which are pre-built pieces of code.

Understand the key components:

Step 3: Create an Equivalent GitHub Actions Workflow

Create a .github/workflows directory in your GitHub repository, and add a new YAML file to define your workflow. Use your analysis from Step 1 to guide the definition of jobs and steps.

Consider using prebuilt actions from the GitHub marketplace to replace custom scripts in your Azure Pipeline, but be aware of versioning and potential security concerns.

Step 4: Handle Secrets and Environment Variables

GitHub Actions provide mechanisms to handle secrets and environment variables. Use the GitHub web interface to configure secrets at the repository or organization level.

Environment variables can be set at the workflow, job, or step level within the YAML workflow file. They can also reference secrets, providing a way to securely handle sensitive data.

Step 5: Implement Conditional Logic

GitHub Actions supports conditional jobs and steps, using a syntax similar to Azure DevOps. Conditional statements are written in JavaScript expression syntax, providing flexibility for complex logic.

Step 6: Testing the Migration

Before fully transitioning to GitHub Actions, make sure to thoroughly test your new workflows. This can be done by manually triggering the workflow or pushing changes to your repository that would trigger the workflow.

Migrating Azure functions deployment from ADO to GHA

This example is a simple comparison of what an Azure Function deployment using Azure DevOps and GitHub Actions.

Azure DevOps YAML pipeline for Azure Function Deployment

GitHub Actions Workflow for Azure Function deployment

In the GitHub Actions workflow, we're also doing two main tasks: Building the app and deploying it. The build is done with a run command that directly uses the dotnet CLI, and the deployment is done with the Azure/functions-action@v1 action.

Side-by-side comparison of ADO ( left) and GHA (right) deploying an Azure Functions App.

As you can see, both the Azure DevOps pipeline and the GitHub Actions workflow achieve the same goal, and the syntax and structure are quite similar, so the migration process is relatively simpler.

Conclusion

GitHub Actions offer significantly better DevOps/DevSecOps tooling. Migrating from Azure DevOps to GitHub Actions involves translating Azure Pipelines YAML to GitHub Actions YAML, while mapping tasks to actions. While the sample provided here is specific to Azure Function App deployment, the process is similar for other applications.

It's worth noting that the convenience and increasing capabilities offered by GitHub Actions make it a compelling option. However, the decision should be based on the specific needs and context of your projects.

Feel free to reach out if you need help with preparing a migration strategy to GitHub Actions.

Further readings:

Understanding GitHub Actions

Migration from Azure Pipelines to GitHub Actions

Interested in hearing more?
Lets connect.