GitHub Actions 로컬 테스트 방법 (with act)
GitHub Actions를 사용하다 보면, 매번 Commit하고 Push하지 않고도 로컬 환경에서 워크플로우가 정상적으로 동작하는지 간단하게 확인하고 싶을 때가 있습니다. 이런 경우 act라는 도구를 활용하면 로컬에서 GitHub Actions 워크플로우를 직접 실행해볼 수 있습니다. 이번 글에서는 act를 사용하여 로컬 환경에서 GitHub Actions 워크플로우를 테스트하는 방법에 대해 소개하겠습니다.
단, act는 간단한 구조의 워크플로우 테스트에 적합하며, 복잡한 조건 분기나 다양한 OS 환경, self-hosted runner, 외부 서비스와의 연동 등 복잡한 구성에서는 한계가 있을 수 있습니다. 따라서 기본적인 기능 확인이나 단순 로직 테스트를 목적으로 사용할 것을 권장드립니다.
또한 act는 내부적으로 Docker 컨테이너를 이용하여 워크플로우를 실행하므로, 사전에 로컬 환경에 Docker가 설치되어 있어야 합니다. Docker가 실행 중이지 않거나 설치되어 있지 않다면, act를 제대로 사용할 수 없습니다.
act 설치히기
MacOS와 Ubuntu별로 설치하는 방법이 다릅니다. 참고로 필자는 Ubuntu에서 실습을 진행하였습니다.
# MacOS 환경에 act 설치하기
> brew install act
# Ubuntu 환경에 act 설치하기
> curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
# 결과
nektos/act info checking GitHub for latest tag
nektos/act info found version: 0.2.79 for v0.2.79/Linux/x86_64
nektos/act info installed ./bin/act
# 실행 파일 옮기기
> sudo mv ./bin/act /usr/local/bin/
# 설치 확인
> act --version
act version 0.2.79
테스트를 위한 workflow 작성
.github/workflows/test-workflow.yaml
name: "Test act workflow"
on:
workflow_dispatch:
jobs:
job-one:
name: Job One
runs-on: ubuntu-latest
steps:
- name: Step 1 - Job One
run: |
echo "Job One - Step 1 시작"
sleep 5
echo "Job One - Step 1 완료"
- name: Step 2 - Job One
run: |
echo "Job One - Step 2 시작"
sleep 5
echo "Job One - Step 2 완료"
job-two:
name: Job Two
runs-on: ubuntu-latest
steps:
- name: Step 1 - Job Two
run: |
echo "Job Two - Step 1 시작"
sleep 5
echo "Job Two - Step 1 완료"
- name: Step 2 - Job Two
run: |
echo "Job Two - Step 2 시작"
sleep 5
echo "Job Two - Step 2 완료"
act 명령어 수행
act의 간단한 사용법을 정리하였습니다. 더 자세한 사용법은 "act --help" 를 통해 확인하시기 바랍니다.
# act 사용법
act --help
# 수행 가능한 모든 잡 출력하기
act --list
act --list | grep test-workflow
# 특정 workflow만 동작하기
act --workflows .github/workflows/test-workflow.yaml workflow_dispatch
act 수행 결과
> act --workflows .github/workflows/test-workflow.yaml workflow_dispatch
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
[Test act workflow/Job Two] ⭐ Run Set up job
[Test act workflow/Job Two] 🚀 Start image=catthehacker/ubuntu:act-latest
[Test act workflow/Job One] ⭐ Run Set up job
[Test act workflow/Job One] 🚀 Start image=catthehacker/ubuntu:act-latest
[Test act workflow/Job One] 🐳 docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Test act workflow/Job Two] 🐳 docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Test act workflow/Job Two] 🐳 docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test act workflow/Job One] 🐳 docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test act workflow/Job One] 🐳 docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test act workflow/Job Two] 🐳 docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test act workflow/Job One] 🐳 docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Test act workflow/Job Two] 🐳 docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Test act workflow/Job Two] ✅ Success - Set up job
[Test act workflow/Job One] ✅ Success - Set up job
[Test act workflow/Job One] ⭐ Run Main Step 1 - Job One
[Test act workflow/Job Two] ⭐ Run Main Step 1 - Job Two
[Test act workflow/Job One] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/0] user= workdir=
[Test act workflow/Job Two] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/0] user= workdir=
| Job Two - Step 1 시작
| Job One - Step 1 시작
| Job Two - Step 1 완료
[Test act workflow/Job Two] ✅ Success - Main Step 1 - Job Two [5.128980331s]
| Job One - Step 1 완료
[Test act workflow/Job One] ✅ Success - Main Step 1 - Job One [5.134181219s]
[Test act workflow/Job One] ⭐ Run Main Step 2 - Job One
[Test act workflow/Job Two] ⭐ Run Main Step 2 - Job Two
[Test act workflow/Job One] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/1] user= workdir=
[Test act workflow/Job Two] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/1] user= workdir=
| Job One - Step 2 시작
| Job Two - Step 2 시작
| Job One - Step 2 완료
[Test act workflow/Job One] ✅ Success - Main Step 2 - Job One [5.117976151s]
| Job Two - Step 2 완료
[Test act workflow/Job Two] ✅ Success - Main Step 2 - Job Two [5.121284792s]
[Test act workflow/Job One] ⭐ Run Complete job
[Test act workflow/Job One] Cleaning up container for job Job One
[Test act workflow/Job Two] ⭐ Run Complete job
[Test act workflow/Job Two] Cleaning up container for job Job Two
[Test act workflow/Job One] ✅ Success - Complete job
[Test act workflow/Job One] 🏁 Job succeeded
[Test act workflow/Job Two] ✅ Success - Complete job
[Test act workflow/Job Two] 🏁 Job succeeded
컨테이너 확인
위에서 소개한 방법으로 act를 이용해 워크플로우를 실행하면, 각 job이 Docker 컨테이너로 실행되는 것을 확인할 수 있습니다. 예를 들어 두 개의 job이 정의된 워크플로우를 실행했다면, act는 각각의 job을 개별 Docker 컨테이너로 실행합니다.
실행 중인 컨테이너는 다음 명령어를 통해 확인할 수 있습니다:
watch “docker ps -a” 이 명령어를 입력하면 현재 실행 중이거나 종료된 Docker 컨테이너 목록이 출력되며, act가 실행한 job들이 컨테이너로 존재하는 것을 확인할 수 있습니다. 컨테이너 이름에는 보통 `act-` 접두사가 포함되어 있으므로 식별하기도 비교적 쉽습니다.

상세 기능 사용하기
이제는 좀 더 Act의 상세 기능을 사용하기 위해 다음과 같이 workflow를 수정하였습니다.
name: "Test Act workflow"
on:
workflow_dispatch:
env:
SLEEP_TIME: ${{ vars.SLEEP_TIME }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
jobs:
job-one:
name: Job One
runs-on: ubuntu-latest
steps:
- name: Step 1 - Job One
run: |
echo "Job One - Step 1 시작"
sleep $SLEEP_TIME
echo "Sleep Time: $SLEEP_TIME"
- name: Step 2 - Job One
run: |
echo "Job One - Step 2 시작"
sleep $SLEEP_TIME
echo "Sleep Time: $SLEEP_TIME"
job-two:
name: Job Two
needs: job-one
runs-on: ubuntu-custom
steps:
- name: Step 1 - Job Two
run: |
echo "Test Password: $TEST_PASSWORD"
Values와 Secret 사용하기
GitHub Actions 워크플로우를 작성할 때 보안이 필요한 secrets나 공통 설정값인 vars는 필수적인 요소입니다. 보통 GitHub 리포지토리 설정에서 이를 관리하지만, 로컬 실행 도구인 act는 원격의 설정에 직접 접근할 수 없다는 제약이 있습니다. 이를 해결하기 위해 act는 --secret-file 및 --var-file 옵션을 지원하며, 이를 통해 로컬 파일에 정의된 값을 워크플로우에 주입할 수 있습니다.
.values 파일
SLEEP_TIME=5
.secret 파일
TEST_PASSWORD=admin1234
나만의 Docker image 사용하기
act는 기본 제공 이미지 외에도 사용자가 직접 빌드한 커스텀 Docker 이미지를 활용할 수 있는 유연성을 제공합니다. -P 옵션을 사용하면 워크플로우의 runs-on 설정에 맞춰 로컬 환경의 특정 Docker 이미지를 매핑하여 작업을 수행할 수 있습니다. 이를 통해 실제 운영 환경과 가장 유사한 아티팩트나 라이브러리가 포함된 환경에서 테스트를 진행할 수 있습니다.
act --workflows .github/workflows/test-workflow.yaml workflow_dispatch \
--secret-file .secret \
--var-file .value \
--pull=false \
-P ubuntu-custom=my-custom:latest
결과
> act --workflows .github/workflows/test-workflow.yaml workflow_dispatch \
--secret-file .secret \
--var-file .value \
--pull=false \
-P ubuntu-custom=my-custom:latest
INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock'
[Test Act workflow/Job One] ⭐ Run Set up job
[Test Act workflow/Job One] 🚀 Start image=catthehacker/ubuntu:act-latest
[Test Act workflow/Job One] 🐳 docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=false
[Test Act workflow/Job One] 🐳 docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test Act workflow/Job One] 🐳 docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test Act workflow/Job One] 🐳 docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Test Act workflow/Job One] ✅ Success - Set up job
[Test Act workflow/Job One] ⭐ Run Main Step 1 - Job One
[Test Act workflow/Job One] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/0] user= workdir=
| Job One - Step 1 시작
| Sleep Time: 5
[Test Act workflow/Job One] ✅ Success - Main Step 1 - Job One [5.128447773s]
[Test Act workflow/Job One] ⭐ Run Main Step 2 - Job One
[Test Act workflow/Job One] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/1] user= workdir=
| Job One - Step 2 시작
| Sleep Time: 5
[Test Act workflow/Job One] ✅ Success - Main Step 2 - Job One [5.12061753s]
[Test Act workflow/Job One] ⭐ Run Complete job
[Test Act workflow/Job One] Cleaning up container for job Job One
[Test Act workflow/Job One] ✅ Success - Complete job
[Test Act workflow/Job One] 🏁 Job succeeded
[Test Act workflow/Job Two] ⭐ Run Set up job
[Test Act workflow/Job Two] 🚀 Start image=my-custom:latest
[Test Act workflow/Job Two] 🐳 docker pull image=my-custom:latest platform= username= forcePull=false
[Test Act workflow/Job Two] 🐳 docker create image=my-custom:latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test Act workflow/Job Two] 🐳 docker run image=my-custom:latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[Test Act workflow/Job Two] 🐳 docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Test Act workflow/Job Two] ✅ Success - Set up job
[Test Act workflow/Job Two] ⭐ Run Main Step 1 - Job Two
[Test Act workflow/Job Two] 🐳 docker exec cmd=[bash -e /var/run/act/workflow/0] user= workdir=
| Test Password: ***
[Test Act workflow/Job Two] ✅ Success - Main Step 1 - Job Two [120.174762ms]
[Test Act workflow/Job Two] ⭐ Run Complete job
[Test Act workflow/Job Two] Cleaning up container for job Job Two
[Test Act workflow/Job Two] ✅ Success - Complete job
[Test Act workflow/Job Two] 🏁 Job succeeded
'git & github' 카테고리의 다른 글
| git hook을 이용한 commit 메시지에 자동으로 branch명 추가하기 (0) | 2025.05.22 |
|---|---|
| 주니어 개발자가 알면 좋은 git 명령어 정리 (1) | 2023.07.12 |
| git config (0) | 2023.07.11 |
| git commit template 설정하기 (0) | 2023.07.10 |