grep

DevOps

Github PR을 올리면 자동으로 테스트가? 심지어 멀티 프로젝트에도 가능하다!

NHN

2019년 12월 18일

원문에서 보기 ↗

안녕하세요. 메세징플랫폼개발팀의 한병익 사원입니다.

저희 팀을 비롯하여 많은 분들이 사내에서 Jenkins를 CI/CD 도구로 사용하고 있습니다. 저희는 그중에서도 GitHub Pull Request Builder라는 플러그인을 사용하여 PR마다 자동으로 테스트를 돌려 해당 결과를 알려주는 도구로 잘 사용하고 있습니다. GitHub Pull Request Builder를 이용하여 Jenkins를 성공적으로 구현할 경우 애플리케이션에 대한 새로운 코드 변경 사항(Pull Request)이 정기적으로 빌드 및 테스트되어 결과를 보여주게 됩니다. 이러면 여러 명의 개발자가 동시에 애플리케이션 개발을 하더라도, 테스트만 잘 짜 놓는다면 실제로 코드에 반영하기 전에 기능이 깨지진 않았나 정확히 확인할 수 있게 되어 매우 유용합니다.

해당 플러그 인을 설정하면 Pull Request마다 아래와 같이 Test 결과를 Github에서 실시간으로 확인이 가능합니다!

Pull Request 또는 Commit 추가 시 테스트 중...

1.png

테스트 통과!

2.png

테스트 실패...

3.png

저희 팀에서도 각 상품 프로젝트마다 PR 시 자동 테스트해주는 Job을 만들어서 잘 사용하고 있었습니다. 그러다가 상품에서 공통으로 사용하는 모듈들을 모아서 관리하는 멀티 모듈 프로젝트(하나의 깃 레포지토리에 여러 프로젝트를 관리하는 것)에도 이러한 Job을 만들어 적용하고 싶어 졌습니다.

첫 시도

4.png 저희는 현재 13개의 공통 모듈을 한 프로젝트에서 관리하고 있습니다. 이 프로젝트 빌드 및 테스트를 해주는 Job을 만들고자 했을 때 처음 든 생각은...

PR이 올라올 때마다 모든 모듈을 다 돌려보자!

이걸 모토로 잡고 Job을 만들려고 해 보니, Build 단계에서 실제로 메이븐을 통해 빌드와 테스트를 진행해주는 스텝인 Invoke top-level Maven target을 여러 개 생성할 수 있었습니다. 그래서 하나씩 추가하다 보니..... 5.png 이걸 13번이나 해야 하나...? 또 생기면... 또 넣어야 하고.... 13개 빌드하고 테스트 도는데 시간은 얼마나 걸릴까.... 로그도 겁나 길텐데 테스트 깨지면 failure 로그 찾는 것도 일이겠다.. 내가 개발한 모듈이 아니라 다른 모듈에서 테스트가 깨지면 고쳐질 때까지 X표 봐야 하나... 난 빨간색으로 나오는 거 싫은데...

결국 한 PR에 한 모듈만 개발할 테니까! PR에서 가장 많이 변화가 생긴 모듈만 하자!

그래서 새로 브랜치를 딸 때의 커밋과 개발 후 마지막 커밋을 비교해서 변화가 생긴 프로젝트를 빌드하자고 생각이 들었습니다. 당연히 그걸 비교해주는 Jenkins Plugin은 없기 때문에 Execute shell을 이용하여 스크립트를 작성하기 시작했습니다.

멀티 모듈 자동 테스트 Job 스크립트

1. 커밋 ID 얻어오기

먼저 첫 브랜치의 커밋과 개발 후 마지막 커밋을 얻어와야 했습니다. 그래서 Jenkins Execute shell에서 기본적으로 제공해주는 환경 변수들을 확인해보니,

GIT_COMMIT = The commit hash being checked out.

이 변수가 의미하는 건 개발 마지막 커밋이었고, 이와 git show --pretty=format%P를 이용해 해당 커밋의 부모 커밋 ID를 얻을 수 있었습니다. (git show --pretty=format 옵션 정보 https://git-scm.com/docs/pretty-formats)

echo "GIT_COMMIT=${GIT_COMMIT}" # Merge Commit sha1(refs/remotes/origin/pr/PR_번호/merge)의 실제 Commit ID

GIT_SHOW=`git show --pretty='format:%P' ${GIT_COMMIT}` # Merge Commit의 Source Commit과 Target Commit을 출력

COMMIT_IDS=(${GIT_SHOW})

2. 가장 많이 변한 모듈 찾기

그러면 이제 가장 많이 변한 모듈만 찾으면 되고 그 모듈의 디렉터리 경로만 빼올 수 있으면 해결될 것 같았습니다. 그런데 여기서....

가장 많이 변한 모듈은 어떻게 판단할까...?

위의 고민이 생겼습니다. 결국 변한 모듈의 변한 코드량까지 추출을 해야 하는 것일까 라는 생각을 잠시 했으나, 그냥 변한 파일 수로 간단히 구현하자!라고 마음을 먹었습니다. (어차피 한 PR에는 하나의 모듈만 개발할 테니까) 그래서 merge 커밋과 diff를 떠서 모듈 디렉터리 명까지만 추출하고, 가장 많은 파일이 변화된 모듈을 추출하는 작업을 수행했습니다. 이건 spring에 한정된 코드라서 구현은 각 프로젝트마다 달라질 수 있을 것 같습니다.

# Merge Commit과 Target Commit을 diff 하여 파일 명만 추출 -> 모듈 명만 추출하기 위해 문자열 처리 (Spring 프로젝트라서 src 폴더, pom.xml 상위 폴더로 지정)
GIT_DIFF=`git diff --name-only ${GIT_COMMIT}..${COMMIT_IDS[0]} | sed 's/\/src.*$//' | sed 's/\/pom.*$//'` 

IFS=$'\n' MODULES="$GIT_DIFF"
# 모듈 명이 가장 많이 나온 순서대로 정렬
UNIQUE_MODULES=($(printf "%s\n" "${MODULES[@]}"  | uniq -c | sort -nr | awk '{printf "%s\n", $2}'))
# 해당 모듈을 properties에 저장
echo MODULE=${UNIQUE_MODULES} > build.properties

3. 모듈 빌드

이제 해당 모듈을 빌드만 하면 끝이 납니다! 아까 모듈을 저장한 변수를 환경 변수로 등록하기 위해서 Inject environment variables 스텝을 추가합니다 6.png

마지막으로 환경 변수를 이용해 maven 빌드를 수행하는 스텝을 추가하면 끝이 납니다~

마무리

위의 방법으로 저희 팀은 현재 멀티 모듈에서도 테스트를 PR 시마다 통과 여부를 확인할 수 있어, 좀 더 관리하기 편해졌습니다. 생각보다 별거 아닌 곳에서 시간이 오래 걸리기도 했고 실제로 멀티 모듈로 관리하는 팀 분들도 비슷한 고민을 가지고 있었지 않을까 싶어서 공유하게 되었습니다! 저희 팀에서 적용한 방법이 가장 나은 방법이라고 할 수 없고 많은 방향이 존재하지만, 이 글이 실제로 멀티 모듈로 관리하고 계시는 팀 분들께 조금이나마 도움이 되었으면 합니다. 끝까지 읽어 주셔서 감사합니다