Migrating from CircleCI to GitHub Actions

Samuel Cabral Cruz
3 min readMar 23, 2021

As I said in a previous article, I recently been assigned to a new project at my daily workplace. The project was already going on for a couple of months and the team was strictly developing a Node.js/Typescript/Express backend. Upon my arrival, the scope of the project get extended to also develop a frontend to expose the capacity of the backend through a beautiful web interface. The team agreed on using a monorepo approach to facilitate team organization and project management. The backend project’s CI/CD pipeline was supported by CircleCI.

Even if CircleCI is a mature workflow automation platform, it performs poorly when used in a monorepo context. We then decided to migrate over GitHub Actions which is a lot more suited for this use case. However, this is not a perfect solution as I showed in another article. Moreover, due to lack of time before the delivery date, it been decided to keep the current CircleCI as is, but to stop fatten it if possible and to write all new CI/CD configuration with GitHub Actions henceforth.

The Problem

Nevertheless, we faced the problem that the CI/CD workflow were constantly executed even if nothing had changed in the backend project. No need to say that this was a lost of time and resources that had to be addressed especially knowing that the workflow takes about 10 minutes to execute. In the following article, I will share how we dealt with this situation.

Obtained Solution

The obtained solution consists in leveraging GitHub Actions on.<push|pull_request>.paths feature and the CircleCI’s Pipeline API along with some minimal tweaks to the existing .circleci/config.yml file. The tweaks can be summarized as

  • The addition of a action parameter which can take two values (skip or execute)
  • The creation of a new workflow and a placeholder job since CircleCI absolutely need to execute at least one job successfully to report a success
  • The usage of when attribute based on the action parameter in the initial workflow to conditionally run it

Here is a gist presenting the most important parts of the solution.

Even if I consider the gist self explanatory, it won’t hurt to explain it in more details. So most of the complexity resides in the GitHub action itself. What it says is that this action should be trigger whenever a push is made on master branch or on a pull request based on master affecting any file into the subfolders backend or infra or to this workflow file itself. There is only one job which aims at programmatically trigger a new CircleCI pipeline using the public API.

In order to query the API, we must extract the branch name to indicate to CircleCI from which branch it should checkout the code. Due to some inconsistencies between both push and pull_request event payloads, we need to use GitHub Actions environment variables conditionally to the type of event to extract the correct branch name.

The last step is simply to call the CircleCI API to trigger a new pipeline with the action parameter set to execute.

Important Details

  • The CIRCLECI_TOKEN has to be an admin Personal API Token (PAT). See the documentation for more details.
  • It is necessary to run the placeholder job for both skip and execute. Otherwise, you could have a stale failed status on your pull request for the placeholder job because CircleCI will report a failure when a job is canceled.
  • I had no success using when condition on workflow with computed environment variables that would determine if the workflow has to be executed or not and then had to default to the usage of parameters.
  • I found no way to tell CircleCI not to trigger automatically but keep the reporting to GitHub on workflow execution.

Conclusion

I really hope this article will help anyone facing the same issue as we did by partially migrating from CircleCI to GitHub Actions. Bare in mind that the obtained solution is a viable workaround to different limitations of CircleCI.

--

--