티스토리 뷰

배포조건

  • 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이름]

 

  1. 라벨을 먼저 체크한 뒤 이후 작업들을 실행한다
  2. deploy 작업에서 프로덕트 빌드와 배포를 진행한다
  3. 배포가 이루어지면 이후에 각 태그 및 릴리즈노트를 생성한다

실제 배포 성공 이미지

 

deploy 작업이 실행되지 않으면 이후 작업들도 스킵되는 모습

 

비고

https://github.com/pnpm/action-setup

 

GitHub - pnpm/action-setup: Install pnpm package manager

Install pnpm package manager. Contribute to pnpm/action-setup development by creating an account on GitHub.

github.com

https://github.com/actions/setup-node

 

GitHub - actions/setup-node: Set up your GitHub Actions workflow with a specific version of node.js

Set up your GitHub Actions workflow with a specific version of node.js - GitHub - actions/setup-node: Set up your GitHub Actions workflow with a specific version of node.js

github.com

https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-data

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
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
글 보관함