gitlab-api 25.8.14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012-2023 Audel Rouhi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ include README.md include requirements.txt recursive-include gitlab_api *.py
@@ -0,0 +1,468 @@
1
+ Metadata-Version: 2.4
2
+ Name: gitlab-api
3
+ Version: 25.8.14
4
+ Summary: GitLab API Python Wrapper
5
+ Author-email: Audel Rouhi <knucklessg1@gmail.com>
6
+ License: MIT
7
+ Classifier: Development Status :: 5 - Production/Stable
8
+ Classifier: License :: Public Domain
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: requests>=2.8.1
16
+ Requires-Dist: urllib3>=2.2.2
17
+ Requires-Dist: pydantic[email]>=2.8.2
18
+ Requires-Dist: SQLAlchemy>=2.0.36
19
+ Requires-Dist: alembic>=1.14.1
20
+ Requires-Dist: tenacity>=9.0.0
21
+ Requires-Dist: fastmcp>=2.11.3
22
+ Dynamic: license-file
23
+
24
+ # GitLab API
25
+
26
+ ![PyPI - Version](https://img.shields.io/pypi/v/gitlab-api)
27
+ ![PyPI - Downloads](https://img.shields.io/pypi/dd/gitlab-api)
28
+ ![GitHub Repo stars](https://img.shields.io/github/stars/Knuckles-Team/gitlab-api)
29
+ ![GitHub forks](https://img.shields.io/github/forks/Knuckles-Team/gitlab-api)
30
+ ![GitHub contributors](https://img.shields.io/github/contributors/Knuckles-Team/gitlab-api)
31
+ ![PyPI - License](https://img.shields.io/pypi/l/gitlab-api)
32
+ ![GitHub](https://img.shields.io/github/license/Knuckles-Team/gitlab-api)
33
+
34
+ ![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/Knuckles-Team/gitlab-api)
35
+ ![GitHub pull requests](https://img.shields.io/github/issues-pr/Knuckles-Team/gitlab-api)
36
+ ![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed/Knuckles-Team/gitlab-api)
37
+ ![GitHub issues](https://img.shields.io/github/issues/Knuckles-Team/gitlab-api)
38
+
39
+ ![GitHub top language](https://img.shields.io/github/languages/top/Knuckles-Team/gitlab-api)
40
+ ![GitHub language count](https://img.shields.io/github/languages/count/Knuckles-Team/gitlab-api)
41
+ ![GitHub repo size](https://img.shields.io/github/repo-size/Knuckles-Team/gitlab-api)
42
+ ![GitHub repo file count (file type)](https://img.shields.io/github/directory-file-count/Knuckles-Team/gitlab-api)
43
+ ![PyPI - Wheel](https://img.shields.io/pypi/wheel/gitlab-api)
44
+ ![PyPI - Implementation](https://img.shields.io/pypi/implementation/gitlab-api)
45
+
46
+ *Version: 25.8.14*
47
+
48
+ Pythonic GitLab API Library
49
+
50
+ GitLab MCP Server for Agentic AI! Get started with Pip or Docker
51
+
52
+ Includes a large portion of useful API calls to GitLab and SQLAlchemy Models to handle loading API calls directly to a database!
53
+
54
+ This repository is actively maintained - Contributions are welcome!
55
+
56
+ Additional Features:
57
+ - All responses are returned as native Pydantic models
58
+ - Save Pydantic models to pickle files locally
59
+ - Easily convert Pydantic to SQLAlchemy models for quick database insertion
60
+
61
+ ### API Calls:
62
+ - Branches
63
+ - Commits
64
+ - Deploy Tokens
65
+ - Groups
66
+ - Jobs
67
+ - Members
68
+ - Merge Request
69
+ - Merge Request Rules
70
+ - Merge Request Rule Settings
71
+ - Namespaces
72
+ - Packages
73
+ - Pipeline
74
+ - Pipeline Schedules
75
+ - Projects
76
+ - Protected Branches
77
+ - Releases
78
+ - Runners
79
+ - Users
80
+ - Wiki
81
+ - Environments
82
+ - Protected Environments
83
+ - Tags
84
+ - Protected Tags
85
+ - Custom Endpoint
86
+
87
+ If your API call isn't supported, you can always run the standard custom API endpoint function to get/post/put/delete and endpoint
88
+
89
+ #### Using an an MCP Server:
90
+
91
+ AI Prompt:
92
+ ```text
93
+ Get me the details of my gitlab project with id 420.
94
+ ```
95
+
96
+ AI Response:
97
+ ```text
98
+ Here are the details for your GitLab project:
99
+
100
+ Project Name: Docker Registry
101
+
102
+ Project ID: 420
103
+
104
+ Namespace: Homelab
105
+
106
+ Visibility: Internal
107
+
108
+ Created: November 13, 1999
109
+
110
+ Default Branch: main
111
+
112
+ Last Activity: August 20, 2025
113
+
114
+ Web URL:
115
+ http://gitlab.arpa/homelab/docker-registry
116
+
117
+ This project is configured with container registry capabilities
118
+ and supports CI/CD pipelines, merge requests, and issue tracking.
119
+ It is currently active with no open issues and follows
120
+ the default branch strategy for its workflow.
121
+
122
+ ```
123
+
124
+ AI Prompt:
125
+ ```text
126
+ Run the gitlab pipeline for this project id 133 on the main branch
127
+ ```
128
+
129
+ AI Response:
130
+ ```text
131
+ The pipeline has been successfully created for the project ID 133 on the main reference. Here are the details:
132
+
133
+ Pipeline ID: 20601
134
+ Reference: main
135
+ SHA: asidfaskdjvalksdjv
136
+ Status: Created
137
+ Web URL:
138
+
139
+ Created At: 2025-08-27T02:49:20.068Z
140
+ The pipeline is now queued and will start executing soon. Let me know if you'd like further details or actions!
141
+ ```
142
+
143
+ AI Prompt:
144
+ ```text
145
+ Create a branch called "test" from the main branch in this gitlab project with id 133
146
+ ```
147
+
148
+ AI Response:
149
+ ```text
150
+ The branch test has been successfully created from the main branch in the project with ID 133.
151
+
152
+ Branch Details:
153
+ Name: test
154
+ URL:
155
+
156
+ Commit:
157
+ Short ID: 1c3775ac
158
+ Message: "Add adgurad."
159
+ Author: KnucklesSG1
160
+ Committed Date: August 25, 2025, at 05:03:01 (UTC-05:00)
161
+ This branch is not protected, and it is not set as the default branch.
162
+ The branch is currently unmerged and can be pushed to by authorized users.
163
+
164
+ Let me know if you'd like further actions (e.g., merging, pushing changes)!
165
+ ```
166
+
167
+ AI Prompt:
168
+ ```text
169
+ Create a merge request for project 133 from the test branch to the main branch with a title of
170
+ "This is a merge request by Qwen3" and a description of "This is a description populated by qwen3 LLM"
171
+ ```
172
+
173
+ AI Response:
174
+ ```text
175
+ The merge request has been successfully created with the following details:
176
+
177
+ Title: This is a merge request by Qwen3
178
+ Description: This is a description populated by qwen3 LLM
179
+ Source Branch: test
180
+ Target Branch: main
181
+ Project: homelab/containers/services/project
182
+ Merge Request URL:
183
+
184
+ Status: Opened
185
+ Created At: 2025-08-27T03:15:51.520000Z
186
+ The merge request is currently in the "preparing" state and is ready for review. Let me know if you'd like further actions, such as assigning reviewers or approving the merge! 😊
187
+ ```
188
+
189
+ <details>
190
+ <summary><b>Usage:</b></summary>
191
+
192
+ ### Use API directly
193
+
194
+ ```python
195
+ #!/usr/bin/python
196
+
197
+ import gitlab_api
198
+ from gitlab_api import pydantic_to_sqlalchemy, upsert, save_model, load_model
199
+ from gitlab_api.gitlab_db_models import (
200
+ BaseDBModel as Base,
201
+ )
202
+ import urllib3
203
+ import os
204
+ from urllib.parse import quote_plus
205
+
206
+ from sqlalchemy import create_engine
207
+ from sqlalchemy.orm import sessionmaker
208
+
209
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
210
+
211
+ gitlab_token = os.environ["GITLAB_TOKEN"]
212
+ postgres_username = os.environ["POSTGRES_USERNAME"]
213
+ postgres_password = os.environ["POSTGRES_PASSWORD"]
214
+ postgres_db_host = os.environ["POSTGRES_DB_HOST"]
215
+ postgres_port = os.environ["POSTGRES_PORT"]
216
+ postgres_db_name = os.environ["POSTGRES_DB_NAME"]
217
+
218
+
219
+ if __name__ == "__main__":
220
+ print("Creating GitLab Client...")
221
+ client = gitlab_api.Api(
222
+ url="http://gitlab.arpa/api/v4/",
223
+ token=gitlab_token,
224
+ verify=False,
225
+ )
226
+ print("GitLab Client Created\n\n")
227
+
228
+ print("\nFetching User Data...")
229
+ user_response = client.get_users(active=True, humans=True)
230
+ print(
231
+ f"Users ({len(user_response.data)}) Fetched - "
232
+ f"Status: {user_response.status_code}\n"
233
+ )
234
+
235
+ print("\nFetching Namespace Data...")
236
+ namespace_response = client.get_namespaces()
237
+ print(
238
+ f"Namespaces ({len(namespace_response.data)}) Fetched - "
239
+ f"Status: {namespace_response.status_code}\n"
240
+ )
241
+
242
+ print("\nFetching Project Data...")
243
+ project_response = client.get_nested_projects_by_group(group_id=2, per_page=100)
244
+ print(
245
+ f"Projects ({len(project_response.data)}) Fetched - "
246
+ f"Status: {project_response.status_code}\n"
247
+ )
248
+
249
+ print("\nFetching Merge Request Data...")
250
+ merge_request_response = client.get_group_merge_requests(
251
+ argument="state=all", group_id=2
252
+ )
253
+
254
+ print(
255
+ f"\nMerge Requests ({len(merge_request_response.data)}) Fetched - "
256
+ f"Status: {merge_request_response.status_code}\n"
257
+ )
258
+
259
+ # Pipeline Jobs table
260
+ pipeline_job_response = None
261
+ for project in project_response.data:
262
+ job_response = client.get_project_jobs(project_id=project.id)
263
+ if (
264
+ not pipeline_job_response
265
+ and hasattr(job_response, "data")
266
+ and len(job_response.data) > 0
267
+ ):
268
+ pipeline_job_response = job_response
269
+ elif (
270
+ pipeline_job_response
271
+ and hasattr(job_response, "data")
272
+ and len(job_response.data) > 0
273
+ ):
274
+ pipeline_job_response.data.extend(job_response.data)
275
+ print(
276
+ f"Pipeline Jobs ({len(getattr(pipeline_job_response, 'data', []))}) "
277
+ f"Fetched for Project ({project.id}) - "
278
+ f"Status: {pipeline_job_response.status_code}\n"
279
+ )
280
+
281
+ print("Saving Pydantic Models...")
282
+ user_file = save_model(model=user_response, file_name="user_model", file_path=".")
283
+ namespace_file = save_model(
284
+ model=namespace_response, file_name="namespace_model", file_path="."
285
+ )
286
+ project_file = save_model(
287
+ model=project_response, file_name="project_model", file_path="."
288
+ )
289
+ merge_request_file = save_model(
290
+ model=merge_request_response, file_name="merge_request_model", file_path="."
291
+ )
292
+ pipeline_job_file = save_model(
293
+ model=pipeline_job_response, file_name="pipeline_job_model", file_path="."
294
+ )
295
+ print("Models Saved")
296
+
297
+ print("Loading Pydantic Models...")
298
+ user_response = load_model(file=user_file)
299
+ namespace_response = load_model(file=namespace_file)
300
+ project_response = load_model(file=project_file)
301
+ merge_request_response = load_model(file=merge_request_file)
302
+ pipeline_job_response = load_model(file=pipeline_job_file)
303
+ print("Models Loaded")
304
+
305
+ print("Converting Pydantic to SQLAlchemy model...")
306
+ user_db_model = pydantic_to_sqlalchemy(schema=user_response)
307
+ print(f"Database Models: {user_db_model}\n")
308
+
309
+ print("Converting Pydantic to SQLAlchemy model...")
310
+ namespace_db_model = pydantic_to_sqlalchemy(schema=namespace_response)
311
+ print(f"Database Models: {namespace_db_model}\n")
312
+
313
+ print("Converting Pydantic to SQLAlchemy model...")
314
+ project_db_model = pydantic_to_sqlalchemy(schema=project_response)
315
+ print(f"Database Models: {project_db_model}\n")
316
+
317
+ print("Converting Pydantic to SQLAlchemy model...")
318
+ merge_request_db_model = pydantic_to_sqlalchemy(schema=merge_request_response)
319
+ print(f"Database Models: {merge_request_db_model}\n")
320
+
321
+ print("Converting Pydantic to SQLAlchemy model...")
322
+ pipeline_db_model = pydantic_to_sqlalchemy(schema=pipeline_job_response)
323
+ print(f"Database Models: {pipeline_db_model}\n")
324
+
325
+ print("Creating Engine")
326
+ engine = create_engine(
327
+ f"postgresql://{postgres_username}:{quote_plus(postgres_password)}@"
328
+ f"{postgres_db_host}:{postgres_port}/{postgres_db_name}"
329
+ )
330
+ print("Engine Created\n\n")
331
+
332
+ print("Creating Tables...")
333
+ Base.metadata.create_all(engine)
334
+ print("Tables Created\n\n")
335
+
336
+ print("Creating Session...")
337
+ Session = sessionmaker(bind=engine)
338
+ session = Session()
339
+ print("Session Created\n\n")
340
+
341
+ print(f"Inserting ({len(user_response.data)}) Users Into Database...")
342
+ upsert(session=session, model=user_db_model)
343
+ print("Users Synchronization Complete!\n")
344
+
345
+ print(f"Inserting ({len(namespace_response.data)}) Namespaces Into Database...")
346
+ upsert(session=session, model=namespace_db_model)
347
+ print("Namespaces Synchronization Complete!\n")
348
+
349
+ print(f"Inserting ({len(project_response.data)}) Projects Into Database...\n")
350
+ upsert(session=session, model=project_db_model)
351
+ print("Projects Synchronization Complete!\n")
352
+
353
+ print(
354
+ f"Inserting ({len(merge_request_response.data)}) Merge Requests Into Database..."
355
+ )
356
+ upsert(session=session, model=merge_request_db_model)
357
+ print("Merge Request Synchronization Complete!\n")
358
+
359
+ print(
360
+ f"Inserting ({len(pipeline_job_response.data)}) Pipeline Jobs Into Database..."
361
+ )
362
+ upsert(session=session, model=pipeline_db_model)
363
+ print("Pipeline Jobs Synchronization Complete!\n")
364
+
365
+ session.close()
366
+ print("Session Closed")
367
+ ```
368
+
369
+ ### Use with AI
370
+
371
+ Deploy MCP Server as a Service
372
+ ```bash
373
+ docker pull knucklessg1/gitlab:latest
374
+ ```
375
+
376
+ Modify the `compose.yml`
377
+
378
+ ```compose
379
+ services:
380
+ gitlab-mcp:
381
+ image: knucklessg1/gitlab:latest
382
+ environment:
383
+ - HOST=0.0.0.0
384
+ - PORT=8003
385
+ ports:
386
+ - 8003:8003
387
+ ```
388
+
389
+ Configure `mcp.json`
390
+
391
+ Recommended: Store secrets in environment variables with lookup in JSON file.
392
+
393
+ For Testing Only: Plain text storage will also work, although **not** recommended.
394
+
395
+ ```json
396
+ {
397
+ "mcpServers": {
398
+ "gitlab": {
399
+ "command": "gitlab-mcp"
400
+ },
401
+ "env": {
402
+ "GITLAB_INSTANCE": "http://gitlab.com/api/v4/",
403
+ "ACCESS_TOKEN": "${env:ACCESS_TOKEN}"
404
+ // "ACCESS_TOKEN": "glpat-abc123youandme"
405
+ },
406
+ "timeout": 300000
407
+ }
408
+ }
409
+ ```
410
+
411
+ ```
412
+
413
+ </details>
414
+
415
+ <details>
416
+ <summary><b>Installation Instructions:</b></summary>
417
+
418
+ Install Python Package
419
+
420
+ ```bash
421
+ python -m pip install gitlab-api
422
+ ```
423
+
424
+ </details>
425
+
426
+ <details>
427
+ <summary><b>Tests:</b></summary>
428
+
429
+ pre-commit check
430
+ ```bash
431
+ pre-commit run --all-files
432
+ ```
433
+
434
+ pytest
435
+ ```bash
436
+ python -m pip install -r test-requirements.txt
437
+ pytest ./test/test_gitlab_models.py
438
+ ```
439
+
440
+ Full pytests
441
+
442
+ ```bash
443
+ rm -rf ./dist/* \
444
+ && python setup.py bdist_wheel --universal \
445
+ && python -m pip uninstall gitlab-api -y \
446
+ && python -m pip install ./dist/*.whl \
447
+ && pytest -vv ./test/test_gitlab_models.py \
448
+ && pytest -vv ./test/test_gitlab_db_models.py \
449
+ && python ./test/test_sqlalchemy.py
450
+ ```
451
+
452
+ Validate MCP Server
453
+
454
+ ```bash
455
+ npx @modelcontextprotocol/inspector gitlab-mcp
456
+ ```
457
+
458
+ </details>
459
+
460
+ <details>
461
+ <summary><b>Repository Owners:</b></summary>
462
+
463
+ <img width="100%" height="180em" src="https://github-readme-stats.vercel.app/api?username=Knucklessg1&show_icons=true&hide_border=true&&count_private=true&include_all_commits=true" />
464
+
465
+ ![GitHub followers](https://img.shields.io/github/followers/Knucklessg1)
466
+ ![GitHub User's stars](https://img.shields.io/github/stars/Knucklessg1)
467
+
468
+ </details>