Local testing GitHub Actions

Last week, somewhere I recall someone (maybe @kzantow ) suggesting that there might be use in being able to iterate on, and debug GitHub actions locally, rather than wait for the GitHub runners. I suspect this was in the context of a bug, but this weekโ€™s brain has swapped out last weeks context, sorry.

Anyway, I just stumbled on act which may be of use. Itโ€™s a tool written in Go which does pretty much what you expect. It leverages your local Docker setup to spin up GHA-like containers in which to run the actions.

As a test I grabbed sbom-action to give it a shake. Iโ€™m using an M1 mac, so it recommended I add the --container-architecture linux/amd64 and pointed it at the test.yml action, thus:

act -W ./.github/workflows/test.yml --container-architecture linux/amd64

Any of you lot tried this before? Itโ€™s super quick (on my M3 Pro), feels pretty handy, and could speed up some iteration, assuming the tests work locally in docker.

Hereโ€™s the top of the output:

INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
[build-test/build                 ] ๐Ÿš€  Start image=catthehacker/ubuntu:act-latest
[build-test/test-as-action        ] ๐Ÿš€  Start image=catthehacker/ubuntu:act-latest
[build-test/test                  ] ๐Ÿš€  Start image=catthehacker/ubuntu:act-latest
[build-test/test-on-fixture-dirs-2] ๐Ÿšง  Skipping unsupported platform -- Try running with `-P ubuntu-latest=...`
[build-test/test-on-fixture-dirs-1] ๐Ÿš€  Start image=catthehacker/ubuntu:act-latest
[build-test/build                 ]   ๐Ÿณ  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
[build-test/test-as-action        ]   ๐Ÿณ  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
[build-test/test-on-fixture-dirs-1]   ๐Ÿณ  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
[build-test/test                  ]   ๐Ÿณ  docker pull image=registry:2 platform=linux/amd64 username= forcePull=true
[build-test/test                  ]   ๐Ÿณ  docker pull image=catthehacker/ubuntu:act-latest platform=linux/amd64 username= forcePull=true
[build-test/build                 ]   ๐Ÿณ  docker create image=catthehacker/ubuntu:act-latest platform=linux/amd64 entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"

Hereโ€™s the bottom of the output:

|  PASS  tests/integration/GitHubSnapshot.test.ts
|   GitHub Snapshot
|     โœ“ runs with default inputs (272 ms)
|
----------------------|---------|----------|---------|---------|---------------------------------------------------------------------------------------------------
| File                  | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s                                                                            
| ----------------------|---------|----------|---------|---------|---------------------------------------------------------------------------------------------------
| All files             |   88.11 |       75 |   91.42 |   88.33 |                                                                                              
|  src                  |     100 |      100 |     100 |     100 |                                                                                              
|   SyftVersion.ts      |     100 |      100 |     100 |     100 |                                                                                              
|  src/github           |      86 |    72.32 |   89.47 |    86.3 |                                                                                              
|   Executor.ts         |     100 |      100 |     100 |     100 |                                                                                              
|   GithubClient.ts     |   82.03 |    54.16 |   89.28 |   82.25 | 107-111,157,168-169,305,384,388-389,419-420,447,454-457,477-490                              
|   SyftGithubAction.ts |   87.89 |    77.27 |      92 |   88.18 | 65-66,124,134,148,193-194,205-210,220,246-247,289-290,420,429,433-436,509,535,545-546,563,590-593
|   Util.ts             |   66.66 |      100 |       0 |   66.66 | 4                                                                                            
|  tests                |    96.8 |       90 |   93.75 |   96.73 |                                                                                              
|   mocks.ts            |    96.8 |       90 |   93.75 |   96.73 | 90,155,208                                                                                   
| ----------------------|---------|----------|---------|---------|---------------------------------------------------------------------------------------------------
| Test Suites: 1 failed, 3 passed, 4 total
| Tests:       12 failed, 37 passed, 49 total
| Snapshots:   9 passed, 9 total
| Time:        18.588 s
| Ran all test suites.
[build-test/test                  ]   โŒ  Failure - Main npm test
[build-test/test                  ]   โš™  ::set-env:: ANCHORE_SBOM_ACTION_PRIOR_ARTIFACT=test-repo-a_job.cyclonedx.json
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ]   โš™  ::add-path:: /tmp/actions/cache/syft/1.9.0/x64
[build-test/test                  ] exitcode '1': failure
[build-test/test                  ] ๐Ÿ  Job failed

A useful tool for the armoury? Anyone else tried this?

(I did have to patch the workflow I used above, because port 5000 is in use by some macOS thing):

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 89aae24..48e53a9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -62,14 +62,14 @@ jobs:
       registry:
         image: registry:2
         ports:
-          - 5000:5000
+          - 5001:5000
     steps:
       - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
       - name: Build images
         run: |
           for distro in alpine centos debian; do
-            docker build -t localhost:5000/match-coverage/$distro ./tests/fixtures/image-$distro-match-coverage
-            docker push localhost:5000/match-coverage/${distro}:latest
+            docker build -t localhost:5001/match-coverage/$distro ./tests/fixtures/image-$distro-match-coverage
+            docker push localhost:5001/match-coverage/${distro}:latest
           done
       - run: npm ci
       - run: npm test

I had previously tried to set up act, and it seems to have remembered what images I was using, so I had to make a couple of changes to get this working. Specifically, I had to explicitly pass -P ubuntu-latest=catthehacker/ubuntu:act-latest or it was using to node:16-buster-slim which is both too old a Node and lacks the git cli.

I also had it hang on git diff and had to change the command to git --no-pager diff to prevent it from waiting for someone to page down in less I think.

Regarding port 5000, I think you might find that itโ€™s actually a previous test run that is using port 5000, which is something we probably want to fix. @popey if you docker ps, is there a registry:2 image listening on 5000?

When running this locally, I usually see a successful test run followed by a cryptic failure. For example, Error: Job 'build' failed. (Iโ€™ve seen other job failures as well, such as Error: Job 'test-as-action' failed. Iโ€™m not sure what to make of these failures being printed later. Is that othersโ€™ experience with this test setup? It would be nice if it ran the whole workflow and exited zero :slight_smile:

I was also trying to make local and GHA testing of sbom-action more similar, and had opened a small PR, but GHA runs the local registry as a service, rather than as an imperative part of the tests, which might be preferable for a reason Iโ€™m not thinking of.

1 Like

Would you believe, itโ€™s AirPlay Receiver thatโ€™s taking port 5000!

vs

:exploding_head: