はじめに
完成記事
※この記事は作業ログになりますので、試行錯誤の記録になります。
ChatGPT
を使用して、マージリクエストのレビューをしてもらいたいという気持ちがあり、
実際にできそうなので構築をしてみる。
環境
Windows 10 Professional
WSL2 (Ubuntu22.04 LTS)
docker version 23.0.5
Docker Compose version v2.17.3
GitLab
gitlab-runner latest
GitLab-runner の登録
- gitlab-runner コンテナへ入る。
docker compose exec runner bash
gitlab-runner register
を実行する
root@1ce083816d04:/# gitlab-runner register
- URL と token を入力する。
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://gitlab.example.com/
Enter the registration token:
XXXXXXXXXXXX
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://gitlab.example.com/
Enter the registration token:
XXXXXXXXXXXX
Verifying runner... is valid runner=oLvY-r6z9
Enter a name for the runner. This is stored only in the local config.toml file:
[0fa8a2573a3b]: mr-review
Enter an executor: ssh, docker-autoscaler, docker+machine, instance, custom, docker, parallels, kubernetes, docker-windows, shell, virtualbox:
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@0fa8a2573a3b:/#
項目名 | 設定値 |
---|---|
URL | http://gitlab.example.com/ |
token | XXXXXXXXXXXX |
runner name | mr-review |
executor | docker |
Docker image | python:3.11.4-buster |
image
は python3.11.4-buster
を使用した。DockerHub から使用できるイメージを参照する。
Personal Access Tokenを取得する
CI/CDの設定
プロジェクト → 「CI/CD」から「Variables」で変数を設定する。
エラー
Docker が実行できないみたいなエラー
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"
ホスト側の docker.sock
をマウントする必要がある。
そうすることで、ホスト側の Docker のソケットを共有し、gitlab-runner
コンテナからもdocker
が使用できる。
gitlab.example.com で gitlab のコンテナアクセスする。
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"
アクセスできるように
docker compose exec runner bash
root@d824f2d5f224:/# curl http://gitlab.example.com/
<html><body>You are being <a href="http://gitlab.example.com/users/sign_in">redirected</a>.</body></html>root@d824f2d5f224:/#
※しかし、後述の手順のエラーを解消できるわけではないので、必ずしも設定する必要はなさそう。
JobでGit cloneができないエラー
CI/CD
が走ったあとに、Job
では下記のエラーが出た。
Running with gitlab-runner 16.0.2 (85586bd1)
on 7bc6a94280ca jLhReocfx, system ID: r_clhhKBEcv5mo
Preparing the "docker" executor
00:04
Using Docker executor with image python:3.11.4-buster ...
Pulling docker image python:3.11.4-buster ...
Using docker image sha256:5bf9a636079f0257fa52c3bbed42ec29d3b975cfd1ef2db2016c1bf598492d97 for python:3.11.4-buster with digest python@sha256:3a19b4d6ce4402d11bb19aa11416e4a262a60a57707a5cda5787a81285df2666 ...
Preparing environment
00:01
Running on runner-jlhreocfx-project-3-concurrent-0 via d824f2d5f224...
Getting source from Git repository
00:01
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/root/mr-review-test/.git/
fatal: unable to access 'http://gitlab.example.com/root/mr-review-test.git/': Failed to connect to gitlab.example.com port 80 after 112 ms: Couldn't connect to server
ERROR: Job failed: exit code 1
gitlab.example.com
の ドメインに対する git clone
ができていないみたい。
runner
コンテナで git clone
ができるのかを調査。
docker compose exec runner bash
git clone http://gitlab.example.com/root/mr-review-test.git/
root@d824f2d5f224:/# git clone http://gitlab.example.com/root/mr-review-test.git/
Cloning into 'mr-review-test'...
Username for 'http://gitlab.example.com':
いけるみたい。gitlab-runner
ユーザでプロジェクトが見えない可能性?
→gitlab-runner
で使用しているGitのユーザは何なのか?
※プロジェクトの可視性を public にしてみたけどだめだった。
解決方法url
, clone_url
をホストの IP にした。
ドメインだとうまく解決できないみたい。runner
のコンテナに入ると gitlab.example.com
はうまく行っているのだが・・・。
- 参考
https://qiita.com/pike3/items/0953f167cf67058eb8a7 - 参考
https://gitlab-docs.creationline.com/runner/faq/
toml
を下記のように修正, url
, clone_url
を修正した。
[[runners]]
name = "7bc6a94280ca"
url = "http://192.168.0.20"
id = 3
token = "glrt-jLhReocfxa-C7VJbP1ws"
token_obtained_at = 2023-06-14T10:36:53Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
clone_url = "http://192.168.0.20"
[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
.gitlab-ci.yml
stages:
- test
test:
stage: test
script:
- python mr_review.py
build:
script:
- echo Hello,World
# - python mr_review.py
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
script: echo "Dummy job for merge-request"
うまく行っているみたい。
test
のジョブ
Running with gitlab-runner 16.0.2 (85586bd1)
on 7bc6a94280ca jLhReocfx, system ID: r_clhhKBEcv5mo
Preparing the "docker" executor
00:04
Using Docker executor with image python:3.11.4-buster ...
Pulling docker image python:3.11.4-buster ...
Using docker image sha256:5bf9a636079f0257fa52c3bbed42ec29d3b975cfd1ef2db2016c1bf598492d97 for python:3.11.4-buster with digest python@sha256:3a19b4d6ce4402d11bb19aa11416e4a262a60a57707a5cda5787a81285df2666 ...
Preparing environment
00:01
Running on runner-jlhreocfx-project-3-concurrent-0 via d824f2d5f224...
Getting source from Git repository
00:01
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/root/mr-review-test/.git/
Checking out da64e4af as detached HEAD (ref is main)...
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:02
Using docker image sha256:5bf9a636079f0257fa52c3bbed42ec29d3b975cfd1ef2db2016c1bf598492d97 for python:3.11.4-buster with digest python@sha256:3a19b4d6ce4402d11bb19aa11416e4a262a60a57707a5cda5787a81285df2666 ...
$ python mr_review.py
Hello,World
Job succeeded
build
のジョブ
Running with gitlab-runner 16.0.2 (85586bd1)
on 7bc6a94280ca jLhReocfx, system ID: r_clhhKBEcv5mo
Preparing the "docker" executor
00:04
Using Docker executor with image python:3.11.4-buster ...
Pulling docker image python:3.11.4-buster ...
Using docker image sha256:5bf9a636079f0257fa52c3bbed42ec29d3b975cfd1ef2db2016c1bf598492d97 for python:3.11.4-buster with digest python@sha256:3a19b4d6ce4402d11bb19aa11416e4a262a60a57707a5cda5787a81285df2666 ...
Preparing environment
00:01
Running on runner-jlhreocfx-project-3-concurrent-0 via d824f2d5f224...
Getting source from Git repository
00:02
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/root/mr-review-test/.git/
Checking out da64e4af as detached HEAD (ref is main)...
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:01
Using docker image sha256:5bf9a636079f0257fa52c3bbed42ec29d3b975cfd1ef2db2016c1bf598492d97 for python:3.11.4-buster with digest python@sha256:3a19b4d6ce4402d11bb19aa11416e4a262a60a57707a5cda5787a81285df2666 ...
$ echo Hello,World
Hello,World
Job succeeded
GitLabAPI を使うテスト
$ python mr_review.py
Traceback (most recent call last):
File "/builds/root/mr-review-test/mr_review.py", line 1, in <module>
import requests
ModuleNotFoundError: No module named 'requests'
pip install
する必要がある。
test:
stage: test
script:
+ - pip install -r requirements.txt
- python mr_review.py
build:
script:
- echo Hello,World
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
script: echo "Dummy job for merge-request"
上記を追加する。
requirements.txt
は下記のとおりに作成した。
requests
openai
yml
を書き直し。
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"'
これで、 mr-review-gpt
はマージリクエストを作成したときのみ実行される。
マージリクエストを作成する
適当なブランチを作ってプッシュし、マージリクエストを作成する。
$ python mr_review.py
Traceback (most recent call last):
File "/builds/root/mr-review-test/mr_review.py", line 16, in <module>
api_url = f'{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/changes'
^^^^^^^^^^^^^^^^^
NameError: name 'merge_request_iid' is not defined. Did you mean: 'merge_request_id'?
エラーが出るのでスクリプトを見直す。
merge_request_id
なのに、 merge_request_iid
で参照していたので修正した。
- api_url = f'{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/changes'
+ api_url = f'{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_id}/changes'
gitlab.example.comが参照できないエラー
requests.exceptions.ConnectionError: HTTPConnectionPool(host='gitlab.example.com', port=80): Max retries exceeded with url: /api/v4/projects/3/merge_requests/4/changes (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f228ca49510>: Failed to establish a new connection: [Errno 111] Connection refused'))
gitlab.example.com
が参照できないエラー。
うーん、やっぱりホストの IP アドレスで直接参照させるのが良さそうか。
というわけでスクリプトのurl
を変える。
Successfully installed aiohttp-3.8.4 aiosignal-1.3.1 async-timeout-4.0.2 attrs-23.1.0 certifi-2023.5.7 charset-normalizer-3.1.0 frozenlist-1.3.3 idna-3.4 multidict-6.0.4 openai-0.27.8 requests-2.31.0 tqdm-4.65.0 urllib3-2.0.3 yarl-1.9.2
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
$ python mr_review.py
{'message': '404 Project Not Found'}
Job succeeded
できた、が Project Not Found
になった・・・!
あと、pip
をroot
で実行しないで、venv
使ってみたいなのが出た。
venv を使うように修正
とりあえず、 venv
使うように yml
を修正する。
+before_script:
+ - python -V
+ - pip install virtualenv
+ - virtualenv venv
+ - source venv/bin/activate
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"'
これで良さそう。
Project Not Found を直す。
おそらく、 Personal Access Token
が悪そうか?
下記でアクセストークンとかを表示させてみた。
print(access_token)
print(project_id)
print(merge_request_id)
$ python mr_review.py
None
3
4
{'message': '404 Project Not Found'}
コンソールを見ると、 None
になっていた。
これが原因みたい。
どうやら、「CI/CD」の設定の「Variables」で下記のチェックが入っていたので変数が展開されなかったみたい。
変更すると下記のように表示された。
$ python mr_review.py
[MASKED]glpat-
3
4
{'error': 'insufficient_scope', 'error_description': 'The request requires higher privileges than provided by the access token.', 'scope': 'api read_api'}
今度は、アクセストークンの権限が足りないみたい。
read_repository, write_repository, admin_mode
しか許可していないので・・・。
最終的に、 MR に書き込みをするので、 「api」の権限を与える。
api, read_api, read_repository, write_repository, admin_mode
が最終的な権限。
変わらずだめみたい。
$ python mr_review.py
[MASKED]glpat-
3
4
{'message': '404 Not found'}
https://gitlab.com/gitlab-org/gitlab-foss/-/issues/29022
↑ みたいに、同じ人がいるみたいだ。
解決方法は・・・?
curl
でアクセスできるか試す。
curl --header "PRIVATE-TOKEN: <TOKEN>" <API_ENDPOINT>
$ curl --header "PRIVATE-TOKEN: glpat-M32nMy-Axbe6q-hzqCd8" http://192.168.0.20/api/v4/projects/3/merge_requests/4/changes
{"message":"404 Not found"}
curl
でもアクセスできないみたい。
この API URL になにか問題があるのかを調査。
ChatGPT にきいたやつなので、情報が古いかもと思い、API の Reference を見る。
https://docs.gitlab.com/ee/api/rest/
https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mrGET /projects/:id/merge_requests/:merge_request_iid
みたい。
curl "http://192.168.0.20/api/v4/projects?private_token=<your_access_token>"
curl --header "PRIVATE-TOKEN: <your_access_token>" "http://192.168.0.20/api/v4/projects"
どっちもアクセスできた。JSON が返ってきた。
PersonalAccessToken
などの問題ではなさそう。
curl --header "PRIVATE-TOKEN: <token>" "http://192.168.0.20/api/v4/projects/3/merge_requests/3/changes"
projects
の ID と MR の ID を適当に変えて実行してみた。
※Web 上で見える ID と JOB で取得される、 CI_MERGE_REQUEST_ID
が異なっているから。
適当に変えたら取れた。
ということは、JOB で取得される MR の ID が悪そうだ。
API の URL を見る限り、 CI_MERGE_REQUEST_ID
ではなく、 CI_MERGE_REQUEST_IID
を使う必要がありそうだ。
ChatGPT に相違点を聞いてみる。
CI_MERGE_REQUEST_IDとCI_MERGE_REQUEST_IIDは、GitLab CI/CDの環境変数であり、マージリクエストに関連する情報を提供します。以下にそれぞれの変数の意味と異なる点を説明します。
CI_MERGE_REQUEST_ID: この環境変数は、現在実行中のジョブが関連付けられているマージリクエストのID(Identifiers)を表します。マージリクエストIDは、プロジェクト内で一意の数値であり、マージリクエストを一意に特定するために使用されます。
CI_MERGE_REQUEST_IID: この環境変数は、現在実行中のジョブが関連付けられているマージリクエストのIID(Internal ID)を表します。マージリクエストIIDは、プロジェクト内でのマージリクエストの相対的な識別子であり、そのプロジェクト内で一意です。マージリクエストIIDは、特定のプロジェクトのマージリクエストの連続的なナンバリングを表します。
異なる点:
CI_MERGE_REQUEST_IDは、プロジェクト全体で一意のIDですが、CI_MERGE_REQUEST_IIDはプロジェクト内で一意です。
CI_MERGE_REQUEST_IDは、他のプロジェクトとのマージリクエストIDと区別するために使用される一意の数値です。一方、CI_MERGE_REQUEST_IIDは、プロジェクト内のマージリクエストの相対的な識別子です。
CI_MERGE_REQUEST_IDは数値の形式を取りますが、CI_MERGE_REQUEST_IIDは文字列の形式を取ります。
したがって、CI_MERGE_REQUEST_IDは他のプロジェクトとのマージリクエストの一意性を確保するために使用され、CI_MERGE_REQUEST_IIDは特定のプロジェクト内のマージリクエストを相対的に識別するために使用されます。
なるほどな。
というわけでコードを直した。
-merge_request_id = os.environ.get('CI_MERGE_REQUEST_ID')
+merge_request_id = os.environ.get('CI_MERGE_REQUEST_IID')
最終的に下記のように修正
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')
print(access_token)
print(project_id)
print(merge_request_iid)
# リクエストヘッダーにアクセストークンを設定します
headers = {'PRIVATE-TOKEN': access_token}
# マージリクエストの変更内容を取得するAPIエンドポイントを構築します
api_url = f'{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/changes'
print(api_url)
# APIリクエストを送信してマージリクエストの変更内容を取得します
response = requests.get(api_url, headers=headers)
# レスポンスのJSONデータから変更内容を抽出します
changes = response.json()
# 変更内容を表示します
print(changes)
↓ いけた!
http://192.168.0.20/api/v4/projects/3/merge_requests/3/changes
{'id': 5, 'iid': 3, 'project_id': 3, 'title': 'Refactor mr review', 'description': '', 'state': 'opened', 'created_at': '2023-06-18T14:10:12.610Z', 'updated_at': '2023-06-18T14:40:04.736Z', 'merged_by': None, 'merge_user': None, 'merged_at': None, 'closed_by': None, 'closed_at': None, 'target_branch': 'main', 'source_branch': 'refactor-mr-review', 'user_notes_count': 0, 'upvotes': 0, 'downvotes': 0, 'author': {'id': 1, 'username': 'root', 'name': 'Administrator', 'state': 'active', 'avatar_url': 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', 'web_url': 'http://gitlab.example.com/root'}, 'assignees': [], 'assignee': None, 'reviewers': [], 'source_project_id': 3, 'target_project_id': 3, 'labels': [], 'draft': False, 'work_in_progress': False, 'milestone': None, 'merge_when_pipeline_succeeds': False, 'merge_status': 'can_be_merged', 'detailed_merge_status': 'mergeable', 'sha': '0d9f59177390bc8c4c96f85800b136cf4775f053', 'merge_commit_sha': None, 'squash_commit_sha': None, 'discussion_locked': None, 'should_remove_source_branch': None, 'force_remove_source_branch': True, 'reference': '!3', 'references': {'short': '!3', 'relative': '!3', 'full': 'root/mr-review-test!3'}, 'web_url': 'http://gitlab.example.com/root/mr-review-test/-/merge_requests/3', 'time_stats': {'time_estimate': 0, 'total_time_spent': 0, 'human_time_estimate': None, 'human_total_time_spent': None}, 'squash': False, 'squash_on_merge': False, 'task_completion_status': {'count': 0, 'completed_count': 0}, 'has_conflicts': False, 'blocking_discussions_resolved': True, 'subscribed': True, 'changes_count': '2', 'latest_build_started_at': '2023-06-18T14:40:59.771Z', 'latest_build_finished_at': None, 'first_deployed_to_production_at': None, 'pipeline': {'id': 49, 'iid': 34, 'project_id': 3, 'sha': '0d9f59177390bc8c4c96f85800b136cf4775f053', 'ref': 'refs/merge-requests/3/head', 'status': 'running', 'source': 'merge_request_event', 'created_at': '2023-06-18T14:40:20.895Z', 'updated_at': '2023-06-18T14:40:59.776Z', 'web_url': 'http://gitlab.example.com/root/mr-review-test/-/pipelines/49'}, 'head_pipeline': {'id': 49, 'iid': 34, 'project_id': 3, 'sha': '0d9f59177390bc8c4c96f85800b136cf4775f053', 'ref': 'refs/merge-requests/3/head', 'status': 'running', 'source': 'merge_request_event', 'created_at': '2023-06-18T14:40:20.895Z', 'updated_at': '2023-06-18T14:40:59.776Z', 'web_url': 'http://gitlab.example.com/root/mr-review-test/-/pipelines/49', 'before_sha': '0000000000000000000000000000000000000000', 'tag': False, 'yaml_errors': None, 'user': {'id': 1, 'username': 'root', 'name': 'Administrator', 'state': 'active', 'avatar_url': 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', 'web_url': 'http://gitlab.example.com/root'}, 'started_at': '2023-06-18T14:40:59.771Z', 'finished_at': None, 'committed_at': None, 'duration': None, 'queued_duration': 38, 'coverage': None, 'detailed_status': {'icon': 'status_running', 'text': 'running', 'label': 'running', 'group': 'running', 'tooltip': 'running', 'has_details': True, 'details_path': '/root/mr-review-test/-/pipelines/49', 'illustration': None, 'favicon': '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png'}}, 'diff_refs': {'base_sha': '55d189ed3b876d106f75d16f247a5c30b154aaea', 'head_sha': '0d9f59177390bc8c4c96f85800b136cf4775f053', 'start_sha': '55d189ed3b876d106f75d16f247a5c30b154aaea'}, 'merge_error': None, 'user': {'can_merge': True}, 'changes': [{'diff': '@@ -1,3 +1,9 @@\n+before_script:\n+ - python -V\n+ - pip install virtualenv\n+ - virtualenv venv\n+ - source venv/bin/activate\n+\n build:\n script:\n - echo Hello,World\n@@ -8,4 +14,6 @@ mr-review-gpt:\n - pip install -r requirements.txt\n - python mr_review.py\n rules:\n- - if: \'$CI_PIPELINE_SOURCE == "merge_request_event"\'\n\\ No newline at end of file\n+ - if: \'$CI_PIPELINE_SOURCE == "merge_request_event"\'\n+ # - if: $CI_MERGE_REQUEST_ID \n+ # (https://gitlab-docs.creationline.com/ee/ci/parent_child_pipelines.html) でもOK\n\\ No newline at end of file\n', 'new_path': '.gitlab-ci.yml', 'old_path': '.gitlab-ci.yml', 'a_mode': '100644', 'b_mode': '100644', 'new_file': False, 'renamed_file': False, 'deleted_file': False}, {'diff': "@@ -1,11 +1,17 @@\n import requests\n import os\n \n+# テストととしてこのコメントを書いてみます。\n+\n # GitLabのURL、アクセストークン、プロジェクトID、マージリクエストのIIDを指定します\n-gitlab_url = 'http://gitlab.example.com'\n+gitlab_url = 'http://192.168.0.20'\n access_token = os.environ.get('PERSONAL_ACCESS_TOKEN')\n project_id = os.environ.get('CI_PROJECT_ID')\n-merge_request_id = os.environ.get('CI_MERGE_REQUEST_ID')\n+merge_request_iid = os.environ.get('CI_MERGE_REQUEST_IID')\n+\n+print(access_token)\n+print(project_id)\n+print(merge_request_iid)\n \n # リクエストヘッダーにアクセストークンを設定します\n headers = {'PRIVATE-TOKEN': access_token}\n@@ -13,6 +19,8 @@ headers = {'PRIVATE-TOKEN': access_token}\n # マージリクエストの変更内容を取得するAPIエンドポイントを構築します\n api_url = f'{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/changes'\n \n+print(api_url)\n+\n # APIリクエストを送信してマージリクエストの変更内容を取得します\n response = requests.get(api_url, headers=headers)\n \n", 'new_path': 'mr_review.py', 'old_path': 'mr_review.py', 'a_mode': '100644', 'b_mode': '100644', 'new_file': False, 'renamed_file': False, 'deleted_file': False}], 'overflow': False}
Job succeeded
よしよし。
次は、この変更を ChatGPT に食わせることをやってみる。
変更を送信して ChatGPT にレビューしてもらう。
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']
response = openai.Completion.create(
engine=model,
prompt = f"次のコード変更を分析し、修正が必要な問題がある場合は見つけます。コードの変更は git diff 表記で行われ、- で始まる行が削除され、+ で始まる行が追加されます。" +
f": {code_changes}",
max_tokens=500,
)
analysis = f"This comment is auto generated by OpenAI {model} : {response['choices'][0]['text']} "
# Make comment on merge request with the analysis
comment_url = f"{gitlab_url}/projects/{project_id}/merge_requests/{merge_request_iid}/notes"
comment_data = {
"body": analysis
}
comment_response = requests.post(comment_url, headers=headers, data=comment_data)
print(comment_response)
コードを変更した。
あとは、GitLab の CI/CD に OpenAI の APIKEY を設定する。
openai.error.AuthenticationError: No API key provided. You can set your API key in code using 'openai.api_key = <API-KEY>', or you can set the environment variable OPENAI_API_KEY=<API-KEY>). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = <PATH>'. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.
なんかエラーでた。
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_requestor.py", line 298, in request
resp, got_stream = self._interpret_response(result, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_requestor.py", line 700, in _interpret_response
self._interpret_response_line(
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_requestor.py", line 763, in _interpret_response_line
raise self.handle_error_response(
openai.error.RateLimitError: You exceeded your current quota, please check your plan and billing details.
openai.error.RateLimitError: You exceeded your current quota, please check your plan and billing details.
ということなので、発行したやつを見てみる。
OpenAPI
の API_KEY
にて料金を設定していないみたい。
クレジットカードの設定をしていなかったのでした。Usage Limit
は $10.00
に設定。
これでもう一回 Job
を実行してみる。
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_resources/completion.py", line 25, in create
return super().create(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_resources/abstract/engine_api_resource.py", line 153, in create
response, _, api_key = requestor.request(
^^^^^^^^^^^^^^^^^^
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_requestor.py", line 298, in request
resp, got_stream = self._interpret_response(result, stream)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_requestor.py", line 700, in _interpret_response
self._interpret_response_line(
File "/builds/root/mr-review-test/venv/lib/python3.11/site-packages/openai/api_requestor.py", line 763, in _interpret_response_line
raise self.handle_error_response(
openai.error.InvalidRequestError: This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?
エラーが出た・・・。
CompletionsAPI
のコールの仕方がこれではないらしい。
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}/projects/{project_id}/merge_requests/{merge_request_iid}/notes"
comment_data = {
"body": analysis
}
comment_response = requests.post(comment_url, headers=headers, data=comment_data)
print(comment_response)
This comment is auto generated by OpenAI gpt-3.5-turbo : このコード変更にはいくつかの問題があるようです。
まず、.gitlab-ci.yml ファイルの変更部分で、before_script で virtualenv を作成していますが、その後の script では venv をアクティブ化していません。したがって、ビルドが失敗する可能性があります。
次に、mr_review.py ファイルの変更部分で、未定義の変数 merge_request_iid を使用しています。変数名が間違っているか、定義されていない可能性があります。
また、OpenAI API キーやモデルの指定がありますが、環境変数から取得するように指定されていません。セキュリティ上の理由から、暗号化されたシークレットにアクセスするために環境変数を使用することが推奨されています。
最後に、コメントの投稿に失敗する可能性があります。コメントを投稿するためのAPIエンドポイントが正しくない可能性があるため、確認が必要です。
これらの問題を解決するために、以下の修正を行うことを提案します:
1. .gitlab-ci.yml ファイルの修正
- script で venv をアクティブ化するように追加する
2. mr_review.py ファイルの修正
- merge_request_iid を merge_request_id に変更する
- OpenAI API キーやモデルの指定を環境変数から取得するように修正する
- コメント投稿用のAPIエンドポイントを修正する
以上の修正が必要です。
こんなレスポンスが得られていた。
しかし、コメントには反映されず・・・。
コメントのAPIを見てみる。
/api/v4
のURLになっていなかったので修正する。
-comment_url = f"{gitlab_url}/projects/{project_id}/merge_requests/{merge_request_iid}/notes"
+comment_url = f"{gitlab_url}/api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/discussions"
参考
- GitLab の CI/CD で超重要な rules の全てを理解する
https://dev.classmethod.jp/articles/sample-to-understand-rules/ - pip を用いて、複数の Python ライブラリを一括インストールする
https://take-tech-engineer.com/pip-requirements/ - 【GitLab CI】Gitへの接続失敗でジョブが失敗するときの対処法
https://qiita.com/pike3/items/0953f167cf67058eb8a7 - Troubleshoot GitLab Runner
https://gitlab-docs.creationline.com/runner/faq/ - GitLab CIでPythonコードの静的解析
https://qiita.com/momomo_rimoto/items/cf2b40c0a235a40fc1ae - token access with api returns “404 Project Not Found” on an internal project or public project with member only access to repository or private project
https://gitlab.com/gitlab-org/gitlab-foss/-/issues/29022 - REST API
https://docs.gitlab.com/ee/api/rest/ - Get single MR
https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr - GitLab CI/CD Variables を翻訳しました。
https://qiita.com/ynott/items/4c5085b4cd6221bb71c5 - malywut/gpt_mr_reviewer
https://github.com/malywut/gpt_mr_reviewer/blob/main/mr_reviewer.py - ChatGPT API の使い方
https://note.com/npaka/n/n0dd2b226e8ae#e192f0b5-54af-41d1-84a7-c55e5989a64c - GPT models
https://platform.openai.com/docs/guides/gpt
改善点
- コメントするアカウントを変更する。→ レビューコメント用のユーザを作成し、そのユーザの
Personal Access Token
を使えば良さそう。 - プロジェクト間で
Runner
を共有したい。→Group Runner
,Shared Runner
を使う。 - 現状だと MRに変更があった場合にレビューが都度入る。→その都度コメントされてしまうため。良いレビューの仕方を考えたい。
- ChatGPTがレビューコメントしたものに返信があった場合に、そのレビューを返したい。
Reviewer
が特定のユーザの場合のみにChatGPT
のレビューを有効にしたい。→.gitlab-ci.yml
を変えればよい?
おわりに
作業ログという形で今回の作業のエラー解消や構築までの流れを記録しておいた。
これを基に、スムーズに構築できる記事を次回は記載する。