Git Workflow 문제 해결 - 보일러 템플레이트
본 포스트에서는 GitHub Actions의 워크플로우를 활용해 프로젝트 관리를 어떻게 더 효율적으로 할 수 있는지에 대한 실제 경험을 공유하고자 합니다
GitHub Actions는 GitHub에서 직접 제공하는 CI/CD(Continuous Integration/Continuous Delivery) 도구로, 코드 통합부터 배포까지의 전 과정을 자동화할 수 있습니다. 복잡한 스크립트나 외부 서비스 없이도 GitHub 리포지토리 내에서 직접 이러한 프로세스들을 관리할 수 있는 것이 큰 장점입니다.
최근 프로젝트 리팩토링을 하며 GitHub의 vite-electron-builder
보일러플레이트를 도입했을 때, 그 중 GitHub Actions 설정이 인상적이었습니다. 프로젝트 빌드, 테스트 실행, 린트 및 코드 스타일 검사, 그리고 자동 릴리즈까지 한 번에 처리할 수 있는 워크플로우를 사용하니 개발자로써 고민해야 하는 영역이 줄어들어 편했습니다.
기본 개념
본격적인 Workflow 설명에 앞서 기본 개념을 짚고 넘어가겠습니다
Workflow (워크플로우)
- 정의: 워크플로우는 개발 프로세스를 자동화하는 데 사용되는 설정입니다. 하나의 YAML 파일로 구성되어, 특정 이벤트에 의해 트리거되면 실행되는 일련의 작업을 정의합니다.
- 구성:
.github/workflows
디렉토리에 저장되며, 하나 이상의 작업(Job)으로 구성됩니다.
Event (이벤트)
- 정의: 이벤트는 워크플로우를 실행시키는 트리거입니다. 코드 푸시, 풀 리퀘스트 생성, 정해진 시간에 따른 스케줄링(cron), 외부 시스템의 웹훅과 같은 다양한 이벤트가 워크플로우를 시작할 수 있습니다.
Job (작업)
- 정의: 작업은 워크플로우 내에서 실행되는 일련의 단계(Steps)입니다. 각 작업은 별도의 가상 환경에서 실행되며, 서로 독립적으로 실행될 수도 있고, 다른 작업들에 의존하여 순차적으로 실행될 수도 있습니다.
Action (액션)
- 정의: 액션은 재사용 가능한 작업 단위입니다. GitHub에서 제공하는 액션을 사용할 수도 있고, 커뮤니티에서 공유하는 액션을 활용하거나 직접 만들어 사용할 수도 있습니다.
- 용도: 액션을 통해 커맨드 실행, 코드 체크아웃, 의존성 설치 등의 작업을 수행할 수 있습니다.
Step (단계)
- 정의: 단계는 작업 내에서 실행되는 개별 명령어 또는 액션을 의미합니다. 각 단계는 순차적으로 실행되며, 액션 호출 또는 쉘 스크립트 실행을 통해 구체적인 작업을 수행합니다.
Workflow 설명
GitHub Actions의 워크플로우는 .yml
형식의 파일로 정의됩니다. 이 파일 내에서 개발자는 워크플로우가 어떠한 이벤트에 의해 트리거될지, 어떤 작업을 수행할지 등을 세밀하게 지정할 수 있습니다.
CI 프로세스 진입점 Workflow
on:
workflow_dispatch: # 수동 트리거를 위해 GitHub UI에서 이 워크플로우를 시작할 수 있습니다.
push:
branches:
- main # main 브랜치에 푸시될 때
- 'renovate/**' # renovate로 시작하는 모든 브랜치명을 가진 브랜치에 푸시될 때
워크플로우의 첫 번째 단계는 이벤트에 의해 트리거되는 조건을 명시하는 것입니다. workflow_dispatch
를 통해 수동으로 워크플로우를 시작할 수 있는 옵션을 제공함으로써, 개발자는 필요할 때 언제든지 CI 프로세스를 실행할 수 있습니다. push
이벤트는 main
브랜치와 renovate/**
브랜치에 코드가 푸시될 때 자동으로 워크플로우를 실행하도록 설정되어 있으며, 이를 통해 코드 변경 사항이 중요한 브랜치에 반영될 때마다 지속적인 통합 프로세스가 유지됩니다.
paths-ignore
설정은 특정 경로의 변경사항이 있을 때 워크플로우가 실행되지 않도록 함으로써, 불필요한 실행을 방지하고 리소스를 절약합니다. 예를 들어, Markdown 파일이나 개발 환경 설정 파일의 변경은 CI 프로세스에 영향을 미치지 않으므로, 이러한 파일의 변경을 무시함으로써 워크플로우의 실행을 최적화할 수 있습니다.
paths-ignore:
- '.github/**'
- '**.md'
concurrency
설정은 동일한 레퍼런스에 대해 여러 워크플로우가 동시에 실행되는 것을 방지합니다. 이를 통해 자원의 낭비를 줄이고, 프로젝트의 상태가 항상 최신 상태를 반영하도록 합니다. cancel-in-progress
옵션은 새로운 실행이 시작될 때 이전 실행을 자동으로 취소하므로, 항상 최신 커밋에 대한 CI 프로세스만이 실행됩니다.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs
섹션에서는 워크플로우 내에서 수행할 구체적인 작업들을 정의합니다. typechecking
작업은 코드의 타입을 체크하고, draft_release
작업은 조건(dry-run
)에 따라 실제 릴리즈를 준비합니다. 이러한 작업들은 프로젝트의 코드 품질을 유지하고, 배포 준비 과정을 자동화함으로써 개발자의 작업 부담을 줄여줍니다.
jobs:
typechecking:
uses: ./.github/workflows/typechecking.yml
draft_release:
permissions:
contents: write
위 설정을 통해 CI 프로세스가 시작되면, 모든 타입 체크를 마친 후 다양한 운영체제 환경에서 릴리즈 준비를 진행합니다. 최종적으로 릴리즈가 준비되면, 릴리즈 드래프트로 남겨 개발자가 최종적으로 릴리즈를 진행할지 결정할 수 있습니다.
결론적으로 CI가 시작이 되면 먼저 모든 타입 체킹을 마치고, 윈도우,맥,우분투 환경에서 릴리즈를 진행합니다. 릴리즈가 끝나면 릴리즈 draft 로 남아 제가 릴리즈를 할지 안할지 선택 할 수 있습니다
Release Workflow
이 워크플로우는 타입 체킹이 완료된 후 시작되며, 배포 준비부터 실제 배포까지의 여러 단계를 포함합니다. 각 설정과 단계별로 어떤 역할을 하는지 자세히 살펴보겠습니다.
릴리즈 워크플로우는 workflow_call
이벤트에 의해 트리거되며, 다른 워크플로우에서 이 워크플로우를 호출할 수 있습니다.
dry-run
옵션은 실제로 변경사항을 적용하지 않고, 명령어나 프로세스를 실행해보는 방식을 의미합니다workflow_call
: 다른 워크플로우에서 이 워크플로우를 호출할 수 있게 해주며,inputs
를 통해dry-run
옵션을 받아들입니다. 이는 실제 배포 과정을 시뮬레이션하고자 할 때 유용합니다
name: Release
on:
workflow_call:
inputs:
dry-run:
description: '앱을 컴파일하지만, 배포 서버에 아티팩트를 업로드하지는 않습니다'
default: false
required: false
type: boolean
모든 run
명령어가 실행될 때 사용될 기본 쉘 환경을 bash
로 설정합니다. 이 설정은 일관된 실행 환경을 제공하여 스크립트의 호환성을 보장합니다.
defaults:
run:
shell: 'bash'
릴리즈 준비를 위한 작업은 다양한 운영체제 환경에서 실행됩니다. permissions
설정을 통해 이 작업이 릴리즈를 생성할 수 있는 권한을 가지며, strategy
설정은 작업의 실패 시 다른 작업의 실행을 즉시 중단(fail-fast
)합니다. 이 설정은 프로젝트의 크로스 플랫폼 호환성을 검증하고, 릴리즈 준비 과정을 자동화합니다.
permissions
: 릴리스를 생성할 수 있는 권한을 작업에 부여합니다.strategy
: 여러 운영체제(macos-latest
,ubuntu-latest
,windows-latest
)에서 작업을 동시에 실행할 수 있게 합니다.fail-fast
: 여러 환경이나 버전에서 동시에 실행 되는 작업들 중 하날도 실패 할 경우 나머지 성공하지 않는 작업들을 즉시 중단 시킵니다
jobs:
draft_release:
permissions:
contents: write
strategy:
fail-fast: true
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
runs-on: ${{ matrix.os }}
코드 체크아웃부터 Node.js 설정, 종속성 설치, 프로젝트 빌드에 이르기까지 다양한 단계를 거칩니다. PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
환경 변수를 통해 불필요한 브라우저 다운로드를 방지합니다.
yarn install
할 때 타임 아웃을 준 이유는 간헐적으로 릴리즈가 되지 않는 경우가 있었는데 타임 아웃 제한 때문이었습니다. 의존성 다운로드를 할 때 느린 경우가 간헐적으로 있었기 때문입니다
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
- name: Clear yarn cache and install dependencies
run: |
yarn cache clean
yarn install --network-timeout 60000 --immutable --immutable-cache --check-cache
- run: yarn build
- 여러 단계를 통해 코드를 체크아웃하고, Node.js 환경을 설정하며, 종속성을 설치하고, 프로젝트를 빌드합니다. 특히
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
환경 변수를 사용하여 불필요한 브라우저 다운로드를 건너뜁니다.
nick-fields/retry@v3
액션을 사용하여 아티팩트의 컴파일 및 업로드 과정을 관리합니다. dry-run
옵션에 따라 실제 GitHub 릴리즈에 아티팩트를 업로드할지 결정하며, 배포 서버와의 연결 문제가 발생할 경우 여러 번 재시도할 수 있습니다.
- name: Compile artifacts ${{ inputs.dry-run && '' || 'and upload them to github release' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 15
max_attempts: 6
retry_wait_seconds: 15
retry_on: error
command: ./node_modules/.bin/electron-builder --config electron-builder.yml --publish ${{ inputs.dry-run && 'never' || 'always' }}
env:
GH_TOKEN: ${{ secrets.github_token }}
이 설정은 릴리즈 프로세스의 안정성을 보장하며, GitHub 토큰을 활용해 보안성 또한 강화합니다.
워크플로우 원본 파일들
워크플로우 진입점 (Workflow Entry Point)
# 이 워크플로우는 모든 CI (지속적 통합) 프로세스의 시작점입니다.
# 여기서부터 모든 다른 워크플로우가 시작됩니다.
on:
workflow_dispatch: # 수동 트리거를 위해 GitHub UI에서 이 워크플로우를 시작할 수 있습니다.
push:
branches:
- main # main 브랜치에 푸시될 때
- 'renovate/**' # renovate로 시작하는 모든 브랜치명을 가진 브랜치에 푸시될 때
paths-ignore:
- '.github/**' # .github 디렉토리 내의 변경사항은 무시
- '!.github/workflows/ci.yml' # 단, ci.yml 파일은 예외
- '!.github/workflows/typechecking.yml' # typechecking.yml 파일도 예외
- '!.github/workflows/tests.yml' # tests.yml 파일도 예외
- '!.github/workflows/release.yml' # release.yml 파일도 예외
- '**.md' # 모든 Markdown 파일 변경사항 무시
- .editorconfig # editorconfig 파일 변경사항 무시
- .gitignore # gitignore 파일 변경사항 무시
- '.idea/**' # IDEA 설정 파일 변경사항 무시
- '.vscode/**' # VSCode 설정 파일 변경사항 무시
pull_request:
paths-ignore:
- '.github/**' # 위와 동일한 경로 무시 규칙
- '!.github/workflows/ci.yml'
- '!.github/workflows/typechecking.yml'
# - '!.github/workflows/tests.yml' # 현재는 tests.yml 파일을 무시하지만, 주석 처리됨
- '!.github/workflows/release.yml'
- '**.md'
- .editorconfig
- .gitignore
- '.idea/**'
- '.vscode/**'
concurrency:
group: ci-${{ github.ref }} # 동일한 참조(ref)에 대한 워크플로우 실행이 중복될 경우 이전 실행을 취소
cancel-in-progress: true
jobs:
typechecking:
uses: ./.github/workflows/typechecking.yml # 타입 체크를 수행하는 작업
# tests:
# uses: ./.github/workflows/tests.yml # 테스트를 수행하는 작업, 현재는 실행되지 않음
draft_release:
permissions:
contents: write # 이 작업이 릴리스를 생성할 수 있도록 권한 부여
with:
dry-run: ${{ github.event_name != 'push' || github.ref_name != 'main' }} # 메인 브랜치에 push될 때만 실제 릴리스 생성
needs: [ typechecking ] # 타입 체킹 작업이 성공한 후 실행
uses: ./.github/workflows/release.yml # 릴리스 생성 작업
릴리스 생성 (Release Creation)
name: Release
on:
workflow_call:
inputs:
dry-run:
description: '앱을 컴파일하지만 배포 서버에 아티팩트를 업로드하지 않습니다'
default: false
required: false
type: boolean
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
draft_release:
permissions:
contents: write # 이 작업이 릴리스를 생성할 수 있도록 권한 부여
strategy:
fail-fast: true
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
- name: Clear yarn cache and install dependencies
run: |
yarn cache clean
yarn install --network-timeout 60000 --immutable --immutable-cache --check-cache
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: yarn build
- name: Compile artifacts ${{ inputs.dry-run && '' || 'and upload them to github release' }}
# 배포 서버와의 문제가 있을 경우 여러 번 재시도할 수 있는 이 액션을 사용합니다
uses: nick-fields/retry@v3
with:
timeout_minutes: 15
max_attempts: 6
retry_wait_seconds: 15
retry_on: error
shell: 'bash'
command: ./node_modules/.bin/electron-builder --config electron-builder.yml --publish ${{ inputs.dry-run && 'never' || 'always' }}
env:
GH_TOKEN: ${{ secrets.github_token }} # GitHub 토큰, 자동으로 제공됩니다 (이 비밀을 리포지토리 설정에서 정의할 필요 없음)
코드 스타일 및 정적 분석 (Linting and Static Analysis)
on:
workflow_dispatch:
push:
paths:
- '**.js'
- '**.mjs'
- '**.cjs'
- '**.jsx'
- '**.ts'
- '**.mts'
- '**.cts'
- '**.tsx'
- '**.vue'
pull_request:
paths:
- '**.js'
- '**.mjs'
- '**.cjs'
- '**.jsx'
- '**.ts'
- '**.mts'
- '**.cts'
- '**.tsx'
- '**.vue'
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
- run: yarn install --frozen-lockfile
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: yarn lint
# 템플릿 기여에서 코드 스타일을 검사하는 작업입니다.
code-style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
- run: yarn add prettier
- run: yarn prettier --check "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue}"
타입 체킹 (Type Checking)
name: Typechecking
on: [ workflow_call ]
concurrency:
group: typechecking-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
typescript:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
- run: yarn install --frozen-lockfile
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: yarn run typecheck || echo "누락된 스크립트를 건너뜁니다"
'DEV' 카테고리의 다른 글
Switching from Sharp to Jimp for Dependency Issues (0) | 2024.02.23 |
---|---|
Electron 에서 HashRouter를 쓰는게 정신 건강에 좋은 이유,, (0) | 2024.02.22 |
Electron에서 모니터 해상도로 인한 이미지 및 창 불일치 문제 해결 (0) | 2024.02.11 |
Electron Unplugin-auto-expose 사용 메뉴얼 (0) | 2024.02.10 |
Git Checkout으로 프로젝트 버전 이동하며 문제 해결하기 (1) | 2024.02.10 |