Developing Github Actions Locally using act

Developing Github Actions Locally using act

Submitted by Ricardo Carrasco on Thu, 04/11/2024 - 14:48

I have been working in a professional devops/automation role for about 2 years now, the CICD work I've done was mainly with Jenkins. Recently I was asked to automate the deployments for a static site using github actions. I've never worked with github actions before but I wasn't afraid of the challenge.
As I began work on the pipeline i stumbled upon a tool that allows you to run your github actions on your local machine, today I'm sharing that tool with you.

The problem with pipeline-as-code CICD


Coming from a Jenkins background, the ability to run a pipeline locally was amazing. For those that don't know, in order to test out any change in a Jenkins pipeline you must commit and push to a remote repository. Only then can you run a new build to test your Jenkinsfile.

This of course is completely backwards from the norm of...
[Change] -> [Test] > [Commit & Push]

Instead you are following...
[Change] -> [Commit & Push] -> [Test]

When you have to follow this flow, your repository starts to get bloated with endless commits that have minute changes like so:

Workarounds for this include:

  • Reseting your previous commit, making a new change, and force pushing..
  • Continuing with multiple commits, squashing them all when you're done with your changes, and then force pushing...

For some fairness, Jenkins does include a "Replay" feature where one can rerun a build with a change in the Jenkinsfile without having to commit to your repository, but it subjects you to using what is essentially a plain-text editor with basic syntax-highlighting

No vs code dark theme, no extensions, no shortcuts, no autocompletion, and no auto formatting. In 2024 thats just unnacceptable.

What is act & how it solves this problem for github actions


act is a cli tool used locally test your github actions. This solves this huge issue of having to commit and push to test. Now you are free to run as many iterations of your actions locally.
This enables you to rapidly iterate over your action and quickly find what is and is not working.

From act's repository:

It uses the Docker API to either pull or build the necessary images, as defined in your workflow files and finally determines the execution path based on the dependencies that were defined. Once it has the execution path, it then uses the Docker API to run containers for each action based on the images prepared earlier.

Installation & Setup


act has several installation methods listed on their website, the simplest of which to follow for me was installing the tool as an extension to the Github CLI

  • Follow this documentation to install the github cli for your linux-based system
  • Proceed with installing the extension with the command below
    gh extension install https://github.com/nektos/gh-act

Once installed you should be able to run gh act
act will prompt you to select the default docker image size to use for actions, giving you three options

  1. Micro <200MB
  2. Medium ~500MB
  3. Large ~ 17GB

For my use case I selected micro images since the project this was for was small in size. Feel free to increase this if you project has the need for it.

Basic Usage


To test your actions locally, you can use act to simulate a trigger event such as push, release, fork, etc. See full list here

gh act push # Run all workflows with push event

gh act release # Run all workflows with release event**

gh act issue # Run all workflows with issue event**

** For trigger events that also rely on an event payload such as github issues, or releases, you can simulate the payload by passing the flag --eventpath /path/to/event.json. This file would include the payload for the trigger event you are simulating.

I mainly used the push event, which is the default when running gh act by itself

Handling Secrets


A crucial part of CICD is credential management & access. Even though we have deployment credentials added onto the Github repository, act is running locally, so when you reference secrets within your actions file, you must provide act with the variables to use since it can't reach out to fetch secrets from github.
You can pass these variables to act by passing them directly to the command like so:

gh act -s MY_SECRET=somevalue

The documentation warns against this, since it might save your secrets as plain text to a history file. To stay on the safe side, a better way to pass variables would be reference a secrets file via the flag:

gh act --secret-file example.env

By passing this flag you'll be able to populate environment variables your deployment depends on. The below is a full example of act in action :)

Basic Example


name: Deploy to Cloudflare Pages
on:
  push: 
    tags:
      - 'release/*'
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Packages
        run: 'npx pnpm install'

      - name: Build Static Site
        run: 'npm run build'

      - name: Publish to Cloudflare Pages
        uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: test-site
          directory: build
          wranglerVersion: '3'

Quick breakdown of steps

  1. Checkout Repo that action is running from, uses a predefined step from github
  2. Install packages with npx pnpm install
  3. Run build with npm run build, storing the build in a new directory named build
  4. Using Cloudflare provided step to deploy to cloudflare pages, importantly providing credentials and build directory,

    Running Locally

Running act locally

With this I can see that the build is deploying and I can trust that my commit won't be in vain!

Resources

Ricardo Carrasco

Profile picture for user Ricardo Carrasco
Software Developer
  • Sveltekit & NodeJS Development
  • Javascript, Python
  • Jenkins CICD 
  • Shell Scripting & Automation tools
  • Kubernetes & Docker (Kubernetes and Cloud Native Associate)

"Intelligence without ambition is like a bird without wings"