281

I am planning to move our Travis CI build to GitHub Actions using Docker for our per-commit testing.

Can I reproducibly run these new GitHub Actions workflows locally? Is there a generic way to run any GitHub Actions workflow locally?

3
  • 2
    There is if you are still using the deprecated HCL syntax for actions and haven't graduated to the new YAML style. I have yet to see anything being done to support the new yaml style workflows
    – smac89
    Commented Dec 9, 2019 at 5:47
  • 1
    there is a lot of interest for nejtos/act to support YAML syntax, see my answer below with links to the issues it's being discussed.
    – Zia
    Commented Jan 31, 2020 at 0:19
  • 2
    maybe self-hosted runners can help github.com/actions/runner Commented Jul 9, 2021 at 1:44

10 Answers 10

222

There are tools like the already-mentioned act, but they are not perfect. You are not alone with this issue. Similar problems are:

  • how to test Jenkins builds locally
  • how to test CircleCI builds locally
  • how to test XXXX builds locally

And my solution for these problems is:

  • avoid functionalities provided by your CI tools (GitHub Actions, GitLab CI, etc.)
  • write as much as possible in a CI-agnostic way (Bash scripts, PowerShell scripts, Gradle scripts, NPM scripts, Dockerfiles, Ansible scripts - anything you know)
  • invoke those scripts from your CI tool. In GitHub Actions: run: your command to run

Bitbucket's Pipelines support is running locally, which means 100% free use-hours, with the cost of buying own PC/Mac (if you want a permanent server).

8
  • 47
    Actually gitlab has a cli tool to test your workflows: gitlab-runner exec docker my-job
    – Zia
    Commented Jan 31, 2020 at 0:12
  • 24
    Great answer! but unfortunately this doesn't help much when you are testing things like uploading and downloading artifacts to github releases.
    – dmedine
    Commented Aug 19, 2020 at 5:43
  • 4
    Circle CI also allows you to run locally: circleci.com/docs/2.0/local-cli Commented Sep 21, 2020 at 15:50
  • 6
    "CI-agnostic way" brings on caching challenges when using each specific CI caching capabilities Commented Oct 4, 2021 at 11:05
  • 6
    That's exactly what I have been doing for years now. The benefit of being in control and not relying on proprietary CI tooling far outweighs the benefit of being able to see little circles marked with checkmarks for each stage completed. CI tool vendors should provide hooks for our scripts to use to indicate stages completed. CI tool vendors should handle caching transparently to us; it is their problem, not ours.
    – Mike Nakis
    Commented May 24, 2022 at 11:52
86

One way to test GitHub Actions is to create a private repository, and iterate the actions configuration there. So you can avoid polluting the actual repository with broken commits.

I know, this is not a direct answer to the question; this is not a local way. But this didn’t occur to me at first and I think this could be enough for many use cases.

7
  • This is a handful when working with massive, multi-repo projects. Wish there was a better alternative but unfortunately this is the only guaranteed mirror functionality for testing.
    – m4heshd
    Commented Aug 23, 2021 at 7:32
  • 106
    Another possibility is creating a new branch in your repo, pushing changes to that branch until you get the action working, and then squash into a single commit and merge into main. Commented Nov 7, 2021 at 12:28
  • 5
    @JordanMitchellBarrett This sadly doesn't work when adding new workflows. Sometimes, the workflow YAML files have to exist on your mainline/default branch for them to show up in the actions UI. Commented May 10, 2023 at 13:20
  • 3
    @void.pointer This is true! What I do in this case is, I create a minimal new workflow in main/master so it shows up under GitHub Actions. And then after this, I create a new branch and continue my development and testing of the workflow til it works fully as I want it to.
    – John
    Commented May 17, 2023 at 9:50
  • 1
    related: stackoverflow.com/q/63480433, github.com/orgs/community/discussions/…
    – djvg
    Commented Nov 6, 2023 at 13:43
52

You can use nektos/act which supports yaml syntax since 0.2.0 (prerelease).

Check out their latest release.

5
  • 2
    but it does not work on .net/windows projects :( Commented Nov 2, 2022 at 20:45
  • @DanielWilliams any details on what doesn't work exactly? Is it act related?
    – Webber
    Commented Nov 2, 2022 at 20:50
  • I keep getting: "{Skipping unsupported platform -- Try running with -P windows-latest=... " But nothing is accepted as arguments to -P I tried this: -P ubuntu-latest=node:16-buster-slim but same error every time Commented Nov 4, 2022 at 12:15
  • @DanielWilliams would you mind reporting that? github.com/nektos/act/issues
    – Webber
    Commented Nov 5, 2022 at 12:03
  • 1
    Thanks, this is helpful. I noticed some differences between execution in GitHub Actions and act when running a python script. I noticed a script ran under GitHub Actions seem to be cached implicitly. This is probably more of GitHub Actions issue than anything, but I was able to catch this behavior thanks to nektos/act. Commented Nov 30, 2022 at 18:16
28

I'm assuming that you want to run the action locally because it is failing, and you want to debug it. If so, another alternative (which doesn't require running locally) is to use action-tmate to SSH into the machine running your action. From there, you can view logs, run commands, etc to work out what the problem is.

To get started:

  1. In your workflow yaml file, after the step that is failing (or at the end), put a new step as follows:
    - name: Setup tmate session
      if: success() || failure()
      uses: mxschmitt/action-tmate@v3
  1. Push the changes to GitHub and rerun the action.

  2. Wait for it to fail again - this time, instead of stopping the workflow, a tmate session will be opened, and the SSH details will be printed in the workflow console.

  3. Connect via SSH from your own machine, and now you have full access to the runner machine.

15

Your best bet is Act. However, (prior to 0.2.0) it doesn't support YAML syntax yet, though there is a lot of interest, AKA: Didn't work with YAML #80, YAML syntax support #76, and Support Actions v2 #74.

GitLab has gitlab-runner exec docker job-name, but that's GitLab :)

1
  • 3
    act supports yaml syntax since 0.2.0 (see Webber's answer)
    – Frank Rem
    Commented Jan 11, 2021 at 6:29
12

In my case, Act was failing even if the GitHub CI was passing, so I have found this:

GitHub Actions Runner

It is the official GitHub action runner (can be self-hosted). Follow the instructions in the README.

It is a little different than the ACT, but it allows, for example, to pair the repository CI to a local runner (can use CUDA, for example).

1
  • Thanks! This is actually super useful for CUDA as you said or even apple arm64 arch!
    – melMass
    Commented Jul 24, 2023 at 9:49
2

To add on top of what's being said by iirekm and @riQQ, in order to stay CI-agnostic and have some orchestration features, you could abstract your steps with Task and then call your tasks from your GitHub Actions or any other CI/CD.

That way, you also get the benefit of being able run everything locally.

2
  • There isn't anyone by the name "riQQ" here. What does it refer to? Commented Apr 9 at 12:47
  • OK, the OP may have left the building. Perhaps somebody else can chime in? Commented Apr 9 at 12:49
0

It's surprising that there is still no official way to do this. Act seems like a very good answer but I want to propose a couple of other solutions:

  1. Skaffold
  2. Earthly

Both these tools let you run you workflows locally and then you CICD can be just a single line command.

0

With the use of Docker containers, there is this tool called Act. You can run all your GitHub Actions stuff locally inside a docker container.

NB: Act builds all necessary containers for actions to run. all you do is follow the documentation on how to use the tool

1
-1

Another option is to put all the work into makefiles, and using GitHub Action(s) to call make.

That way, the jobs can be triggered to run locally via the make command, or remotely via a GitHub Action (also using the make command, but inside a GitHub Action).

I came across this approach mentioned here (one of the top comments on this thread).

2
  • 2
    This isn't really the same thing, though. This will cause all actions to run in the context of one container/shell process, which is fine for singular services, but once you add database+cache+backend+frontend, it doesn't work as well Commented May 27, 2023 at 4:50
  • 4
    Make is a build system, not a task runner. Commented Nov 18, 2023 at 11:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.