티스토리 뷰
배포조건
- CSR, SSG 등의 호스팅 서버가 필요없는 렌더링 조건
- AWS S3, Cloud front 사용시
- MFA, Monorepo를 도입해서 운용하는 프로젝트
배포 트리거
develop 또는 main, master에 PR이 merge되어 Close될때
MFA 구조에 맞게 배포하도록 조건 분할하기
Host, Remote와 같이 패키지별로 관리되어 레포 내에서 분할되어 빌드, 배포 등의 관리가 이루어져야 함
PR시 Label로 구분하여 action 트리거 작동시키도록 구현
Label 정의
- deploy
- host
- remote
- deploy-skip
- invalidation-skip
- release
Label 별 추가시 작동하는 액션 상세내용
- deploy
- 빌드, 배포, 캐시무효화와 같은 배포 로직 실행
- all
- 모든 패키지에 대한 작업을 수행
- host
- 호스트 패키지에 대한 작업을 수행
- remote
- 리모트 패키지에 대한 작업을 수행
- deploy-skip
- 빌드, 배포 행위를 스킵함
- invalidation-skip
- 캐시무효화 행위를 스킵함
- release
- 태그 생성 및 릴리즈 노트 생성 로직 실행
상황별 라벨 부가 케이스 예시
- 소스만 변경하고 싶을때
- 라벨 없이 PR Merge
- 모든 패키지에 대한 배포를 전체적으로 하고 싶을때
- develop, all 추가후 PR Merge
- 모든 패키지에 대한 빌드 및 배포만 전체적으로 하고 싶을때
- develop, all, invalidation-skip 추가후 PR Merge
- 모든 패키지에 대한 캐시 무효화만 전체적으로 하고 싶을때
- develop, host, remote, deploy-skip 추가후 PR Merge
- 모든 패키지에 대한 배포를 전체적으로 하고 릴리즈 노트 생성하고 싶을때
- develop, host, remote, release 추가후 PR Merge
- 특정 패키지에 대한 배포를 컨트롤 하고 싶을때
- develop, 패키지 이름 태그, 원하는 skip 태그
예시 이미지
상세 구현 코드
라벨 체크 작업
steps 내에 github 액션 내 GITHUB_OUTPUT 문법에 맞게 생성
check_labels:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
...
id: pr_label
run: |
echo "isDeploy=${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}" >> $GITHUB_OUTPUT
...
outputs에 키, 값 형태로 매핑하여 다른 job에서도 사용할 수 있게 작성
check_labels:
...
outputs:
isDeploy: ${{ steps.pr_label.outputs.isDeploy}}
...
체크된 라벨 조건 가져와 배포 전개하는 작업
체크된 조건 if문에 넣어 트리거에 맞게 실행시킴
deploy:
needs: check_labels
if: ${{ github.event.pull_request.merged == true && needs.check_labels.outputs.isDeploy == 'true' }}
runs-on: ubuntu-latest
...
steps:
- name: Build Host Application
if: ${{ needs.check_labels.outputs.isDeploySkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isHost == 'true' }}
run: |
pnpm run host:build
...
pnpm install 및 캐싱작업
- pnpm 설치
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
- pnpm 글로벌 가상 스토어 캐싱
- name: Get pnpm store directory
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v3
id: pnpm-cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- node 설치 및 라이브러리 cache
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
pnpm cache된 내용을 토대로 노드를 설치함
전체 설치하지 않고 reused로 이미 캐시된 내용을 reused 해서 CI 실행 시간을 줄임
체크된 라벨 조건 가져와 태그 및 릴리즈 노트 생성하는 작업
체크된 조건 if문에 넣어 트리거에 맞게 실행시킴
host_tag_release:
needs: check_labels
if: ${{ github.event.pull_request.merged == true && needs.check_labels.outputs.isRelease == 'true' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isHost == 'true' }}
runs-on: ubuntu-latest
태그가 있다면 새로 태그 생성하지 않고 없으면 해당 패키지의 version을 가져와 새로 생성하도록 구현
...
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@v1.3.1
with:
path: mfa/host
# 태그 존재 유무 판단
- name: check-tag-exist
id: checkTag
uses: mukunku/tag-exists-action@v1.2.0
with:
tag: host-v${{steps.package-version.outputs.current-version}}
# 태그 미존재시 태그 생성
- name: Bump version and push no exist tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ steps.package-version.outputs.current-version}}
tag_prefix: host-v
if: ${{ steps.checkTag.outputs.exists == 'false' }}
- name: Create a GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.tag_version.outputs.new_tag }}
name: Release Host ${{ steps.tag_version.outputs.new_tag }}
generateReleaseNotes: true
makeLatest: true # latest 릴리즈 설정여부
if: ${{ steps.checkTag.outputs.exists == 'false' }}
...
전체 구현 코드
main_deploy.yml
name: Deploy to AWS S3 and Invalidate CloudFront
on:
pull_request:
branches:
- main
- master
types:
- closed
jobs:
check_labels:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
outputs:
isDeploy: ${{ steps.pr_label.outputs.isDeploy}}
isAll: ${{ steps.pr_label.outputs.isAll}}
isHost: ${{ steps.pr_label.outputs.isHost}}
isRemote: ${{ steps.pr_label.outputs.isRemote}}
isDeploySkip: ${{ steps.pr_label.outputs.isDeploySkip}}
isInvalidationSkip: ${{ steps.pr_label.outputs.isInvalidationSkip}}
isRelease: ${{ steps.pr_label.outputs.isRelease}}
steps:
- name: Check PR Label
id: pr_label
run: |
echo "isDeploy=${{ contains(github.event.pull_request.labels.*.name, 'deploy') }}" >> $GITHUB_OUTPUT
echo "isAll=${{ contains(github.event.pull_request.labels.*.name, 'all') }}" >> $GITHUB_OUTPUT
echo "isHost=${{ contains(github.event.pull_request.labels.*.name, 'host') }}" >> $GITHUB_OUTPUT
echo "isRemote=${{ contains(github.event.pull_request.labels.*.name, 'remote') }}" >> $GITHUB_OUTPUT
echo "isDeploySkip=${{ contains(github.event.pull_request.labels.*.name, 'deploy-skip') }}" >> $GITHUB_OUTPUT
echo "isInvalidationSkip=${{ contains(github.event.pull_request.labels.*.name, 'invalidation-skip') }}" >> $GITHUB_OUTPUT
echo "isRelease=${{ contains(github.event.pull_request.labels.*.name, 'release') }}" >> $GITHUB_OUTPUT
deploy:
needs: check_labels
if: ${{ github.event.pull_request.merged == true && needs.check_labels.outputs.isDeploy == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
- name: Get pnpm store directory
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v3
id: pnpm-cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build Host Application
run: |
pnpm run host:build
if: ${{ needs.check_labels.outputs.isDeploySkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isHost == 'true' }}
- name: Build Remote Application
run: |
pnpm run remote:build
if: ${{ needs.check_labels.outputs.isDeploySkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isRemote == 'true' }}
- name: Deploy Host to AWS S3
run: |
aws s3 sync mfa/host/dist/ s3://${AWS_BUCKET_NAME}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_BUCKET_NAME: ${{ secrets.AWS_LIVE_HOST_BUCKET_NAME }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_LIVE_REGION }} # AWS 지역 설정
if: ${{ needs.check_labels.outputs.isDeploySkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isHost == 'true' }}
- name: Deploy Remote to AWS S3
run: |
aws s3 sync mfa/remote/dist/ s3://${AWS_BUCKET_NAME}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_BUCKET_NAME: ${{ secrets.AWS_LIVE_REMOTE_BUCKET_NAME }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_LIVE_REGION }} # AWS 지역 설정
if: ${{ needs.check_labels.outputs.isDeploySkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isRemote == 'true' }}
- name: Invalidate Host CloudFront Cache
run: |
aws configure set preview.cloudfront true
aws cloudfront create-invalidation --distribution-id ${AWS_DISTRIBUTION_ID} --paths "/*"
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DISTRIBUTION_ID: ${{ secrets.AWS_LIVE_HOST_DISTRIBUTION_ID }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_LIVE_REGION }} # AWS 지역 설정
if: ${{ needs.check_labels.outputs.isInvalidationSkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isHost == 'true' }}
- name: Invalidate Remote CloudFront Cache
run: |
aws configure set preview.cloudfront true
aws cloudfront create-invalidation --distribution-id ${AWS_DISTRIBUTION_ID} --paths "/*"
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DISTRIBUTION_ID: ${{ secrets.AWS_LIVE_REMOTE_DISTRIBUTION_ID }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_LIVE_REGION }} # AWS 지역 설정
if: ${{ needs.check_labels.outputs.isInvalidationSkip == 'false' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isRemote == 'true'}}
host_tag_release:
needs: [check_labels, deploy]
if: ${{ github.event.pull_request.merged == true && needs.check_labels.outputs.isRelease == 'true' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isHost == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@v1.3.1
with:
path: mfa/host
# 태그 존재 유무 판단
- name: check-tag-exist
id: checkTag
uses: mukunku/tag-exists-action@v1.2.0
with:
tag: host-v${{steps.package-version.outputs.current-version}}
# 태그 미존재시 태그 생성
- name: Bump version and push no exist tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ steps.package-version.outputs.current-version}}
tag_prefix: host-v
if: ${{ steps.checkTag.outputs.exists == 'false' }}
- name: Create a GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.tag_version.outputs.new_tag }}
name: Release Host ${{ steps.tag_version.outputs.new_tag }}
generateReleaseNotes: true
makeLatest: true # latest 릴리즈 설정여부
if: ${{ steps.checkTag.outputs.exists == 'false' }}
remote_tag_release:
needs: [check_labels, deploy]
if: ${{ github.event.pull_request.merged == true && needs.check_labels.outputs.isRelease == 'true' && needs.check_labels.outputs.isAll == 'true' || needs.check_labels.outputs.isRemote == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@v1.3.1
with:
path: mfa/remote
# 태그 존재 유무 판단
- name: check-tag-exist
id: checkTag
uses: mukunku/tag-exists-action@v1.2.0
with:
tag: remote-v${{steps.package-version.outputs.current-version}}
# 태그 미존재시 태그 생성
- name: Bump version and push no exist tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ steps.package-version.outputs.current-version}}
tag_prefix: remote-v
if: ${{ steps.checkTag.outputs.exists == 'false' }}
- name: Create a GitHub release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.tag_version.outputs.new_tag }}
name: Release Remote ${{ steps.tag_version.outputs.new_tag }}
generateReleaseNotes: true
makeLatest: true # latest 릴리즈 설정여부
if: ${{ steps.checkTag.outputs.exists == 'false' }}
Action 작업별로 선후 관계 연결하여 처리하기
각 작업에 needs 항목에 각 선행되어야 하는 작업 추가
job이름
needs: [job1이름, job2이름]
- 라벨을 먼저 체크한 뒤 이후 작업들을 실행한다
- deploy 작업에서 프로덕트 빌드와 배포를 진행한다
- 배포가 이루어지면 이후에 각 태그 및 릴리즈노트를 생성한다
실제 배포 성공 이미지
deploy 작업이 실행되지 않으면 이후 작업들도 스킵되는 모습
비고
https://github.com/pnpm/action-setup
https://github.com/actions/setup-node
https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-data
'개발' 카테고리의 다른 글
Cypress 적용 및 github actions CI 붙히기 (2) | 2024.01.13 |
---|---|
아카이빙 : 프론트엔드 아티클 모으기 (0) | 2023.12.27 |
jest 세팅 및 zustand 전역 스토어 테스트 구축 (0) | 2023.11.01 |
Storybook 도입 및 배포 정리 (0) | 2023.10.27 |
AWS Lambda CloudFront SubRouting으로 버전별로 정적 호스팅 서빙하기 (0) | 2023.10.24 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- frontend
- Micro Frontend Architecture
- deploy
- node module
- pnpm
- 당신은 결국 무엇이든 해내는 사람
- zero install
- 프론트엔드아키텍처
- DevOps
- vue
- subrouting
- vue3
- 상태관리전략
- Flutter
- CI
- 서버상태관리
- error handle
- 프론트엔드최적화
- Module Federation
- MFA
- Infra
- test
- Style
- yarn-berry
- design system
- aws
- defineProps
- TanStackQuery
- 독서
- 독후감
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함