안녕하세요. 이번에 새로 피플펀드에 인턴으로 합류하게 된 이수호입니다. 이번 글에서는 제가 Soft Landing 동안 무슨 일을 했는지, 또 피플펀드에서는 git 정책을 github action 을 이용해 어떻게 자동화 하였는지에 대해 적어 보고자 합니다.

Soft Landing

피플펀드에는 Soft Landing이라는 제도가 있습니다. 입사 후 약 한 달 간 일정을 정하지 않고 작업을 진행하며 회사의 분위기와 업무에 적응할 수 있도록 합니다. 여러 이슈들 중에서 Good first issue 로 정의 된 이슈 중 하나를 골라 진행하되, 일정은 크게 신경쓰지 않습니다. 프로젝트의 난이도, 예상 소요 기간과는 무관하게 일정을 미리 고정하는 부담감을 덜어드리기 위한 과정입니다.

Soft Landing 과제

제가 맡게 된 과제는 다음과 같았습니다.

Github Workflow 개선

원래는 어땠는가?

저희는 원격 저장소로 Github 를 사용하고 있습니다. Git 컨벤션은 git-flow를 사용하고 있었습니다. 하지만 Commit 컨벤션이나 PR 컨벤션 등이 지정되어있지 않아 각자의 방법대로 커밋을 하고, PR을 날리고 있었습니다. Merge Conflict가 발생할 경우 후에 Merge하는 사람이 알아서 해결해야 했고, 따라서 내 변경점이 사라지거나 의도하지 않았던 변경점이 반영되는 등의 문제가 발생했습니다.

목표는 무엇인가?

그동안 규격화되어있지 않던 Commit Message 및 PR Title, Description을 정의하고. 될 수 있으면 이것들을 자동화 하는 것이 목표입니다.

automation

일을 제가 왜 합니까? 컴퓨터가 해야지 Source: https://www.youtube.com/watch?v=WSKi8HfcxEk

Commit Convention

Commit convention 을 다른 곳에선 어떻게 정의하고 있을까요? 몇 가지 찾아 본 결과 Angular Style이 가장 기본적이고 많이 쓰인다는 것을 알았습니다.

Angular Style

Angular Style이라는 말에 생소하실 수도 있지만. 다들 한 번쯤은 보셨을 형태입니다. Angular Style은 다음과 같이 생겼습니다.

type(scope?): subject  #scope is optional
type: [
    'build',
    'ci',
    'docs',
    'feat',
    'fix',
    'perf',
    'refactor',
    'revert',
    'style',
    'test'
]    

이를 실제 예시를 들어 보면 다음과 같은 커밋 메세지가 나옵니다.

docs: 오타 수정
feat: 입금자에게 메일 보내는 기능 추가
fix(pipeline): 데이터 잘못 전달되는 문제 해결 

더 자세한 설명은 Contributing to Angularcommit message guidelines 섹션을 참고하시면 됩니다.

한 번쯤은 보셨을 형태라고 생각합니다. 이 방법의 특징은 기존에 대중적으로 사용되고 있는 방법을 크게 해치지 않고 적용 가능하다는 점입니다.

적용하기

저희는 기본적으로 Angular Style을 사용하되, 사용하지 않는 type 및 scope에 대한 컨벤션을 제거하여 후에 일어날 혼란을 막고자 하였습니다. 몇 번의 미팅 결과 다음과 같은 type들만을 사용하는 것으로 결정하였습니다.

type: [
    'feat',
    'fix',
    'refac',
    'revert',
    'test',
    'misc'
]    

몇 가지 지워진 것들 뿐만 아니라 추가되고 변경된 부분도 보실 수 있습니다.

  • refac: refactor 가 너무 길어서 줄였습니다.
  • misc: 모든 변경사항에 대해 type을 지정하는 것은 속도를 저해하는 일이라고 판단하였고, 적당히 Trade-off 한 부분이 바로 misc입니다. type을 지정하기 애매하면 misc로 지정하여 message를 작성합니다.
  • 기타 삭제된 것들: 모호하거나 저희 시스템과 맞지 않아 삭제하였습니다.

또한 Merge Commit Message도 생성해 주는 것이 아닌, PR Title을 이용하는 것으로 변경하였습니다. 확실히 커밋 메세지는 깔끔해 졌습니다. 그런데 문제는 여전히 남아 있었습니다.

Git-Flow

저희는 Git Branch 정책으로 Git-flow를 사용합니다.

git-flow diagram

Source: https://gist.github.com/ihoneymon/a28138ee5309c73e94f9

하지만 저희 Github 저장소 Merge 정책 때문에 몇 가지 귀찮은 점이 생겼습니다.

PR을 통해서만 Merge 가능

Develop Branch에 Merge하기 위해서는 무조건 PR을 통해야 하고, 한 명 이상의 Approve가 필요하다는 점이었습니다. 어? 이게 왜 문제가 되나요? 라는 의문이 들 수도 있습니다. 여기서 문제는 hotfix에 발생합니다.

hotfix

hotfix의 경우 master에 ‘먼저’ 머지된 후 이후에 develop에 머지되어야 합니다. 이 말인 즉슨 저희는 PR을 두 번(!!) 만들어야 한다는 거죠. 심지어 Approve도 두 번이나(!!) 찍어줘야 합니다. 같은 작업을 두 번 하는 것은 매우 의미 없는 일인 데다가 휴먼 에러도 발생하기 쉽습니다. 만약 까먹고 develop에 머지를 하지 않는다면?

no please no

Source: https://meme-yeet.fandom.com/wiki/OH_GOD_PLEASE_NO

자동화 & Lint \w Github Action

문제를 찾았으니 자동화도 해 보고, 컨벤션에 맞지 않는 작업들도 경고를 띄워 봅시다. 여러 의견이 있었지만 Github Action 을 사용하기로 했습니다. 이유는 다음과 같습니다.

  1. 설정 파일(.yml)만 관리하면 서버 관리는 크게 하지 않아도 된다.
  2. 꽤 많이 돌려도 무료다.
  3. github 내에서 발생하는 모든 event를 캐치할 수 있다.

Lint Action

CommitLint나 PRLint 같은 경우는 다음 Action을 이용했습니다.

Github Action 사용법 및 각 Action의 설정은 다른 문서들을 참고하시면 될 것 같습니다.

자동화

자동화의 경우 두 가지 자동화를 하기로 했습니다.

  1. hotfix/ 접두사로 push 된 branch를 head 로 해서 자동으로 master base에 merge하는 PR 생성
  2. hotfix/ 접두사가 master에 merge된 경우 자동으로 develop base에 merge하는 PR 생성 후 Merge

1번 같은 경우 다음 Action을 사용했습니다.

2번 같은 경우는 특별히 Action을 찾을 수 없어 Custom Action을 만들기로 했습니다. Custome Action을 만드는 방법은 크게 두 가지로 나뉩니다.

  • Docker Image
  • Javascript(Typescript)

이번에는 Docker Image를 작성하여 Shell Script로 동작하는 Action을 만들기로 했습니다. 크게 어렵지는 않았습니다. Github API와 Github Event Document가 잘 되어 있어서 보고 따라 만들기만 하면 되었습니다.

PR을 만드는 펑션 부분입니다.

function create_pr()
{
 TITLE="hotfix auto merged by $(jq -r ".pull_request.head.user.login" "$GITHUB_EVENT_PATH" | head -1)."
 REPO_FULLNAME=$(jq -r ".repository.full_name" "$GITHUB_EVENT_PATH")
 SOURCE_BRANCH=$(jq -r ".pull_request.head.ref" "$GITHUB_EVENT_PATH")
 RESPONSE_CODE=$(curl -o $OUTPUT_PATH -s -w "%{http_code}\n" \
  --data "{\"title\":\"$TITLE\", \"head\": \"$SOURCE_BRANCH\", \"base\": \"$TARGET_BRANCH\"}" \
  -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  "https://api.github.com/repos/$REPO_FULLNAME/pulls")
 echo "head: $SOURCE_BRANCH, base: $TARGET_BRANCH"
 echo "Create PR Response:"
 echo "Code : $RESPONSE_CODE"
 if [[ "$RESPONSE_CODE" != "201" ]];
 then
  exit 1
 fi
}

다음은 머지를 직접 하는 부분입니다.

function merge_pr()
{
 REPO_FULLNAME=$(jq -r ".repository.full_name" "$GITHUB_EVENT_PATH")
 COMMIT_TITLE="$(jq -r ".title" "$OUTPUT_PATH" | head -1)"
 COMMIT_MESSAGE="$(jq -r ".body.head_commit.message" "$OUTPUT_PATH" | head -1)"
 HEAD_SHA="$(jq -r ".head.sha" "$OUTPUT_PATH" | head -1)"
 MERGE_METHOD="merge"
 PULL_NUMBER="$(jq -r ".number" "$OUTPUT_PATH" | head -1)"
 RESPONSE_CODE=$(curl -o $OUTPUT_PATH -s -w "%{http_code}\n" \
  --data "{\"commit_title\":\"$COMMIT_TITLE\", \"commit_message\":\"$COMMIT_MESSAGE\", \"sha\": \"$HEAD_SHA\", \"merge_method\": \"$MERGE_METHOD\"}" \
  -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  "https://api.github.com/repos/$REPO_FULLNAME/pulls/$PULL_NUMBER/merge")
 echo "Merged PR Response:"
 echo "Code : $RESPONSE_CODE"
}

문제점

하지만 늘 문제가 생기기 마련입니다. 앞에서 말했던 1명 이상의 Approve가 필요하다는 점이었습니다. 누군가 Approve 해 줘야 Merge되는 자동화라니. 이상하지 않습니까? 하지만 늘 방법은 있기 마련입니다. 피플펀드의 저장소에는 매일 정기 빌드를 올려주는 pbot이라는 친구가 있습니다. pbot

이 친구가 대신 찍어줄 수 있지 않을까요? 이 친구의 키를 저장소의 secrets에 저장하고, 가져다 쓰면 될 것 같습니다.

function approve_pr()
{
 REPO_FULLNAME=$(jq -r ".repository.full_name" "$GITHUB_EVENT_PATH")
 PULL_NUMBER="$(jq -r ".number" "$OUTPUT_PATH" | head -1)"
 RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}\n" \
  --data "{\"event\":\"APPROVE\"}" \
  -X POST \
  -H "Authorization: token $BOT_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  "https://api.github.com/repos/$REPO_FULLNAME/pulls/$PULL_NUMBER/reviews")
 echo "Approve PR Response:"
 echo "Code : $RESPONSE_CODE"
}

테스트 해 보니 잘 됩니다.

pbot become reviewer

전부 작성한 후 CTO이신 섬기님에게 부탁드려 피플펀드의 팀 저장소에 공개 저장소로 배포하였고, 현재 저장소 및 Action Marketplace에서 확인할 수 있습니다.

마치며

이걸로 저의 소프트랜딩 프로젝트는 끝이 났습니다. 나머지 기능 추가나 다른 큰 로드맵 및 자잘한 용어 및 버그 수정등이 있긴 하지만, 천천히 유지보수 해 나가면 될 것이라고 생각합니다. 이번 프로젝트를 진행하면서 가장 놀랐던 점은 인턴이 발언을 해도 ‘안 돼’ 보다는 진지하게 같이 고민해 주고 좋은 방향성을 찾아가려는 팀원들이었습니다. 그리고 들어온 지 1주일도 안 된 신입이 ‘컨벤션을 뒤집어 엎어야 합니다!’ 라고 말했을 때 듣고 ‘왜? 싫은데?’ 라고 말 하는 사람이 없다는 점도 놀랐습니다. 개인적으로도 Github Action을 써 볼 수 있어서 좋았고, 이걸 공개 저장소로 돌리는 것을 승낙해 준 섬기님에게도 감사의 말씀 남기고 싶습니다. 이만 마치겠습니다.