act lets you run GitHub Actions workflows on your local machine, which is a game-changer for faster iteration and debugging.
Let’s see it in action. Imagine you have a simple workflow in .github/workflows/main.yml:
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run a one-line script
run: echo "Hello, world!"
- name: Run a script with arguments
run: |
echo "Greeting: Hello"
echo "Target: World"
To run this locally with act, you first need to install it. On macOS with Homebrew, it’s brew install act. For other systems, check the act GitHub repository.
Once installed, navigate to your repository’s root directory in your terminal. To simulate a push event, you’d run:
act push
This will download the necessary Docker images (like ubuntu-latest) and execute your workflow. You’ll see output similar to what you’d get on GitHub Actions, including the "Hello, world!" and the greeting/target messages.
The real power comes with more complex workflows. Let’s say your workflow needs to use secrets or environment variables. You can create a .actrc file in your repository’s root or home directory. For secrets, it looks like this:
--secret MY_SECRET_TOKEN=ghs_abcdef1234567890
--secret NPM_TOKEN=npm_xyz7890123456
And for environment variables:
--env MY_ENV_VAR=some_value
--env ANOTHER_VAR=another_value
Now, if your workflow uses an action that requires MY_SECRET_TOKEN or ANOTHER_VAR, act will provide them.
To target a specific job or workflow, you can use flags. If you have multiple workflows in .github/workflows/, you can specify which one to run:
act -W .github/workflows/deploy.yml push
Or, if a workflow has multiple jobs and you only want to run one:
act -j build push
This allows you to quickly test individual components of your CI/CD pipeline without pushing code and waiting for GitHub Actions to run.
act also understands the event payload. For a push event, it defaults to using the last commit on your current branch. You can provide a custom payload using a JSON file:
act push -f event.json
Where event.json might contain:
{
"ref": "refs/heads/main",
"before": "a1b2c3d4e5f6",
"after": "f6e5d4c3b2a1",
"commits": [
{
"id": "f6e5d4c3b2a1",
"message": "Test commit for act",
"author": {
"name": "Test User"
}
}
]
}
This is crucial for testing workflows triggered by specific commit messages or branch references.
When you encounter issues with act itself, or if your actions behave differently locally than on GitHub, the most common culprit is how act emulates the GitHub Actions environment. It uses Docker containers, and the underlying container image is critical. By default, act uses images from ghcr.io/actions/actions-runner/_temp. If your workflow relies on specific system packages or versions not present in the default image, you’ll need to specify a custom runner image.
You can do this via the .actrc file:
--container-architecture linux/amd64
--container-image ghcr.io/my-custom-runners/ubuntu:latest
Or directly on the command line:
act --container-architecture linux/amd64 --container-image ghcr.io/my-custom-runners/ubuntu:latest push
This allows you to precisely control the execution environment, mirroring what might be available on a self-hosted runner or ensuring compatibility with your build tools. The container-architecture flag is important for M1/M2 Macs to ensure compatibility with images built for amd64.
Another common pitfall is how act handles caching and artifacts. By default, act stores caches and artifacts in local directories like ~/.act/cache and ~/.act/artifacts. If your workflow relies on specific cache keys or artifact names, and they aren’t being restored correctly, ensure your workflow’s actions/cache or actions/upload-artifact steps are configured to use consistent names that act can recognize. Sometimes, clearing these local cache/artifact directories (rm -rf ~/.act/cache ~/.act/artifacts) can resolve stubborn issues, as stale data can lead to unexpected behavior.
The next hurdle you’ll likely face is debugging complex multi-job workflows where dependencies between jobs are not being met correctly in the local simulation.