DevOps

동료가 없어서 GitLab에 GPT 코드리뷰어 봇을 만든 건에 대하여

mokpolar 2024. 1. 23. 23:42
반응형

TL;DR

 

안녕하세요,
"동료가 없어서 GitLab에 GPT 코드리뷰어 봇을 만든 건에 대하여" 시작하겠습니다.

 

사실 정말로 동료가 없어서 코드리뷰어 봇을 만들어야겠다고 생각한건 아니고,
코드 커밋시에 여러 보안 문제들을 자동으로 체크할 수 있는 도구를 만들어서 GitLab CI/CD에 붙여야겠다는 생각을 했습니다.

 

이 글은 GitLab v16.9(2024.1.21 현재)를 기준으로 작성되었습니다.

LLM API 호출

최근 GPT로 대표되는 LLM들을 이용해서 여러가지 생산성이 있는 일들을 할 수 있게 되었고, 자동화시킨 코드에 대한 "피드백"은 그 중 하나일 것입니다.

 

어떤 형태로든, 외부 API나 자체 모델이나, GPT 모델을 호출할 수 있는 API를 갖고 있다면 코드리뷰를 해달라는 요청을 보낼 수 있을 것입니다.

 

예컨대 아래와 같은 형태일 것 같습니다.

코드의 내용과 프롬프트를 함께 담아 어떤 내용에 중점을 둬서 코드리뷰를 해달라고 하는 것입니다.
여러 사람이 이에 대한 글을 쓴 내용을 읽었는데, 프롬프트에 아이덴티티를 담고, 내용에 대한 보상을 담으면 더 좋은 내용이 나온다고 합니다.

 

예 :
"당신은 30년 경력의 전문 파이썬 개발자입니다. 아래 코드를 리뷰해주세요.
{코드 내용}
제대로 리뷰를 해주시면 100달러의 팁을 드리겠습니다."

response = requests.post({URI}, headers={HEADER}, json=data)
...
review = f"다음은 {response} 에 대한 코드 리뷰 입니다. :..."

 

GitLab Commit API 호출해서 Diff 가져오기

 

prompt와 이를 호출할 LLM API가 준비되었다고 가정하고,
GitLab commit 에서 어떻게 내용을 가져올 지를 생각해봤습니다.

 

일단 먼저 생각났던 commit 의 diff를 가져오는 방법은,
commit이 일어날 때마다 특정 commit 에 바로 접근하는 법이었습니다.

 

이 문서에서 commit 과 관련한 GitLab의 API들을 확인할 수 있었습니다.
https://docs.gitlab.com/ee/api/commits.html#list-repository-commits

 

project의 commit 리스트를 긁어와서 commit의 id를 확인할 수 있습니다.

이를 호출하면 아래와 같이 해당 project에 속하는 전체 commit 을 긁어오게 됩니다.

[
  {
    "id": "ed899a2f4b50b4370feeea94676502b42383c746",
    "short_id": "ed899a2f4b5",
    "title": "Replace sanitize with escape once",
    "author_name": "Example User",
...
    "message": "Replace sanitize with escape once",
    "parent_ids": [
      "6104942438c14ec7bd21c6cd5bd995272b3faff6"
    ],
    "web_url": "https://gitlab.example.com/janedoe/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746",
    "trailers": {},
    "extended_trailers": {}
  },
...
]

 

특정 commit 의 id 를 알 수 있다면 아래 API를 통해 바로 해당 commit의 diff를 긁어올 수 있습니다.
https://docs.gitlab.com/ee/api/commits.html#get-the-diff-of-a-commit

response는 아래와 같습니다.

[
  {
    "diff": "@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
    "new_path": "doc/update/5.4-to-6.0.md",
    "old_path": "doc/update/5.4-to-6.0.md",
    "a_mode": null,
    "b_mode": "100644",
    "new_file": false,
    "renamed_file": false,
    "deleted_file": false
  }
]

 

commit id 를 알아낼 방법도 있고, diff를 알아낼 방법도 있으니 리뷰를 받아와서 comment 를 바로 달면 될 것 같았는데, commit 에 대해 리뷰를 달려고 하니 애매한 점이 하나 있었습니다.

 

commit 에 대해서 사용자의 리뷰는 comment라는 형태로 달린다는 사실을 알게 되었습니다.
https://docs.gitlab.com/ee/api/commits.html#post-comment-to-commit

 

이 도구의 목적 상 MergeRequest에 들어가서 전체 commit들에 대한 리뷰를 보는 게 낫다는 생각이 들어서
GitLab MergeRequest API 를 찾아보게 되었습니다.

GitLab MR API 호출 후 Diff 가져오기

아래 API를 참고해서 현재 열려있는 MR 들의 목록을 갖고 왔습니다.
https://docs.gitlab.com/ee/api/merge_requests.html#list-project-merge-requests

 

response의 예시를 보면 아래와 같은데,
여기서 제가 필요로 하는 건 iid 입니다.
iid를 통해 MR에 속한 각 commit 들의 diff를 가지고 올 수 있습니다.

[
  {
    "id": 1,
    "iid": 1,
    "project_id": 3,
    "title": "test1",
    "description": "fixed login page css paddings",
...
  }
]
...

 

diff는 GitLab 콘솔 화면에서 이렇게 보여집니다.

diff를 들고 오면 이제 이 diff를 프롬프트와 함께 아래와 같이 LLM API로 요청을 보낼 수 있습니다.

 

"당신은 30년 경력의 전문 파이썬 개발자입니다. 아래 코드를 리뷰해주세요.
{코드 내용}
제대로 리뷰를 해주시면 100달러의 팁을 드리겠습니다."

 

LLM API 에서 받은 리뷰 결과를 GitLab MR Discussion으로 남기기

 

commit을 한후 이 내용을 프롬프트와 함께 실어 LLM API에 보냈더니 아래와 같은 답을 받았다고 해보겠습니다.

다음은 test/testfile2.py 에 대한 코드 리뷰 입니다. :

이 코드는 보안 상의 문제점이 존재합니다. 어떠한 개인이든 그의 핸드폰 번호를 공개하는 것은 정보 보호 정책을 위반하게 됩니다. 이는 해당 사용자의 개인 정보를 보호하지 못하고, 이를 악용할 수 있는 가능성이 있는 문제가 존재합니다.

▶ 수정 사항 핸드폰 번호 등의 개인 정보는 코드 내에 직접적으로 드러나지 않도록 합니다. 만약 코드에서 필요한 경우, 적절히 수집하고 암호화/해시화를 해서 보관하는 것이 중요합니다. 그리고 암호화/해시화된 정보를 이용할 시에는 해당 정보를 정상적으로 복원할 수 있는 방법이 구현되어야 합니다.

본 코드는 아래와 같이 수정될 수 있습니다:

phone_number = '0100000000' # 혹시 필요하다면, 이 정보는 암호화/해시화 된 정보로 대체합니다.
print("홍길동의 핸드폰 번호는 -")


또한, 개인 정보는 외부에서 입력을 받아 처리하거나, 환경 변수 등을 사용하여 코드 내에 직접적으로 노출시키지 않는 것이 일반적인 방법입니다.

개인 정보의 보호는 개발자의 기본적인 윤리이며, 법적인 문제로 이어질 수도 있으므로 반드시 신경쓸 부분입니다.필요한 경우 외부에서 입력받거나, 환경 변수 등을 사용해보세요.

 

이 내용을 새로 생성할 예정이거나 열려 있는 MR에 Discussion으로 남겨야 합니다.

 

해당 API는 아래 문서를 참고 했습니다.
https://docs.gitlab.com/ee/api/discussions.html#create-new-merge-request-thread

 

이런 API들을 사용해서 MR 을 찾아서 diff 를 갖고오고 이를 LLM API 에 보내서 리뷰를 받아오고 

이를 GitLab MR 에 discussion으로 보내는 파이썬 스크립트를 짰습니다. 

 

그리고 컨테이너에 말아넣고 사용준비를 끝냈습니다. 

 

GitLab CI/CD Runner로 Review job 동작 시키기

 

이제 실제로 commit 시에 GitLab Runner를 통해 위에서 만든 스크립트가 들어있는 컨테이너를 동작시켜야 합니다.

 

GitLab Runner에 대한 얘기를 하면 길어지니, 이미 K8S 클러스터가 있고, runner가 세팅되어 있으며,

GitLab CI/CD에 등록이 되어있다는 전제를 깔겠습니다.

 

그런 전제를 깔고 .gitlab-ci.yml 파일을 만들어보면 아래와 같습니다.

 

  • KUBERNETES_SERVICE_ACCOUNT_OVERWRITE 항목은 우리가 타겟으로 하는 service account를 사용하기 위해 overwrite를 해줍니다.
  • REVIEWER_IMAGE 라는 변수로 실제 사용하기 위한 이미지의 경로도 정의해주고요.
  • CI_PROJECT_ID 변수를 쓴 이유는 현재 내가 작업하고 있는 GitLab의 이 프로젝트의 id가 GitLab API 사용에 필요하기 때문입니다.
  • script에는 리뷰어용 컨테이너 이미지에 들어있는 실행파일을 동작하게 합니다.
  • rules: 부분이 핵심인데요, merge request event에 대해, commit message에 fix가 있을 경우 동작하게 했습니다.
  • job을 늘려서 fix, feat, refactor.. 와 같은 식으로 각각 다르게 반응하도록 했습니다.
  • 프롬프트도 커밋메시지에 맞게 다른 역할을 하도록 조정하였습니다.
stages:
  - review

variables:
  KUBERNETES_SERVICE_ACCOUNT_OVERWRITE: gitlab-runner-XXXX
  REVIEWER_IMAGE: xxxx/code-reviewer:x.x.x


review_for_fix_job:
  variables:
    CI_PROJECT_ID: $CI_PROJECT_ID
  stage: review
  image: $REVIEWER_IMAGE
  script:
    - echo "Reviewing....."
    - ./run_review_tool.sh
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_MESSAGE =~ /fix/i'

 

실제로 commit message에 fix: 를 준 commit 을 하고 merge를 띄우면 아래와 같이 pipeline이 동작하기 시작합니다.

 

그리고 아래와 같이 리뷰의 결과가 MR에 discussion 형태로 달린 것을 볼 수 있습니다.
보안에 대한 부분을 지적해달라는 말을 프롬프트에 썼기 때문에 신경을 쓴 모습을 볼 수 있습니다.

 

 

긴 글 읽어주셔서 감사합니다.

반응형