기본적으로 github action은 docker container를 띄워 실행하기 때문에, 우리의 repo를 가져오지 않는다. 따라서, github action에서 우리의 code를 가져와서 특정 작업을 시켜주기 위해서는 repo를 github action에 다운로드 시켜주는 기능이 필요한데, 이 기능이 가능하도록 하는 action이 바로 checkout
이다
github repository를 가져와 작업을 수행하는 것으로, github marketplace에 정의된 공식 action이다. 이전에 말했듯이 action을 사용하기 위해서는 job
의 step 중에 run
대신에 uses
를 사용하면 된다.
name: checkout
on: workflow_dispatch
jobs:
no-checkout:
runs-on: ubuntu-latest
steps:
- name: check file list
run: cat README.md
checkout:
runs-on: ubuntu-latest
steps:
- name: user checkout action
uses: actions/checkout@v4
- name: check file list
run: cat README.md
workflow_dispatch
event이므로 workflow를 수동으로 실행시켜주어야 한다.
no-checkout
의 경우는 README.md
가 없기 때문에 에러를 반환하게 될 것이다. user checkout action
의 경우에는 actions/checkout@v4
이므로 repo에 모든 file들을 가져오게 된다. 우리의 경우 README.md
file도 가져오게 되어 cat
실행시에 문제없이 구동된다.
echo "hell world" >> ./README.md
git add ./README.md
git commit -m "feat: update README.md"
git push origin main
이 다음 github에 들어가서 workflow를 실행시키면 첫번째 job인 no-checkout
는 실패하지만 checkout
은 성공하는 것을 볼 수 있다.
context는 workflow를 실행하는 환경에 대한 정보를 말한다.
context는 다양한 정보들을 가지는데, github
context는 repository이름, owner이름, branch, tag 이름 등도 확인할 수 있다.
github context를 사용해서 dev
branch에 push
event가 발생하면 workflow에서 1번 job을 실행하도록 할 수 있고, master
branch에서 push
event가 발생하면 workflow에서 2번 job을 실행하도록 수 있다.
단, 오해하지 말것이 context
는 "context"
라는 이름 자체를 가진 객체가 아니다. 아래의 링크를 참고하면 context
객체들의 구체적인 타입들이 있다.
대표적인 context
들은 다음과 같다.
1. github
2. end
3. vars
4. job
5. jobs
context
를 사용하기 위해서는 ${{ <context> }}
방법으로 step에서 사용하면 된다. 만약 github
context를 사용하고 싶다면 ${{ github }}
이렇게 사용한다.
name: context
on: workflow_dispatch
jobs:
context:
runs-on: ubuntu-latest
steps:
- name: github context
run: echo '${{ toJSON(github) }}'
context
객체 자체는 javascript object이기 때문에 json 문자열로 바꾸어주기 위해서는 toJSON
을 사용해주어야 한다. 출력 결과로 쭉 나올 것이다.
{
"token": "***",
"job": "context",
...
"action_ref": ""
}
이 중 하나를 가지고 실행해보도록 하자.
name: context
on: workflow_dispatch
jobs:
context:
runs-on: ubuntu-latest
steps:
- name: github context
run: echo '${{ toJSON(github) }}'
- name: check github context
run: |
echo ${{ github.repository }}
echo ${{ github.event_name }}
실행 결과를 보면 github.repository
와 github.event_time
의 결과가 나올 것이다.
event가 특정 조건에 부합할 때 실행하도록 한다. 이를 통해서 workflow 실행을 더욱 효과적으로 제어가 가능하다.
branch filter는 event아래에 branch, path, tag에 적용이 가능한데 filter를 통해 특정 branch, 특정 path, 특정 tag에서 실행가능하도록 할 수 있다.
가령, 특정 branch에서 실행하는 경우인데, dev, master branch 중에서 master branch로 push해야만 실행하도록 할 수 있다.
name: branch-filter
on:
push:
branches: ["dev"]
jobs:
branch-filter:
runs-on: ubuntu-latest
steps:
- name: echo
run: echo hello
위는 push
event에 대해서 dev
branch일 때만 실행하도록 만든 것이다.
git push origin main
으로 해놓으면 실행이 안되는 것을 볼 수 있다.
git push origin dev
로 해보도록 하자.
git switch -c dev
git add .
git commit -m "add branch_filter"
git push origin dev
dev
branch에서는 branch-filter
worflow가 동작하는 것을 볼 수 있다.
특정 directory path에서 변화가 감지되어야 실행되도록 한다. 가령 my-app
이라는 directory 내에 file이 update될 때만 실행된다.
먼저 새로운 directory와 file을 만들어보도록 하자.
mkdir -p foo/poo/
echo "hello" >> ./foo/poo/hello.txt
echo "SECRET_KEY=HELLO" >> ./foo/poo/.env
다음으로 foo/poo/
path에 변화가 생기면 event를 실행하도록 하는 path filter를 만들도록 하자.
name: path-filter
on:
push:
paths:
- 'foo/poo/*'
- '!foo/poo/.env'
jobs:
path-filter:
runs-on: ubuntu-latest
steps:
- name: echo hello
run: echo hello
event아래에 path
를 나열하면 된다. foo/poo/*
는 해당 path에 대한 모든 file들의 변화가 있을 경우 실행한다는 의미이고, !foo/poo/.env
는 .env
의 변화에 대해서는 감지하지 말라는 것이다.
push하면 foo/poo/
에 파일이 생겼으므로 실행된다.
다음으로 .env
파일을 수정하고 넣어보자.
echo "SECRET_KEY=WORLD" >> ./foo/poo/.env
path-filter
workflow가 실행되지 않을 것이다.
특정 tag 패턴일 때만 event를 실행한다. 가령 v1.0.0
으로 태깅해야 실행하도록 할 수 있다. 단 tag filter는 push
event에서만 사용이 가능하다.
name: tag-filter
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+' # v1.0.0 or v2.2.2, v1.0이나 1.0.0은 filter에 안걸린다.
jobs:
tag-filter:
runs-on: ubuntu-latest
steps:
- name: echo
run: echo hello
git push
로 github에 넣어둔 다음에 github action을 확인하면 별다르게 실행된 것이 없다. git tag
를 통해서 tag를 만들어보도록 하자.
git tag v0.0.0
git push origin v0.0.0
workflow가 생성되어 실행되는 것을 볼 수 있다.
이번에는 pattern에 맞지 않는 tag를 만들어보도록 하자.
git tag 0.0.0
git push origin 0.0.0
github action에 가보면 workflow가 생성되어 있지 않는 것을 볼 수 있다.
특정 시간 이상 실행되면 자동으로 workflow를 중단되도록 설정하는 기능이다. 이를 통해 특정 job, 특정 step이 무한 루프에 걸리지 않도록 하여, 불필요한 자원 소모를 방지한다. default로 360분이다.
timeout이 사용할 수 있는 곳은 다음이다.
1. job-level: 이 job이 timeout 내에 실행되어야 한다.
2. step-level: 각 step마다 timeout을 두어 timeout 내에 실행되어야 한다.
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 3 # 3분 job-level 정의
steps:
- name: loop
run: echo hello
timeout-minutes: 1 # 1분 step-level
- name: echo
run: echo "hello world"
위와 같이 job-level, step-level의 timeout 설정이 가능하다.
이제 timeout에 대한 workflow를 하나 만들어보도록 하자.
name: timeout
on: push
jobs:
timeout:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: loop
run: |
count=0
while true; do
echo "seconds: $count"
count=$((count+1))
sleep 1
done
- name: echo
run: echo hello
위 timeout workflow는 첫번째 step인 loop
에서 무한 반복되기 때문에, 두번째 step인 echo
가 동작하지 않는다. 그래서 2분 뒤에 job-level timeout인 timeout-minutes
2분 후에 종료된다.
name: timeout
on: push
jobs:
timeout:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: loop
run: |
count=0
while true; do
echo "seconds: $count"
count=$((count+1))
sleep 1
done
timeout-minutes: 1
- name: echo
run: echo hello
다음으로 loop
에 대해서 timeout-minutes
1분을 주어 결과를 확인해보도록 하자.
1분 후로 넘어가면 loop
step이 timeout되어 workflow가 종료된다.
자주 사용되는 데이터를 빠르게 불러올 수 있도록 저장하는 action이다. 주로 의존성 설치 시간 단축에 사용되며, github marketplace에 정의된 공식 action이다.
test를 위해서 react
app을 설치하도록 하자.
npx create-react-app my-app
cd my-app
npm start
npm start
명령어까지 치면 라이브러리를 모두 설치한 것이다.
다음으로 cache workflow를 만들어보도록 하자.
name: cache
on:
push:
paths:
- 'my-app/**' # my-app아래의 모든 파일을 포함
jobs:
cache:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup-node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Cache Node.js modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: |
cd my-app
npm ci
- name: npm build
run: |
cd my-app
npm run build
checkout@v4
action을 사용하여 repo를 가져온다.runner.os
로 os image 이름을 가져오고, hashFiles
를 통해서 package-lock.json`을 해싱한다.my-app
에 npm ci를 실행맨 처음에 git push
를 해보면 cache를 할 것이 없어서 Cache not found
가 발생하지만, 두 번쨰부터는 caching 제대로 동작하는 것을 볼 수 있다.
workflow 실행 중 생성된 file 또는 file 모음이다. 이는 workflow가 끝나고 나온 결과물을 사용자가 얻기 위해서 artifact를 이용하는 것이다. 또한, 동일한 workflow 내에서 job 사이에 데이터 공유가 가능하고, workflow 종료된 이후에도 데이터를 유지한다. (90일)
github merketplace 공식 액션으로 다음이 있다.
1. upload-artifact
2. download-artifact
upload-artifact
로 저장했다가, download-artifact
로 다른 job에서 다운받으면 된다.
name: artifact
on: push
jobs:
upload-artifact:
runs-on: ubuntu-latest
steps:
- name: echo
run: echo hello-world > hello.txt
- name: upload artifact
uses: actions/upload-artifact@v3
with:
name: artifact-test
path: ./hello.txt
download-artifact:
runs-on: ubuntu-latest
needs: [upload-artifact]
steps:
- name: download artifact
uses: actions/download-artifact@v3
with:
name: artifact-test
path: ./
- name: check
run: cat hello.txt
upload-artifact
job의 echo
step으로 hello.txt
를 만들고 upload artifact
step으로 hello.txt
file을 업로드한다. download-artifact
job의 download artifact
step에서 download-artifact
를 사용하여 다른 job에 있는 hello.txt
를 받을 수 있다. 단, 이들은 파일 생성 시점에 대한 의존 관계가 있으므로 needs
로 upload-artifact
를 먼저 실행하도록 해야한다.
github action의 결과로 artifacts가 나오며, 해당 file을 저장할 수 있다.
한 job에서 생성된 데이터를 동일한 job의 step 또는 다른 job에 데이터를 쉽게 공유할 수 있다. 즉, 여러 step과 job간에 데이터를 손쉽게 전달이 가능하다. 이는 artifact처럼 결과 산출물을 얻기 위함이 아니라, job과 step 사이에 데이터를 공유하기 위함이다.
artifact는 또한, 파일 또는 파일 모음으로 데이터 공유가 가능한데, output은 단순 값 형태로 데이터를 저장하며 key-value 형식을 사용한다. 사용 방법도 간단한데, 다음과 같이 사용하면 된다.
echo "{key}={value}" >> $GITHUB_OUTPUT
가령 key
가 name
이고 value
가 github-actions
라면 다음과 같다.
echo name=github-actions >> $GITHUB_OUTPUT
output을 사용하려면 output이 정의된 고유한 ID를 사용해야한다.
name: output
on: push
jobs:
create_output:
runs-on: ubuntu-latest
steps:
- name: echo output
id: check-output
run: echo "test=hello" >> "$GITHUB_OUTPUT"
- name: check output
run: |
echo ${{ steps.check-output.outputs.test }}
echo output
step의 test
output을 사용하기 위해서는 id를 사용해야한다. 형식은 다음과 같다.
${{ steps.{step_id}.outputs.{key} }}
위는 같은 job에서 output을 사용하는 방법이었고, 다른 job에서 output을 사용하려면 needs
와 job-level outputs
을 사용해야한다.
name: output
on: push
jobs:
create-output:
runs-on: ubuntu-latest
outputs: # job level output
test: ${{ steps.check-output.outputs.test }}
steps:
- name: echo output
id: check-output
run: | # step level output
echo "test=hello" >> "$GITHUB_OUTPUT"
- name: check output
run: |
echo ${{ steps.check-output.outputs.test }}
get-output:
needs: [create-output]
runs-on: ubuntu-latest
steps:
- name: get output
run: |
echo ${{ needs.create-output.outputs.test }}
step인 echo output
에서 만든 output을 job인 create-output
에서 가져와 get-output
에 건내줄 수 있다. 이때 종속 관계가 있어야하는데, get-output
이 create_output
에 의존하고 있는 것이다.
이때 다른 job에서 해당 output을 사용하려면 다음과 같은 형식을 따라야한다.
${{ needs.{job-name}.outputs.{outputs-key} }}
단, outputs-key
는 job-level의 outputs
하위에 있어야 하는 key이다.
이제 github에 push하고 결과를 확인해보도록 하자.