はじめに

ChatGPTを使用して、マージリクエストのレビューをしてもらいたいという気持ちがあり、
実際にできそうなので構築をしてみる。
作業ログ編で構築したので清書をしたものを記載する。

環境

1
2
3
4
5
6
7
Windows 10 Professional
WSL2 (Ubuntu22.04 LTS)
docker version 23.0.5
Docker Compose version v2.17.3

GitLab
gitlab-runner latest

目指すべきアーキテクチャ

OpenAI API
OpenAI API
MRの内容をリクエスト
MRの内容をリクエスト
レビュー結果のレスポンス
レビュー結果のレスポンス
Text is not SVG - cannot display

ローカルPC上で起動したDockerGitLabコンテナを利用して、MR時にOpenAI APIにMRの内容をリクエストする。

全体シーケンス図

User
User
GitLab
GitLab
MR作成
MR作成
GitLab
Runner
GitLab…
OpenAI
OpenAI
Runner 起動
Runner 起動
MR内容
APIリクエスト
MR内容…
GitLabAPI
MR内容を取得
GitLabAPI…
MR内容レスポンス
MR内容レスポンス
レビュー内容
APIレスポンス
レビュー内容 APIレスポンス
GitLabAPI
レビュー結果を
MRにコメント書き込み
GitLabAPI…
コメント書き込み結果
レスポンス
コメント書き込み結果 レスポンス
Text is not SVG - cannot display

完成後の動作

mr-review-complete

構築

全体の流れ

全体の構築手順としては以下になる。
前提として、Docker, GitLab, gitlab-runnerは構築済みとする。
また、適用するプロジェクトも作成済みとする。

  1. GitLab-runner の登録
  2. GitLabのレビューコメントをするユーザの作成
  3. 作成したユーザのPersonal Access Tokenの取得
  4. 作成したユーザに対してレビュー対象のプロジェクトを見られるようにする。
  5. 取得した Personal Access TokenをCI/CDの変数へ設定
  6. OpenAIのAPIKeyもCI/CDの変数へ設定
  7. .gitlab-ci.ymlの作成およびAPIのリクエスト用のpythonプログラム作成
  8. 実際にMRを作ってレビューしてもらう。

前提: GitLabのDockerの設定

※以前設定したものと同様ではあるが、compose.ymlを記載しておく。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
services:
  gitlab:
    build: ./gitlab
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG:
        external_url 'http://gitlab.example.com/'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    ports:
      - '80:80'
      - '443:443'
      - '22:22'
    shm_size: '256m'
    restart: unless-stopped
  runner:
    image: 'gitlab/gitlab-runner:latest'
    restart: always
    volumes:
      - ./srv/gitlab-runner/config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - "8093:8093"
    links:
      - "gitlab:gitlab.example.com"

1. GitLab-runner の登録

  1. gitlab-runner コンテナへ入る。
1
docker compose exec runner bash
  1. gitlab-runner register を実行する
1
root@1ce083816d04:/# gitlab-runner register
  1. URLtoken を入力する。
    tokenGitlabRunnerをDockerで構築する で記載しているので参照する。

URL はローカルだと、IPアドレスを設定しているのでローカルIPを設定している。
ここは、構築しているURLに依存するので注意

項目名設定値
URLhttp://192.168.0.20/
tokenglrt-ctnzpdPRws3J43rsgP-7
runner namemr-review
executordocker
Docker imagepython:3.11.4-buster
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
root@5e286da6b99a:/# gitlab-runner register
Runtime platform                                    arch=amd64 os=linux pid=40 revision=85586bd1 version=16.0.2
Running in system-mode.                            
                                                   
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://192.168.0.20/
Enter the registration token:
glrt-ctnzpdPRws3J43rsgP-7
Verifying runner... is valid                        runner=ctnzpdPRw
Enter a name for the runner. This is stored only in the local config.toml file:
[5e286da6b99a]: mr-review
Enter an executor: shell, virtualbox, docker+machine, instance, kubernetes, custom, docker, ssh, docker-autoscaler, docker-windows, parallels:
docker
Enter the default Docker image (for example, ruby:2.7):
python:3.11.4-buster
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
 
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml" 
root@5e286da6b99a:/# 

imagepython3.11.4-buster を使用した。DockerHub から使用できるイメージを参照する。

※作成した runnersharedタグを付けておくと、 他のプロジェクトでも使用可能になる。

また、clone_urlは自動的に設定されないため、設定ファイルを編集する。
runnerコンテナの設定ファイルを編集する。
今回は、sudo vi srv/gitlab-runner/config/config.toml で編集する。
コンテナ内のファイルは、 /etc/gitlab-runner/config.toml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[[runners]]
  name = "mr-review"
  url = "http://192.168.0.20/"
+ clone_url = "http://192.168.0.20/"
  id = 4
  token = "glrt-ctnzpdPRws3J43rsgP-7"
  token_obtained_at = 2023-08-05T05:18:41Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  [runners.cache]
    MaxUploadedArchiveSize = 0
  [runners.docker]
    tls_verify = false
    image = "python:3.11.4-buster"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    shm_size = 0

runnerの設定は下記の通り。
runner-setting

2. GitLabのレビューコメントをするユーザの作成

  1. rootユーザでログインをし、AdminArea->New Userを押す。 create-user-1

  2. 下記の設定でユーザを作成する。
    create-user-2

  3. 作成したユーザを編集し、パスワードを更新する。 create-user-3

3. 作成したユーザのPersonal Access Tokenの取得

  1. 作成したユーザでログインをする。
    get-pat-1

  2. パスワード変更画面で変更する。
    get-pat-2

  3. ログインできる。
    get-pat-3

  4. 左上のアイコンから、Preferences を押す。
    get-pat-4

  5. Access Tokens を押す。
    get-pat-5

  6. Personal Access Tokensから、 Token nameの入力と Select Scopes -> apiを選択し、 Create personal access token を押す。
    get-pat-6

  7. 作成した Tokenが表示されることを確認する。
    get-pat-7

  8. 上部に Personal Access Tokenが表示されるので、これをメモしておく。
    get-pat-8

4. 作成したユーザに対してレビュー対象のプロジェクトを見られるようにする。

※プロジェクトの編集権限を持っているユーザでログインをする。

  1. Manage -> Members から Invite members を押す。 invite-member-project.png

  2. ChatGPT-Review-BOTを選択し、Maintainer権限を与え、Inviteを押す。
    invite-member-project.png

  3. 招待ができたことを確認する。
    invite-member-project.png

  4. ChatGPT-Review-BOTでログインしたときに、プロジェクトが見られることを確認する。
    invite-member-project.png

5. 取得したPersonal Access TokenをCI/CDの変数へ設定

※プロジェクトの編集権限を持っているユーザでログインをする。

  1. 設定したいプロジェクトのページに移動する。

  2. Settings -> CI/CD を押す。
    setting-ci-cd-variables-1

  3. Variables より、 Add variableを押す。
    setting-ci-cd-variables-2

  4. Key, Valueをセットし、Add variableを押す。
    setting-ci-cd-variables-3
    Protect variable はチェックを外しておき、Mask variableはチェックを付けておく。

  5. 設定が反映されていることを確認する。
    setting-ci-cd-variables-4

6. OpenAI のAPIkeyもCI/CDの変数へ設定

上記と同じ手順で、今度はOpenAIAPI Keyを設定する。

  1. OpenAIAPIKeyを設定する。
    setting-ci-cd-variables-for-openai-1

  2. 設定が反映されていることを確認する。
    setting-ci-cd-variables-for-openai-2

7. .gitlab-ci.ymlの作成およびAPIのリクエスト用のpythonプログラム作成

  1. プロジェクトをクローンする。

  2. .gitlab-ci.ymlを作成する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
before_script:
  - python -V
  - pip install virtualenv
  - virtualenv venv
  - source venv/bin/activate

build:
  script:
    - echo Hello,World

mr-review-gpt:
  stage: test
  script:
    - pip install -r requirements.txt
    - python mr_review.py
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  # - if: $CI_MERGE_REQUEST_ID 
  # (https://gitlab-docs.creationline.com/ee/ci/parent_child_pipelines.html) でもOK

test 用で Hello,Worldも用意している。
3. requirements.txtを作成する。

1
2
requests
openai
  1. mr_review.pyを作成する。
    gitlab_url = 'http://192.168.0.20' の部分だけ注意。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import openai
import requests
import os

# GitLabのURL、アクセストークン、プロジェクトID、マージリクエストのIIDを指定します
gitlab_url = 'http://192.168.0.20'
access_token = os.environ.get('PERSONAL_ACCESS_TOKEN')
project_id = os.environ.get('CI_PROJECT_ID')
merge_request_iid = os.environ.get('CI_MERGE_REQUEST_IID')

# OpenAIのAPI_KEYとModelを指定します
openai.api_key = os.environ.get('OPENAI_API_KEY')
model = 'gpt-3.5-turbo'

# リクエストヘッダーにアクセストークンを設定します
headers = {'PRIVATE-TOKEN': access_token}

# マージリクエストの変更内容を取得するAPIエンドポイントを構築します
gitlab_api_url = f'{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/changes'

# APIリクエストを送信してマージリクエストの変更内容を取得します
mr_changes_response = requests.get(gitlab_api_url, headers=headers)

# レスポンスのJSONデータから変更内容を抽出します
mr_changes_data  = mr_changes_response.json()

code_changes = mr_changes_data['changes']

print(code_changes)

response = openai.ChatCompletion.create(
        model=model,
        messages = [
          {"role": "system", "content": "あなたはプログラミングのスペシャリストでコードレビューをするアシスタントです。"},
          {"role": "user", "content":f"次のコード変更を分析し、修正が必要な問題がある場合は見つけます。 コードの変更は git diff 表記で行われ、- で始まる行が削除され、+ で始まる行が追加されます。" +
                 f": {code_changes}",
          }
        ],
        max_tokens=2000,
)

analysis = f"This comment is auto generated by OpenAI {model} : {response['choices'][0]['message']['content']} "

print(analysis)
# Make comment on merge request with the analysis
comment_url = f"{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/discussions"
comment_data = {
    "body": analysis
}
comment_response = requests.post(comment_url, headers=headers, data=comment_data)

print(comment_response)
  1. 上記の修正を main ブランチにプッシュしておく。
    ※全体のファイル配置はこんな感じ。
    project
    project-gitlab

  2. Jobが成功することを確認する。
    success-job

8. 実際にMRを作ってレビューしてもらう。

  1. 下記のようなファイルを追加で作成する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def fibonacci(n):
    if n <= 0:
        return []
    elif n == 1:
        return [0]
    elif n == 2:
        return [0, 1]
    else:
        fib_sequence = [0, 1]
        for i in range(2, n):
            next_fib = fib_sequence[i-1] + fib_sequence[i-2]
            fib_sequence.append(next_fib)
        return fib_sequence

# フィボナッチ数列の長さを指定して計算
length = int(input("フィボナッチ数列の長さを入力してください: "))
sequence = fibonacci(length)
print(sequence)

ChatGPTに作成してもらったフィボナッチ数列を計算するプログラムを作成する。
fibonacci.py で作成する。

  1. feat-add-fibonacci というブランチを作成してプッシュ
  2. MRを作成する。 review-success-gpt-1
    上記のようにジョブが走っていることがわかる。
  3. ChatGPT-Review-BOTのユーザがコメントできていることを確認。
    review-success-gpt-2

できた!

注意するべき点について

  • プロジェクトのユーザの権限
  • CI/CD variableの変数の設定 (保護の有無)
  • .gitlab-ci.ymlの設定について
  • url, clone_urlの設定について

上記を気をつけておけば、構築できるはず…。
デバッグなどは、作業ログ編を見ていただくと解決できると思う。

改善したい点

  • MRの変更があるたびにjobが走るので、その度にすべての変更点のレビューが再度されてしまう。
    →この辺りどうにかうまい仕組みにしたい。運用で解決という手もあるが…。

  • コメントをしても、ChatGPT-Review-BOTは反応してくれない。

  • Assignee, Reviewerに特定のユーザが設定された場合に限りレビューするようにしたい。

参考

作業ログ編の#参考を参照

おわりに

作業ログとして残したものを清書版として記事にした。
現在では、GitHubActionでこういう物があると思うので、あまり需要がなさそうだが、GitLabCI/CDの使い方が少しだけ分かって嬉しい。
もう少し活用できるようにスキルアップしたい。