1+ import os
2+ import re
3+ import shutil
4+ import subprocess
5+ import time
6+
17import requests
28from retrying import retry
39from config .config import *
410from utils .logger import log
11+ from utils .tools import run_command
12+
513
614class GitlabMergeRequestFetcher :
715 def __init__ (self , project_id , merge_request_iid ):
816 self .project_id = project_id
917 self .iid = merge_request_iid
18+ self ._changes_cache = None
19+ self ._file_content_cache = {}
20+ self ._info_cache = None
1021
1122 @retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
12- def get_changes (self ):
23+ def get_changes (self , force = False ):
1324 """
1425 Get the changes of the merge request
1526 :return: changes
1627 """
28+ if self ._changes_cache and not force :
29+ return self ._changes_cache
1730 # URL for the GitLab API endpoint
1831 url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } /merge_requests/{ self .iid } /changes"
1932
@@ -27,12 +40,49 @@ def get_changes(self):
2740
2841 # Check if the request was successful
2942 if response .status_code == 200 :
43+ self ._changes_cache = response .json ()["changes" ]
3044 return response .json ()["changes" ]
3145 else :
3246 return None
3347
3448 @retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
35- def get_info (self ):
49+ # 获取文件内容
50+ def get_file_content (self , file_path , branch_name = 'main' , force = False ):
51+ """
52+ Get the content of the file
53+ :param file_path: The path of the file
54+ :return: The content of the file
55+ """
56+ # 对file_path中的'/'转换为'%2F'
57+ file_path = file_path .replace ('/' , '%2F' )
58+ if file_path in self ._file_content_cache and not force :
59+ return self ._file_content_cache [file_path ]
60+ # URL for the GitLab API endpoint
61+ url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } /repository/files/{ file_path } /raw?ref={ branch_name } "
62+
63+ # Headers for the request
64+ headers = {
65+ "PRIVATE-TOKEN" : gitlab_private_token
66+ }
67+
68+ # Make the GET request
69+ response = requests .get (url , headers = headers )
70+
71+ # Check if the request was successful
72+ if response .status_code == 200 :
73+ self ._file_content_cache [file_path ] = response .text
74+ return response .text
75+ else :
76+ return None
77+
78+ @retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
79+ def get_info (self , force = False ):
80+ """
81+ Get the merge request information
82+ :return: Merge request information
83+ """
84+ if self ._info_cache and not force :
85+ return self ._info_cache
3686 # URL for the GitLab API endpoint
3787 url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } /merge_requests/{ self .iid } "
3888
@@ -44,8 +94,112 @@ def get_info(self):
4494 # Make the GET request
4595 response = requests .get (url , headers = headers )
4696
97+ # Check if the request was successful
98+ if response .status_code == 200 :
99+ self ._info_cache = response .json ()
100+ return response .json ()
101+ else :
102+ return None
103+
104+ # gitlab仓库clone和管理
105+ class GitlabRepoManager :
106+ def __init__ (self , project_id , branch_name = "" ):
107+ self .project_id = project_id
108+ self .timestamp = int (time .time () * 1000 )
109+ self .repo_path = f"./repo/{ self .project_id } _{ self .timestamp } "
110+ self .has_cloned = False
111+
112+ def get_info (self ):
113+ """
114+ Get the project information
115+ :return: Project information
116+ """
117+ # URL for the GitLab API endpoint
118+ url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } "
119+
120+ # Headers for the request
121+ headers = {
122+ "PRIVATE-TOKEN" : gitlab_private_token
123+ }
124+
125+ # Make the GET request
126+ response = requests .get (url , headers = headers )
127+
47128 # Check if the request was successful
48129 if response .status_code == 200 :
49130 return response .json ()
50131 else :
51- return None
132+ return None
133+
134+ @retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
135+ def shallow_clone (self , branch_name = "main" ):
136+ """
137+ Perform a shallow clone of the repository
138+ param branch_name: The name of the branch to clone
139+ """
140+ # If the target directory exists, remove it
141+ self .delete_repo ()
142+
143+ # Build the authenticated URL
144+ authenticated_url = self ._build_authenticated_url (self .get_info ()["http_url_to_repo" ])
145+
146+ # Build the Git command
147+ command = ["git" , "clone" , authenticated_url , "--depth" , "1" ]
148+ if branch_name :
149+ command .extend (["--branch" , branch_name ])
150+ command .extend ([self .repo_path + "/" + str (branch_name )])
151+ else :
152+ command .extend ([self .repo_path + "/default" ])
153+ # command 添加clone到的位置:
154+ if run_command (command ) != 0 :
155+ log .error ("Failed to clone the repository" )
156+ self .has_cloned = True
157+
158+ # 切换分支
159+ def checkout_branch (self , branch_name , force = False ):
160+ # Build the Git command
161+ if not self .has_cloned :
162+ self .shallow_clone (branch_name )
163+ else :
164+ # 检查是否已经在目标分支上
165+ if not force and os .path .exists (self .repo_path + "/" + str (branch_name ) + "/.git" ):
166+ return
167+ else :
168+ self .shallow_clone (branch_name )
169+
170+ # 删除库
171+ def delete_repo (self ):
172+ if os .path .exists (self .repo_path ):
173+ shutil .rmtree (self .repo_path )
174+
175+ # 查找相关文件列表
176+ def find_files_by_keyword (self , keyword , branch_name = "main" ):
177+ matching_files = []
178+ regex = re .compile (keyword )
179+ self .checkout_branch (branch_name )
180+ for root , _ , files in os .walk (self .repo_path + "/" + str (branch_name )):
181+ for file in files :
182+ file_path = os .path .join (root , file )
183+ try :
184+ with open (file_path , 'r' , encoding = 'utf-8' ) as f :
185+ content = f .read ()
186+ if regex .search (content ):
187+ matching_files .append (file_path )
188+ except (UnicodeDecodeError , FileNotFoundError , PermissionError ):
189+ # 跳过无法读取的文件
190+ continue
191+
192+ return matching_files
193+
194+
195+ # 构建带有身份验证信息的 URL
196+ def _build_authenticated_url (self , repo_url ):
197+ # 如果 URL 使用 https
198+ token = gitlab_private_token
199+ if repo_url .startswith ("https://" ):
200+ return f"https://oauth2:{ token } @{ repo_url [8 :]} "
201+ # 如果 URL 使用 http
202+ elif repo_url .startswith ("http://" ):
203+ return f"http://oauth2:{ token } @{ repo_url [7 :]} "
204+ else :
205+ raise ValueError ("Unsupported URL scheme" )
0 commit comments