GitHub Actions, a powerful tool provided by GitHub, enables developers to automate their workflows directly within their repositories. Whether it’s continuous integration (CI), continuous deployment (CD), or any other custom automation task, GitHub Actions simplifies the process. In this article we will learn how to test your GitHub Actions locally with the act CLI tool.
The challenge of running locally GitHub actions
New to GitHub Actions? check this article: Getting started with GitHub Actions
One challenge developers often face is testing these workflows locally before pushing changes to the remote repository. Historically, this has been cumbersome, requiring developers to push changes to a branch and wait for the GitHub Actions workflow to execute. However, a solution has emerged in the form of “act” – a remarkable CLI tool that allows developers to run GitHub Actions workflows locally.
How does act work?
When you execute act, it scans your GitHub Actions from .github/workflows/
and identifies the set of actions requiring execution. It leverages the Docker API to pull or build the requisite images, as specified in your workflow files, and subsequently establishes the execution flow based on the specified dependencies. Upon determining the execution flow, it utilizes the Docker API to launch containers for each action using the pre-prepared images. The environment variables and filesystem are configured to align with GitHub’s offerings.
Installing Act
Firstly, you need to install the act tool on your machine. There are several options available:
# Linux curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash # Window choco install act-cli # MacOS brew install act
Then, include the act command line in your system PATH. For example, if you downloaded it from your user’s home directory:
export PATH=$PATH:~./bin
Next, make sure your Docker daemon is up and running:
service docker start
Finally, you can run the act tool. The first time you will execute, it will ask you which image you will be using to build your GitHub actions. I have tested the medium size image which should be compatible with most actions:
Your first local GitHub action
Now we are ready to run our first GitHub action locally.
- Firstly, clone or download any of your GitHub repository .
- Then, add a workflow in the
.github/workflows
folder. For example, thehelloworld.yml
:
├── .github └── workflows ├── helloworld.yml
Here is, for example, the example helloworld.yml
file:
name: HelloWorld Action on: push: branches: - main jobs: helloworld-job: name: HelloWorld Job runs-on: ubuntu-latest steps: - name: Print HelloWorld run: echo "helloworld"
Nothing fancy, this GitHub action merely prints an Hello World Message.
You can test the act -l
command which shows the list of available jobs in your repository:
act -l Stage Job ID Job name Workflow name Workflow file Events 0 helloworld-job HelloWorld Job HelloWorld Action helloworld.yml push
Then, run your job as follows:
act --job helloworld-job
The first time you start a job, the act CLI will download the Docker image which you need to run the jobs so it will take a while. At the end of it, you should see the following output:
[HelloWorld Action/HelloWorld Job] 🚀 Start image=catthehacker/ubuntu:act-latest INFO[0000] Parallel tasks (0) below minimum, setting to 1 [HelloWorld Action/HelloWorld Job] 🐳 docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true INFO[0001] Parallel tasks (0) below minimum, setting to 1 [HelloWorld Action/HelloWorld Job] 🐳 docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host" [HelloWorld Action/HelloWorld Job] 🐳 docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host" [HelloWorld Action/HelloWorld Job] ⭐ Run Main Print HelloWorld [HelloWorld Action/HelloWorld Job] 🐳 docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/0] user= workdir= | helloworld [HelloWorld Action/HelloWorld Job] ✅ Success - Main Print HelloWorld [HelloWorld Action/HelloWorld Job] Cleaning up container for job HelloWorld Job [HelloWorld Action/HelloWorld Job] 🏁 Job succeeded
Coding a Maven GitHub action locally
The next action we will run is a real one as it includes a Maven build of the project and a staging of the output. Create a YAML file with the following content:
name: Java CI with Maven on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' cache: maven - name: Download Maven run: | curl -sL https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-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.9.6/bin/mvn /usr/bin/mvn echo "M2_HOME=/usr/share/apache-maven-3.9.6" | tee -a /etc/environment - name: Build with Maven run: mvn -B package --file pom.xml - name: Create staging directory run: mkdir -p staging - name: Copy JAR files to staging directory run: cp target/*.jar staging/ - name: Upload artifacts uses: actions/upload-artifact@v3 with: name: java-artifacts path: staging/*.jar
If you are using the default GitHub actions template, you will notice there’s a difference as here we are downloading Maven in the “Download Maven” step.
If you don’t include this step, the action will fail as it cannot find the mvn tool:
| /github/workflow/3: line 2: mvn: command not found [Build, Test, and Upload/Java Application Container] ❌ Failure - Compile and Test Using Maven
This is discussed in greater detail in this issue. There are several possible solutions to it. In this case, we are installing the Maven tool before using it:
- name: Download Maven run: | curl -sL https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-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.9.6/bin/mvn /usr/bin/mvn echo "M2_HOME=/usr/share/apache-maven-3.9.6" | tee -a /etc/environment
Let’s list again the available Jobs:
act -l Stage Job ID Job name Workflow name Workflow file Events 0 helloworld-job HelloWorld Job HelloWorld Action helloworld.yml push 0 build build Java CI with Maven maven.yaml push,pull_request
Now you can run the build job, by providing also the path where act will upload your artifacts with artifact-server-path
:
act --job build --artifact-server-path /tmp/artifacts
Your job should successfully complete:
[Java CI with Maven/build] ✅ Success - Main Upload artifacts [Java CI with Maven/build] ⭐ Run Post Set up JDK 17 [Java CI with Maven/build] 🐳 docker exec cmd=[node /var/run/act/actions/actions-setup-java@v3/dist/cleanup/index.js] user= workdir= [Java CI with Maven/build] 💬 ::debug::Checking zstd --version [Java CI with Maven/build] 💬 ::debug::*** zstd command line interface 64-bits v1.4.8, by Yann Collet *** [Java CI with Maven/build] 💬 ::debug::implicitDescendants 'false' [Java CI with Maven/build] 💬 ::debug::followSymbolicLinks 'true' [Java CI with Maven/build] 💬 ::debug::implicitDescendants 'false' [Java CI with Maven/build] 💬 ::debug::omitBrokenSymbolicLinks 'true' [Java CI with Maven/build] 💬 ::debug::Search path '/root/.m2/repository' [Java CI with Maven/build] 💬 ::debug::Matched: ../../../../root/.m2/repository [Java CI with Maven/build] 💬 ::debug::Cache Paths: [Java CI with Maven/build] 💬 ::debug::["../../../../root/.m2/repository"] [Java CI with Maven/build] 💬 ::debug::Archive Path: /tmp/704e9468-8dc9-4c5d-a505-ddd89817fa63/cache.tzst | [command]/usr/bin/tar --posix --use-compress-program zstdmt -cf cache.tzst --exclude cache.tzst -P -C /home/francesco/git/githubactiondemo --files-from manifest.txt [Java CI with Maven/build] 💬 ::debug::File Size: 9914711 [Java CI with Maven/build] 💬 ::debug::Reserving Cache [Java CI with Maven/build] 💬 ::debug::Resource Url: http://192.168.1.5:43793/_apis/artifactcache/caches ERRO[0021] POST /_apis/artifactcache/caches: already exist module=artifactcache | Cache saved with the key: setup-java-Linux-maven-b8e7380cd023cf0af45ace7459a151ef98d9da4061dbcd5fb2d0d8ccc0a2ff1c [Java CI with Maven/build] ✅ Success - Post Set up JDK 17 [Java CI with Maven/build] Cleaning up container for job build [Java CI with Maven/build] 🏁 Job succeeded
Conclusion
This article provided a set-by-step guide to get started with the act CLI which allows you to test your GitHub actions locally, Act complements this by enabling developers to test their workflows locally, providing a fast and efficient mechanism for validation and debugging.