|

Run GitHub Actions in Your Local System

Running GitHub Actions is an essential part of software development and continuous integration/continuous deployment (CI/CD) pipelines. However testing these workflows requires pushing code to a remote repository and waiting for the machine to execute the specified workflows. Constantly pushing commits, waiting for the Actions to run, and reviewing the results can be time-consuming and inefficient.

If we can run GitHub actions on the local system it can speed up development, reduce errors, and improve collaboration among team members. Running actions locally allows developers to test and debug their workflows locally before pushing to GitHub.

In this blog post I will show you way to run GitHub Actions locally with library called act.

How act Works

When you run act CLI, it reads your GitHub Actions from .github/workflows/ directory and chooses which actions to execute. It uses the Docker API to either pull or build the images specified in your workflow files, and then determines the execution path based on the dependencies. Once the execution path is determined, it uses the Docker API to launch containers for each action based on the images created earlier. The environment variables and filesystem are all set up to work with what GitHub offers.

Prerequisites

  • Docker

Installation

You can install act both manually and using package manager

Manual Installation

Download the latest binary release  corresponding to your OS and add the binary into your PATH.

Installation through package managers

With Chocolatey

choco install act-cliCode language: Java (java)

With Scoop

scoop install actCode language: Java (java)

With WinGet

winget install nektos.actCode language: Java (java)

With Homebrew

brew install actCode language: Java (java)

With AUR

yay -Syu actCode language: Java (java)

With Bash Script

curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bashCode language: Java (java)

With Homebrew

brew install act

With MacPorts

sudo port install actCode language: Java (java)

Usage

When we run act command first time, it will ask us to download image.

Select the medium image. You could also select Large image if you have enough space, which contains all the tools used GitHub Actions.

$ act
? Please choose the default image you want to use with act:

  - Large size image: +20GB Docker image, includes almost all tools used on GitHub Actions (IMPORTANT: currently only ubuntu-18.04 platform is available)
  - Medium size image: ~500MB, includes only necessary tools to bootstrap actions and aims to be compatible with all actions
  - Micro size image: <200MB, contains only NodeJS required to bootstrap actions, doesn't work with all actions

Default image and other options can be changed manually in ~/.actrc (please refer to https://github.com/nektos/act#configuration for additional information about file structure)  [Use arrows to move, type to filter, ? for more help]
  Large
> Medium
  MicroCode language: Java (java)

Let’s use the act to run the GitHub Actions in local system using the workflow from the https://github.com/sureshgadupu/sb-ghaction-docker repo.

Command Structure

act cli supports following style command.

$ act [<event>] [options]
If no event name passed, will default to "on: push"
If actions handles only one event it will be used as default instead of "on: push"Code language: Java (java)

You can run following commands from the root of the project

Listing all the actions

You can list all the actions associated with different events.

$ act -l
Stage  Job ID       Job name     Workflow name      Workflow file      Events
0      publish-app  publish-app  Docker Multi-Arch  docker-image.yaml  push,pull_requestCode language: Java (java)

List the actions for a specific event:

$ act push -l
Stage  Job ID       Job name     Workflow name      Workflow file      Events
0      publish-app  publish-app  Docker Multi-Arch  docker-image.yaml  push,pull_request

List the actions for a specific job:

$ act -j publish-app -l
Stage  Job ID       Job name     Workflow name      Workflow file      Events
0      publish-app  publish-app  Docker Multi-Arch  docker-image.yaml  push,pull_requestCode language: Java (java)

Run a specific event:

$ act pull_requestCode language: Java (java)
$ act pushCode language: Java (java)

Run a specific job:

$ act -j publish-appCode language: Java (java)

Run Default Job

$ actCode language: Java (java)

Testing the Workflow in Local System

Let’s look at the workflow file from the our selected repo.

Below GitHub Action workflow does the following .

1.Checkout the code

2. Setup JVM

3.Build the jar with Maven

4.Login into Docker Hub

5.Build multi arch docker image and push it to hub

name: Docker Multi-Arch

env:
  IMAGE_NAME: sureshgkhyd/sb-crud-image
  DOCKER_REGISTRY: docker.io

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  publish-app:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: |
          echo "IMAGE_NAME_WITH_REGISTRY=$DOCKER_REGISTRY/$IMAGE_NAME" >> $GITHUB_ENV
          export IMAGE_NAME_WITH_REGISTRY=$DOCKER_REGISTRY/$IMAGE_NAME
          echo "FULL_IMAGE_NAME=$IMAGE_NAME_WITH_REGISTRY:latest" >> $GITHUB_ENV
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven
      - name: Build with Maven
        run: mvn --batch-mode --update-snapshots package
      - name: Login to Docker Hub
        uses:  docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
      - name: Setup Docker buildx
        uses: docker/setup-buildx-action@v2
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: |
              ${{ env.FULL_IMAGE_NAME }}
              ${{ env.IMAGE_NAME_WITH_REGISTRY }}:${{ github.sha }}Code language: Java (java)

Since we have single workflow in our test repo, we can use the simply act command from the root of the project from terminal.

Note

At the time of writing this article, act docker image had issues with Docker version 4.16.x while setting up JVM in workflow. I had to downgraded the docker version to 4.16.0 to resolve the problem.

If we run following command from root of the project

$ actCode language: Java (java)

It ran workflow but failed as it could not setup the Maven in act docker image.

[Docker Multi-Arch/publish-app] ⭐ Run Main Build with Maven
[Docker Multi-Arch/publish-app]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/3] user= workdir=
| /var/run/act/workflow/3: line 2: mvn: command not found
[Docker Multi-Arch/publish-app]   ❌  Failure - Main Build with Maven
[Docker Multi-Arch/publish-app] exitcode '127': command not found, please refer to https://github.com/nektos/act/issues/107 for more informationCode language: Java (java)

There are 2 options to resolve the problem.

1.Build the custom act docker image with Maven installed.

2.Install the Maven on the act docker image during the workflow.

In this blog post, we will modify the workflow to install the Maven and use it.

Let’s add following workflow step to install the Maven.

      - name: Download Maven
        run: |
          curl -sL https://www-eu.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip -o maven.zip
          apt-get update
          apt-get -y install unzip
          unzip -d /usr/share maven.zip
          rm maven.zip
          ln -s /usr/share/apache-maven-3.6.3/bin/mvn /usr/bin/mvn
          echo "M2_HOME=/usr/share/apache-maven-3.6.3" | tee -a /etc/environment
      - name: Build with Maven
        run: mvn --batch-mode --update-snapshots packageCode language: Java (java)

Note

I have not tested with Gradle, but if you face problem with building with Graden, similar to Maven you could install the Gradle. You can see the installation commands from link

Now lets run the act command again.

Now jar is built successfully but workflow fails to log into docker as it requires user name and password / token which are secrets.

[Docker Multi-Arch/publish-app] ⭐ Run Main Login to Docker Hub
[Docker Multi-Arch/publish-app]   🐳  docker cp src=C:\Users\deepika\.cache\act/docker-login-action@v2/ dst=/var/run/act/actions/docker-login-action@v2/
[Docker Multi-Arch/publish-app]   🐳  docker exec cmd=[node /var/run/act/actions/docker-login-action@v2/dist/index.js] user= workdir=
[Docker Multi-Arch/publish-app]   ❗  ::error::Username and password required
[Docker Multi-Arch/publish-app]   ❌  Failure - Main Login to Docker HubCode language: Java (java)

We can pass the secrets using CLI params with -s option.

act -s DOCKER_USERNAME=<username> -s DOCKER_TOKEN=<password/token>Code language: Java (java)
[Docker Multi-Arch/publish-app] ✅ Success - Post Build and push<br>[Docker Multi-Arch/publish-app] ⭐ Run Post Setup Docker buildx<br>[Docker Multi-Arch/publish-app] 🐳 docker exec cmd=[node /var/run/act/actions/docker-setup-buildx-action@v2/dist/index.js] user= workdir=Code language: Java (java)

If you need to pass the environment variables you can do with –env <key>=<value> option.

Another option to avoid installation of Maven in workflow is to use Multistage docker build file which already uses JDK and Maven Image to compile and build the application jar file.

You can look at the repo https://github.com/sureshgadupu/sb-ghaction-docker2 to view the multi stage docker image and corresponding the GitHub Action workflow.

If your workflow depends on this token, you need to create a personal access token and pass it to act as a secret:

act -s GITHUB_TOKEN=[personal access token ]Code language: Java (java)

Similar Posts