개발지식

github actions, label로 하는 versioning, 릴리즈 관리

weaklion 2024. 1. 9. 21:11

오랜만이다.

 

최근에 글을 안 썼는데, 이유는 그냥 너무 바빠서 안 썼습니다.

오늘은 github actions을 통한 릴리즈 관리와 auto tagging에 대해서 알아보도록 하겠습니다.

처절한 흔적...

 

사실 처음 목적은 릴리즈 라기 보단 auto tagging에 중점을 뒀었습니다. 지금 운영하고 있는 애플리케이션의 버전 관리가 제대로 되어 있지 않았는데, 개발팀에선 이를 tag를 통해 해결하려고 했습니다.

 

tag는 수동으로 처리하는 방법과 자동으로 처리하는 방법이 있습니다. 수동으로 처리하는 방식도 고려해봤지만, 굳이 할 이유가 없었기에 평소에 사용중이던 github action을 이용해서 워크플로우를 통해 자동으로 버전의 태그를 관리하는 방식으로 처리하기로 결심했습니다.

 

저희 회사는 매주 목요일마다 정기적으로 운영에 배포하고 있습니다. 저는 이 때 돌아가고 있는 CI/CD에 package.json과 연동된 tagging job을 추가해 돌아가게 하기로 결정했습니다.

대략적인 처리 로직은 다음과 같습니다.

  1. 배포하려는 작업물을 지정된 브랜치(ex: main)에 pr 한다.
  2. 이 과정에서 label을 사용한다. major, minor, patch. 총 3개의 label을 만들어 각각의 label에 맞는 버전을 배포한다.
  3. release-drafter-config에서 버전에 맞는 label을 정의 하고 판별한다.

 

versioning에서 사용할 label은 semantic version을 참조했습니다. semantic version은 흔히 사용하는 X.Y.Z의 형태를 지니고 있으며 음이 아닌 정수를 사용해야만 합니다.

 

각각의 버전이 업데이트 되면 앞자리의 수는 초기화 되어야 합니다. 요컨데 major 버전을 업데이트 한다면 minor와 patch는 0이 되어야 하고 (1.3.2 에서 major를 업데이트하면 2.0.0), minor를 업데이트 할시에 patch는 0이 되어야만 합니다.

github에서 정의한 label은 semantic version의 major, minor, patch를 정확하게 따라갑니다.

자세한 정보는 아래 홈페이지를 참조해주세요.

Semantic Versioning 소개

 

Semantic Versioning 소개

소프트웨어의 버전 명을 정하는 방법은 여러 가지가 있지만 명확한 기준 없이 지어질 때가 많습니다. 이번 글은 여러 경험을 종합하여 만들어진 Semantic Versioning 스펙을 소개합니다.

spoqa.github.io

 

위의 작업이 끝나고 나면 pr 이후에 돌아가는 workflow를 만듭니다. 이 과정에서 tag 뿐만 아니라, 릴리즈를 통해 지금까지의 커밋을 정리하면 어떨까 하는 생각이 들었고, 이를 실행하기로 합니다.

릴리즈를 통한 작업은 release-drafter 액션을 이용하기로 했습니다. 잘 정리되어있고, star도 많은 만큼 사이드 이펙트가 생겨도 해결할 수 있을거라 생각했습니다.

.github/workflows/draft-release.yaml

name: draft-release
on:
  pull_request:
    types: [closed]

permissions: write-all

jobs:
  check_labels:
    name: 'Check PR Labels'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: mheap/github-action-required-labels@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          mode: exactly
          count: 1
          labels: '🔖 major,🔖 minor,🔖 patch'
          
  draft-release:
    runs-on: ubuntu-latest
    needs: check_labels
    steps:
      - uses: actions/checkout@v3

      - name: Git config
        run: |
          git config --global user.name "${GITHUB_ACTOR}"
          git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
      
      - name: Apply version bump (major)
        if: contains(github.event.pull_request.labels.*.name, '🔖 major')
        run: |
          npm version major > version.txt
      - name: Apply version bump (minor)
        if: contains(github.event.pull_request.labels.*.name, '🔖 minor')
        run: |
          npm version minor > version.txt
      - name: Apply version bump (patch)
        if: contains(github.event.pull_request.labels.*.name, '🔖 patch')
        run: |
          npm version patch > version.txt
          
      - name: Read version.txt
        id: commit
        uses: juliangruber/read-file-action@v1
        with:
            path: ./version.txt

      - name: Push
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}
         

      - name: Release drafter
        uses: release-drafter/release-drafter@v5
        if: ${{ steps.git_tag_check.outputs.tag == '' }}
        with:
          config-name: release-drafter-config.yml
          version: 'v${{ (steps.commit.outputs.content) }}'
          publish: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
#

 

raft-release 워크플로우는 pull_request가 닫혔을 때에 실행됩니다. 총 두 개의 job이 실행되는데 각각이 어떤 일을 하는지 대략적으로 정리해보겠습니다.

 

Check Labels

 

워크플로우가 실행되고 가장 먼저 실행되는 job입니다. 여기선 pull_reuqest의 label을 판별하는데, 🔖 major,🔖 minor,🔖 patch 총 3가지를 구별합니다.

mode : exactly로 인해 label은 정확하게 구별되며, 여기서 3개의 label 외에 다른 label (혹은 없을 시 ) 에러를 발생시킵니다.

 

Draft-release

 

check labels job이 성공했을 시 실행되는 job입니다. 우선 repo에 push 하기 전 간략하게 profile을 등록하고 이후 label에 있는 순서대로 npm version을 실행합니다. version은 major, minor, patch 총 3가지가 있는데 위 3가지 명령어는 npm에서 지원하는 기능으로, 실행할 시에 package.json의 버전을 업데이트하고, 버전에 맞는 태그를 생성시켜줍니다.

이후 나온 버전을 커밋후에 repo에 업데이트합니다. 그리고 난 뒤에 release-drafter를 실행시키고, commit이력들을 토대로 version에 맞는 릴리즈를 자동으로 발생시킵니다.

정리하면 draft-release job에선 총 3가지의 작업이 이루어집니다.

  1. git config을 통해 유저 정보를 정의합니다.
  2. pr에 있는 라벨을 확인하고 버전을 업데이트 합니다.
  3. 업데이트 된 package.json을 push하고 release-drafter 액션을 실행합니다.

 

위의 워크플로우에서 가장 중요한 건 package.json과의 연동입니다. 저는 라벨을 구별하고 각각에 맞는 버전을 npm을 통해 업데이트 했습니다. npm publish를 통해 버전을 업데이트 하는 방법이 있지만, 이 방법은 npm에 진행중인 프로젝트를 공개하기 때문에 내부 프로젝트, 혹은 개인적으로 진행중인 프로젝트에 적합하지 않습니다.

 

pull_request를 닫게 되면 위 워크플로우가 실행됩니다. 총 두 가지의 Job이 실행되는데 check_labels에선 pr할 때에 넣었던 label을 판별합니다. 아까도 말했다시피 label을 통해 versioning이 이루어지기 때문에 label이 없다면 check_labels 단계에서 에러가 뜨게되고, 이후의 작업은 실행되지 않습니다

.

.github/release-drafter-config.yml

name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
template: |
  ## Changes
  $CHANGES
version-resolver:
  minor:
    labels:
      - '🔖 minor'
  major:
    labels:
      - '🔖 major'
  patch:
    labels:
      - '🔖 patch'
  default: patch
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'

release-drafter 액션이 실행되기 위한 템플릿 config 입니다. template에서 원하는 템플릿을 만들 수 있으며 version-resolver에서는 pr 시에 넣어놓은 label을 통해 어떤 버전으로 업데이트 할 지를 구분합니다.

저희 회사의 경우 커밋 룰 확립을 위해 gitmoji를 사용하고 있기 때문에 gitmoji에서 지원하는 emoji + 업데이트할 버전을 인식합니다. 가령 minor 버전을 업데이트하기 위해선 emoji + minor 라벨을 PR할 시에 넣어야만 정상적으로 라벨을 통한 버전 업데이트를 할 수 있습니다.

https://gitmoji.dev/

 

gitmoji

:truck: Move or rename resources (e.g.: files, paths, routes).

gitmoji.dev

https://github.com/release-drafter/release-drafter

 

GitHub - release-drafter/release-drafter: Drafts your next release notes as pull requests are merged into master.

Drafts your next release notes as pull requests are merged into master. - GitHub - release-drafter/release-drafter: Drafts your next release notes as pull requests are merged into master.

github.com

 

 

여기까지가 현재 제가 작업했던 방식입니다. package.json 과 버전을 연동하는 방법은 검색을 해도 잘 나오지 않아 결국 혼자 했고, 아직 미비한 점이 많습니다.

 

첫 번째는 라벨이 없을 때의 문제점입니다.

 

두 가지의 방법이 떠올랐는데 첫 번째는 라벨은 정말로 중요하다고 강조하는 것이고, 안붙여서 생기는 불상사는 너에게 발생한다고 책임을 전가하는 방법입니다.

물론 좋은 방법은 아닙니다. 애초부터 라벨을 안 넣었을 때를 미리 방지 하기 위한 작업이 필요합니다. 가장 이상적인 방법이며, 최우선적으로 작업해야 할 두 번째 방법입니다.

 

두 번째는 릴리즈 템플릿의 고도화입니다.

아직은 릴리즈 템플릿에 아무것도 없어서 릴리즈에 커밋 목록만 뜹니다. 아마 시간이 난다면 다른 기업에서 사용하는 릴리즈 노트처럼 얄콤뽀쨕하게 꾸며볼 것 같습니다.

 

읽어주셔서 감사합니다.