13
13
14
14
class GitlabMergeRequestFetcher :
15
15
def __init__ (self , project_id , merge_request_iid ):
16
+ """
17
+ Initialize a GitLab merge request fetcher.
18
+
19
+ Assigns the project identifier and merge request IID, and sets up caches for
20
+ tracking changes, file content, and merge request information.
21
+
22
+ Parameters:
23
+ project_id: The unique identifier for the GitLab project.
24
+ merge_request_iid: The internal identifier for the merge request.
25
+ """
16
26
self .project_id = project_id
17
27
self .iid = merge_request_iid
18
28
self ._changes_cache = None
@@ -22,17 +32,23 @@ def __init__(self, project_id, merge_request_iid):
22
32
@retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
23
33
def get_changes (self , force = False ):
24
34
"""
25
- Get the changes of the merge request
26
- :return: changes
35
+ Retrieve merge request changes via GitLab API.
36
+
37
+ If cached changes are available and force is False, returns the cached data.
38
+ Otherwise, performs a GET request to fetch the latest changes, caches them on success,
39
+ and returns the list of changes. Returns None if the API request fails.
40
+
41
+ Args:
42
+ force (bool): If True, bypasses the cache to fetch fresh changes.
27
43
"""
28
44
if self ._changes_cache and not force :
29
45
return self ._changes_cache
30
46
# URL for the GitLab API endpoint
31
- url = f"{ GITLAB_SERVER_URL } /api/v4/projects/{ self .project_id } /merge_requests/{ self .iid } /changes"
47
+ url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } /merge_requests/{ self .iid } /changes"
32
48
33
49
# Headers for the request
34
50
headers = {
35
- "PRIVATE-TOKEN" : GITLAB_PRIVATE_TOKEN
51
+ "PRIVATE-TOKEN" : gitlab_private_token
36
52
}
37
53
38
54
# Make the GET request
@@ -49,20 +65,31 @@ def get_changes(self, force=False):
49
65
# 获取文件内容
50
66
def get_file_content (self , file_path , branch_name = 'main' , force = False ):
51
67
"""
52
- Get the content of the file
53
- :param file_path: The path of the file
54
- :return: The content of the file
68
+ Fetch the raw content of a repository file via the GitLab API.
69
+
70
+ This method retrieves the file content from the specified branch by making a GET
71
+ request to the GitLab API. The provided file path is URL-encoded for proper API
72
+ endpoint formatting. Cached content is returned if available, unless the force
73
+ flag is set to True.
74
+
75
+ Args:
76
+ file_path: The repository path of the file; forward slashes are URL-encoded.
77
+ branch_name: The branch to fetch the file from (default is 'main').
78
+ force: If True, bypasses the cache to retrieve fresh content.
79
+
80
+ Returns:
81
+ The raw file content as a string if the request is successful; otherwise, None.
55
82
"""
56
83
# 对file_path中的'/'转换为'%2F'
57
84
file_path = file_path .replace ('/' , '%2F' )
58
85
if file_path in self ._file_content_cache and not force :
59
86
return self ._file_content_cache [file_path ]
60
87
# 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 } "
88
+ url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } /repository/files/{ file_path } /raw?ref={ branch_name } "
62
89
63
90
# Headers for the request
64
91
headers = {
65
- "PRIVATE-TOKEN" : GITLAB_PRIVATE_TOKEN
92
+ "PRIVATE-TOKEN" : gitlab_private_token
66
93
}
67
94
68
95
# Make the GET request
@@ -78,17 +105,26 @@ def get_file_content(self, file_path, branch_name='main', force=False):
78
105
@retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
79
106
def get_info (self , force = False ):
80
107
"""
81
- Get the merge request information
82
- :return: Merge request information
108
+ Retrieve merge request information.
109
+
110
+ If cached data is available and force is False, the cached merge request details
111
+ are returned. Otherwise, the method calls the GitLab API to fetch fresh information,
112
+ caches the result, and returns it. If the API request fails, None is returned.
113
+
114
+ Args:
115
+ force (bool): If True, bypass the cache and retrieve fresh data.
116
+
117
+ Returns:
118
+ dict or None: The merge request information if successful; otherwise, None.
83
119
"""
84
120
if self ._info_cache and not force :
85
121
return self ._info_cache
86
122
# URL for the GitLab API endpoint
87
- url = f"{ GITLAB_SERVER_URL } /api/v4/projects/{ self .project_id } /merge_requests/{ self .iid } "
123
+ url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } /merge_requests/{ self .iid } "
88
124
89
125
# Headers for the request
90
126
headers = {
91
- "PRIVATE-TOKEN" : GITLAB_PRIVATE_TOKEN
127
+ "PRIVATE-TOKEN" : gitlab_private_token
92
128
}
93
129
94
130
# Make the GET request
@@ -104,22 +140,37 @@ def get_info(self, force=False):
104
140
# gitlab仓库clone和管理
105
141
class GitlabRepoManager :
106
142
def __init__ (self , project_id , branch_name = "" ):
143
+ """
144
+ Initialize a GitlabRepoManager instance.
145
+
146
+ Creates a unique repository path by combining the provided project ID with the current
147
+ timestamp. The repository is initially marked as not cloned. The optional branch name
148
+ parameter is accepted for potential branch-related operations, although it is not used
149
+ during initialization.
150
+
151
+ Args:
152
+ project_id: Identifier for the GitLab project.
153
+ branch_name: Optional branch name for repository operations.
154
+ """
107
155
self .project_id = project_id
108
156
self .timestamp = int (time .time () * 1000 )
109
157
self .repo_path = f"./repo/{ self .project_id } _{ self .timestamp } "
110
158
self .has_cloned = False
111
159
112
160
def get_info (self ):
113
161
"""
114
- Get the project information
115
- :return: Project information
162
+ Retrieve project information from GitLab.
163
+
164
+ Makes a GET request to the GitLab API to fetch details for the project identified by the
165
+ instance's project_id. Returns the JSON-decoded response if the request is successful (HTTP 200);
166
+ otherwise, returns None.
116
167
"""
117
168
# URL for the GitLab API endpoint
118
- url = f"{ GITLAB_SERVER_URL } /api/v4/projects/{ self .project_id } "
169
+ url = f"{ gitlab_server_url } /api/v4/projects/{ self .project_id } "
119
170
120
171
# Headers for the request
121
172
headers = {
122
- "PRIVATE-TOKEN" : GITLAB_PRIVATE_TOKEN
173
+ "PRIVATE-TOKEN" : gitlab_private_token
123
174
}
124
175
125
176
# Make the GET request
@@ -129,14 +180,20 @@ def get_info(self):
129
180
if response .status_code == 200 :
130
181
return response .json ()
131
182
else :
132
- log .error (f"获取项目信息失败: { response .status_code } { response .text } " )
133
183
return None
134
184
135
185
@retry (stop_max_attempt_number = 3 , wait_fixed = 2000 )
136
186
def shallow_clone (self , branch_name = "main" ):
137
187
"""
138
- Perform a shallow clone of the repository
139
- param branch_name: The name of the branch to clone
188
+ Shallow clones the repository to a local directory.
189
+
190
+ Deletes any existing local clone, constructs an authenticated Git URL using
191
+ repository information, and executes a shallow clone (depth of 1) for the specified
192
+ branch. If cloning fails, an error is logged; otherwise, the repository is marked
193
+ as cloned.
194
+
195
+ Args:
196
+ branch_name (str): The branch to clone (default "main").
140
197
"""
141
198
# If the target directory exists, remove it
142
199
self .delete_repo ()
@@ -159,6 +216,17 @@ def shallow_clone(self, branch_name = "main"):
159
216
# 切换分支
160
217
def checkout_branch (self , branch_name , force = False ):
161
218
# Build the Git command
219
+ """
220
+ Checks out the specified branch by performing a shallow clone if necessary.
221
+
222
+ If the repository has not been cloned already, the method executes a shallow clone for the target branch.
223
+ If the repository is already cloned, it verifies whether the branch is already checked out (unless forced)
224
+ and performs a shallow clone if the branch differs or if force is True.
225
+
226
+ Args:
227
+ branch_name: The name of the branch to check out.
228
+ force: If True, forces re-cloning of the branch even if it appears to be already checked out.
229
+ """
162
230
if not self .has_cloned :
163
231
self .shallow_clone (branch_name )
164
232
else :
@@ -170,11 +238,30 @@ def checkout_branch(self, branch_name, force=False):
170
238
171
239
# 删除库
172
240
def delete_repo (self ):
241
+ """
242
+ Deletes the cloned repository directory if it exists.
243
+
244
+ This method checks whether the repository path exists on the filesystem and removes it along with its contents. If the directory is not present, no action is taken.
245
+ """
173
246
if os .path .exists (self .repo_path ):
174
247
shutil .rmtree (self .repo_path )
175
248
176
249
# 查找相关文件列表
177
250
def find_files_by_keyword (self , keyword , branch_name = "main" ):
251
+ """
252
+ Search for files whose content matches a regex pattern.
253
+
254
+ Checks out the specified branch and recursively searches for files whose content
255
+ matches the provided regular expression. Files that cannot be read due to encoding,
256
+ permission, or existence issues are skipped.
257
+
258
+ Args:
259
+ keyword: Regular expression pattern to search for in file contents.
260
+ branch_name: Branch to search in; defaults to "main".
261
+
262
+ Returns:
263
+ A list of file paths for files containing a match to the keyword.
264
+ """
178
265
matching_files = []
179
266
regex = re .compile (keyword )
180
267
self .checkout_branch (branch_name )
@@ -196,23 +283,19 @@ def find_files_by_keyword(self, keyword, branch_name="main"):
196
283
# 构建带有身份验证信息的 URL
197
284
def _build_authenticated_url (self , repo_url ):
198
285
# 如果 URL 使用 https
199
- token = GITLAB_PRIVATE_TOKEN
286
+ """
287
+ Builds an authenticated URL for repository access.
288
+
289
+ This method embeds an OAuth2 token into the provided repository URL, supporting only
290
+ URLs beginning with "http://" or "https://". For HTTPS URLs, it returns a URL in the
291
+ format "https://oauth2:{token}@<rest_of_url>" and similarly for HTTP URLs. If the URL
292
+ scheme is unsupported, a ValueError is raised.
293
+ """
294
+ token = gitlab_private_token
200
295
if repo_url .startswith ("https://" ):
201
296
return f"https://oauth2:{ token } @{ repo_url [8 :]} "
202
297
# 如果 URL 使用 http
203
298
elif repo_url .startswith ("http://" ):
204
299
return f"http://oauth2:{ token } @{ repo_url [7 :]} "
205
300
else :
206
- raise ValueError ("Unsupported URL scheme" )
207
-
208
- def is_merge_request_opened (gitlab_payload ) -> bool :
209
- """
210
- 判断是否是merge request打开事件
211
- """
212
- try :
213
- gitlab_merge_request_old = gitlab_payload .get ("object_attributes" ).get ("state" ) == "opened" and gitlab_payload .get ("object_attributes" ).get ("merge_status" ) == "preparing"
214
- gitlab_merge_request_new = gitlab_payload .get ("object_attributes" ).get ("state" ) == "merged" and gitlab_payload .get ("object_attributes" ).get ("merge_status" ) == "can_be_merged"
215
- return gitlab_merge_request_old or gitlab_merge_request_new
216
- except Exception as e :
217
- log .error (f"判断是否是merge request打开事件失败: { e } " )
218
- return False
301
+ raise ValueError ("Unsupported URL scheme" )
0 commit comments