GitHub Actions를 사용하면 GitHub 저장소의 변경 사항에 대해 자동으로 작업을 실행하고 테스트, 빌드, 배포 등 다양한 작업을 자동화할 수 있다.
GitHub Actions는 **워크플로우(Workflow)**라고 불리는 YAML 파일로 정의된다. 이 워크플로우 파일은 저장소의 특정 이벤트(예: push, pull request 등)가 발생했을 때 실행되어야 하는 작업들을 정의한다. 작업은 **여러 단계(steps)**로 구성되며, 각 단계는 명령(command) 또는 **액션(action)**으로 구성된다.
**액션(action)**은 재사용 가능한 작업 단위로, 작업의 실행 단위로 간주된다. 예를 들어, 코드 테스트, 빌드, 컨테이너 이미지 작성 등과 같은 작업은 각각 해당하는 액션을 사용하여 구성할 수 있다. GitHub Actions는 커뮤니티에서 제공하는 다양한 액션들도 활용할 수 있다.
GitHub Actions를 사용하면 개발자는 소프트웨어의 품질과 안정성을 향상시키기 위해 테스트 자동화, 지속적인 통합, 배포 자동화 등을 수행할 수 있다. 또한, 워크플로우를 사용하여 프로젝트의 특정 작업을 자동화하고 이를 팀원과 공유하여 협업을 간편하게 할 수도 있다.
# test-every-push.yml
# workflow의 이름. 나중에 해당 값을 사용할 수 있기 때문에 저는 유니크하게 사용합니다.
name: 'test-every-push'
# workflow를 동작하게하는 trigger입니다.
# repository에 push 이벤트가 발생할 때마다 실행될 거예요.
# push 말고도 여러 가지 이벤트들이 있겠죠?
on: push
# job은 사용자가 정한 플랫폼을 통해 step이라는 일련의 과정을 실행할 수 있어요.
# 여러 개의 job을 사용할 수 있으며, 여러 개의 job을 사용할 때는 서로 정보도 교환할 수 있어요.
# 그리고 각각 독립적으로도 실행할 수도 있어요.
# 해당 예제는 간단한 workflow이므로 하나의 job만 갖도록 할게요.
jobs:
test:
# job의 이름을 정해줍니다.
name: Test lint, tsc, build
# 저는 해당 job을 리눅스 환경에서 사용할 거예요. 다른 플랫폼이 올 수도 있겠죠?
runs-on: ubuntu-latest
# job 안에는 step이라는 키워드가 옵니다. step은 shell script를 실행할 수도 있고,
# 누군가 만들어 놓은 Action을 사용할 수도 있어요.
steps:
# GitHub Actions는 해당 프로젝트를 리눅스 환경에 checkout하고 나서 실행을 합니다.
# 마치 우리가 브랜치를 만들 때 checkout하는 것처럼요. 꼭 필요합니다.
# 참고로 아래 코드는 누군가 만들어놓은 Action을 사용하는 겁니다.
# 만들어놓은 Action을 사용할 때는 uses라는 키워드를 사용해야 돼요.
- uses: actions/checkout@v2
# 해당 환경을 Node.js 위에서 실행하겠다고 명시해줍니다.
# 저희 프로젝트는 리액트니깐요!
# 마찬가지로 누군가 만들어 놓은 Action이겠죠?
- name: Use Node.js
uses: actions/setup-node@v2
# with라는 키워드로 Action에 값을 전달할 수 있어요.
# 이 Action은 node-version이라는 값을 받을 수 있네요?
# 아까 NODE_VERSION이라는 Secret을 만들었는데요,
# ${{ secrets.XXX }}라는 값으로 GitHub의 Secrets 값을 가져올 수 있어요.
# node-version은 16.13.1이 되겠죠?
with:
node-version: ${{ secrets.NODE_VERSION }}
# push할 때마다 npm을 install 해야될까요? (시간이 여간 많이 걸리는 게 아닐 텐데 ..)
# 아닙니다. 해당 프로젝트의 node_modules가 변했는지 안 변했는지를 이용해서
# 모듈 변화가 있을 때만 npm install을 해줄 수도 있어요.
- name: Cache node modules
# 그걸 제공하는 Action도 있네요? 갖다 쓰겠습니다.
uses: actions/cache@v2
# 해당 step을 대표하는 id를 설정할 수도 있어요.
# 해당 값은 뒤의 step에서 사용해볼게요.
id: cache
with:
# node_modules라는 폴더를 검사하여
path: node_modules
# 아래 키값으로 cache가 돼있는지 확인합니다.
key: npm-packages-${{ hashFiles('**/package-lock.json') }}
- name: Install Dependencies
# 위 step에서 node_modules에 대한 cache 검사를 했잖아요?
# 만약 모듈에 변한 게 있다면 `npm install`을 실행하고 아니면 해당 step을 건너뛰게 됩니다.
# if 키워드는 해당 스텝을 실행할지 말지를 결정할 수 있는 키워드예요.
# `steps.cache.outputs.cache-hit`이 값은 무엇일까요?
# 위 step에서 정했던 cache라는 id를 steps.cache로 가져올 수 있어요.
# cache라는 id 값을 가진 step에서는 cache-hit라는 output을 내뱉네요?
# 그걸로 cache가 hit 됐는지 안 됐는지를 알 수 있나봐요!
# 그 값이 true가 아닐 때만 npm install을 하겠죠?
if: steps.cache.outputs.cache-hit != 'true'
run: npm install
# 아래 로직은 저희가 만든 프로젝트의 lint, tsc, build를 테스트하는 곳이에요.
# run 키워드는 `npm start`같이 커맨트 명령어를 입력할 수 있게 해주는 키워드예요.
# `npm run xxx`를 사용하려면 pacakge.json에 해당 명령어에 대한 정의가 필요하겠죠?
# 저는 간단하게 아래 `사진-4`처럼 2개만 만들어봤어요.(실제로는 테스트 항목이 더 많고, 테스트를 실행할 수 있는 코드가 들어가야겠죠?)
- run: npm run lint
# `if: ${{ always() }}`라는 문법은 무엇일까요?
# 만약 `npm run lint`라는 곳에서 에러가 났다고 칩시다.
# 그러면 뒤의 tsc, build를 실행하지 않고 해당 workflow가 끝나버리게 됩니다.
# 뒤의 tsc, build에서도 에러가 있을지도 모르는데 말이죠.
# 하지만 ${{ always() }}라는 문법을 사용한다면
# `npm run lint`라는 곳에서 에러가 나도 뒤의 tsc, build까지 다 실행을 해보고 난 뒤 종료합니다.
# 그래서 모든 테스트 스크립트에 ${{ always() }}를 붙여줍니다.
if: ${{ always() }}
- run: npm run tsc
if: ${{ always() }}
- run: npm run build
if: ${{ always() }}
참고 및 출처 : https://fe-developers.kakaoent.com/2022/220106-github-actions/