개발자로 후회없는 삶 살기

[문법] Github Actions 모듈화와 버저닝이 필요한 이유 본문

[Infra]/[GitHub Actions]

[문법] Github Actions 모듈화와 버저닝이 필요한 이유

몽이장쥰 2025. 2. 7. 22:00

서론

※ 아래 내용을 다룹니다.

  • 모듈화
  • 버저닝

 

본론

- 모듈화란?

작성한 코드를 재사용 가능한 단위로 분리하는 것으로 여러 프로젝트에서 공통적으로 사용되는 이미지 빌드 잡이나 배포 잡을 모듈로 만들고 재사용할 수 있다.

 

-> 모듈화가 필요한 이유

액션을 사용하는 레포지토리가 1개면 상관 없는데 100개라면 100개의 레포에 모두 액션을 작성해야 한다. 또한, 이미지 빌드 코드를 수정해야 한다면 100개를 전부 수정해야 한다. 보통 액션은 환경과 시크릿 값만 다르고 다른 로직은 비슷한데, 비슷한 로직을 100번 작성해야 하는 것은 매우 비효율적이다. 따라서 공통된 로직을 중앙화된 특정 레포지토리 한 곳에서 관리하고 이를 레포지토리에서 가져와서 참조하면 공통 변경 사항이 있을 경우 모듈만 수정하면 된다. 환경변수와 시크릿이 다르면 input 값으로 넣어주면 된다.

 

- 모듈화 하는법

composite action을 사용하면, 액션을 모듈화할 수 있다. 특정 스탭을 묶어서 하나의 액션 단위로 사용할 수 있도록 한다. uses 키워드로 공식 액션을 사용하는 것처럼 내가 만든 모듈을 uses로 불러와서 사용할 수 있다.

name, author, description을 명시해야 하고 input 값이 있다면 입력 값을 추가해야 한다.

 

- test ci 잡 모듈화 방법

=> 모듈을 사용하는 방법

jobs:
  test:
    if: github.event.action == 'opened' || github.event.action == 'synchronize'
    runs-on: ubuntu-latest
    steps:
    - name: checkout the code
      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

기존엔 체크아웃 > 노드 설치 > 캐시 > 의존성 설치 > 빌드 총 5가지 스탭을 따르며 100개의 레포에서 100번 이 코드를 재사용해야 했다. 이 중 중복되는 코드를 모듈화하면 작성해야 하는 코드를 줄일 수 있다.

 

사용하는 코드를 보면 총 2개의 체크아웃을 사용하는데 첫 번째는 현재 레포지토리를 가져오는 코드이고 두 번째는 모듈 레포지토리를 가져오는 코드이다. path를 지정하지 않으면 첫 번째 레포 위에 모듈 레포를 덮어쓰므로 path를 지정해야 한다. action-sang/github-action-module이라는 레포지토리를 actions-module에 체크아웃하고

 

test 폴더 내부에 있는 액션 모듈을 사용하는 코드이다. test 내부에 action.yml 파일이 있어야 하며 action이라는 이름은 반드시 고정되어야 하는 규칙이 있다. 만약 노드 버전이 이를 사용하는 레포마다 수정된다면 입력값으로 넣어주면 된다.

 

=> 모듈을 만드는 방법

이 사진은 두번째 체크아웃해서 가져온 test 모듈이 저장되어 있는 모듈 레포이다.

 

-> 액션 파일 내부

name: test
author: SangBeom-Hahn
description: 'test'
inputs:
  NODE_VERSION:
    description: 'test'
    required: true
    default: 18
  WORKING_DIRECTORY:
    description: 'test'
    required: true
    default: 'my-app'    

runs:
  using: "composite"
  steps:
  - name: setup-node
    uses: actions/setup-node@v3
    with:
      node-version: ${{ inputs.NODE_VERSION }}
  - 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
    shell: bash
    run: |
      cd ${{ inputs.WORKING_DIRECTORY }}
      npm ci
  - name: npm build
    shell: bash
    run: |
      cd ${{ inputs.WORKING_DIRECTORY }}
      npm run build

액션 모듈을 상단과 하단으로 나뉜다.

 

name : 액션 모듈 이름
author : 작성자 깃헙 ID
description : 설명
inputs : 입력값

입력값에서 des는 필수이고 req와 def는 선택값이다.

 

using : 컴포사이트 액션을 사용
run : bash 쉘로 run

하단에는 원본 워크프로우에서 실행되는 스탭을 그대로 복사하여 가져오면 된다. run 키워드를 사용할 때는 bash 쉘을 명시해야 한다.

 

-> 액션 모듈 사용 이후

jobs:
  test:
    if: github.event.action == 'opened' || github.event.action == 'synchronize'
    runs-on: ubuntu-latest
    steps:
    - name: checkout the code
      uses: actions/checkout@v4
    - name: checkout module code
      uses: actions/checkout@v4
      with:
        repository: "action-sang/github-action-module"
        path: ./actions-module
        ref: ${{ vars.VERSION }}
    - name: use test module
      uses: ./actions-module/test
      with:
        NODE_VERSION: '18'
        WORKING_DIRECTORY: 'my-app'

원래는 5개의 스탭을 작성해야 했는데 3가지 스탭으로 준다.

 

1] 현재 레포지토리 체크아웃
2] 모듈 레포지토리 체크아웃
3] 모듈 실행

1번은 현재 레포지토리를 가져오는 것이니 반드시 작성해야 하는 것이고, 2, 3번은 액션 모듈을 가져와서 사용하는 스탭이다. uses 키워드로 액션을 가져올 수 있다. 항상 중복되는 스탭은 모듈화해서 사용할 수 있겠다.

 

- set env 잡 모듈화

name: set-env
author: SangBeom-Hahn
description: 'test'
inputs:
  REF_TYPE:
    description: 'test'
    required: true
    default: 'branch'
  BASE_REF:
    description: 'test'
    required: true
    default: 'dev'
outputs:
  environment:
    description: 'set env'
    value: ${{ steps.set-env.outputs.environment }}

runs:
  using: "composite"
  steps:
  - name: set env
    id: set-env
    shell: bash
    run: |
      if [[ ${{ inputs.REF_TYPE }} == "tag" ]]; then
        echo "environment=qa" >> $GITHUB_OUTPUT
        exit 0
      fi

      if [[ ${{ inputs.REF_TYPE }} == "branch" ]]; then
        echo "environment=dev" >> $GITHUB_OUTPUT

        if [[ ${{ inputs.BASE_REF }} == "master" ]]; then
          echo "environment=prod" >> $GITHUB_OUTPUT 
        fi
      fi
  - name: check env
    shell: bash
    run: echo ${{ steps.set-env.outputs.environment }}

set env 잡은 현재 워크프로우의 environment를 아웃풋으로 정하고 이후 잡에서 아웃풋을 사용하게 하는 잡이다. 모듈에서도 아웃풋을 지정할 수 있다. environment가 키이다. 아래부분은 스탭을 정의한다. set env 잡의 주요 로직을 그대로 사용한다. 인풋값으로 설정한 ref type과 base ref를 사용한다.

 

  set-environment:
    if: github.event.pull_request.merged == true || github.ref_type == 'tag'
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.set-env.outputs.environment }}
    steps:
    - name: checkout module code
      uses: actions/checkout@v4
      with:
        repository: "action-sang/github-action-module"
        path: ./actions-module
        ref: ${{ vars.VERSION }}        
    - name: use set env module
      uses: ./actions-module/set-env
      with:
        REF_TYPE: ${{ github.ref_type }}
        BASE_REF: ${{ github.base_ref }}

원본 set env와 모듈을 비교해보면 동일하게 2개의 스탭을 가지고 있다. 그러니 사용하는 곳에서는 모듈을 가져와서 입력값을 주며 실행만 하면 된다. 사용할 때는 steps.{스탭 ID}.outputs.{아웃풋 키}로 하면 된다.

 

✅ 결론

모듈화를 할 때 우선 모듈을 사용하지 않는 워크플로우를 만들고 사용하다가, 모듈이 필요할 때 원본을 참고해서 action 야물을 만들면 된다.

 

- 버저닝이 필요한 이유

액션 모듈을 100개의 레포에서 사용하고 있을 때, 모듈에 변경 사항이 발생하면 레포 1에서 테스트를 하고 안정적이면 모든 레포에 적용하고 싶다. 하지만, 모듈을 공통으로 사용되기 때문에 불가능하다. 이때 버저닝을 사용하면 모듈을 사용하는 레포지토리마다 다른 모듈을 참조할 수 있다.

 

-> 버저닝하는법

액션 체크아웃을 보면 ref로 원하는 버전의 모듈을 체크아웃 할 수 있다고 나와있다. 체크아웃은 특정 tag, 브랜치, 커밋 시점으로 할 수 있으며

 

ref를 명시하지 않으면 기본 브랜치의 모듈을 참조한다. 위 코드는 모듈 레포의 기본 브랜치의 모듈을 가져온 것이다.

 

1] 브랜치 : 간편하지만, 브랜치 관리를 잘해야 하고 수정 삭제로 인한 오류 발생 가능
2] tag : 오류 발생 경우가 없지만, 유연한 버전 관리 어려움
3] 커밋 : 특정 커밋 ID로 체크아웃

이번에는 오류가 발생할 수 있지만, 간단한 브랜치 방법으로 ref를 해볼 것이다.

 

-> ref의 변경

만약 버전을 사용하는 레포지토리가 100개면 사용하는 워크 플로우의 ref를 100번 수정해야 한다. 따라서 환경변수를 사용하여 한 번만 수정할 수 있도록 한다. 범위는 org, repo, env가 있으며 보통 전부 반영을 원하는 버전은 org로 하고 특정 레포에만 반영하고 싶은 경우 (테스트) 해당 레포 레벨로 변수를 지정할 것이다.

 

사용할 때 ref를 지정하면 환경 변수의 값이 v1.0.0이므로 버전 1의 액션 모듈을 체크아웃한다.

Comments