@zereight/mcp-gitlab 1.0.64 → 1.0.66
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.
- package/README.md +93 -56
- package/build/index.js +248 -59
- package/build/schemas.js +71 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,9 +35,39 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
#### vscode .vscode/mcp.json
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"inputs": [
|
|
43
|
+
{
|
|
44
|
+
"type": "promptString",
|
|
45
|
+
"id": "gitlab-token",
|
|
46
|
+
"description": "Gitlab Token to read API",
|
|
47
|
+
"password": true
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"servers": {
|
|
51
|
+
"GitLab-MCP": {
|
|
52
|
+
"type": "stdio",
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["-y", "@zereight/mcp-gitlab"],
|
|
55
|
+
"env": {
|
|
56
|
+
"GITLAB_PERSONAL_ACCESS_TOKEN": "${input:gitlab-token}",
|
|
57
|
+
"GITLAB_API_URL": "your-fancy-gitlab-url",
|
|
58
|
+
"GITLAB_READ_ONLY_MODE": "true",
|
|
59
|
+
...
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
38
66
|
#### Docker
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
|
|
68
|
+
- stdio mcp.json
|
|
69
|
+
|
|
70
|
+
```json
|
|
41
71
|
{
|
|
42
72
|
"mcpServers": {
|
|
43
73
|
"GitLab communication server": {
|
|
@@ -74,6 +104,7 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
74
104
|
```
|
|
75
105
|
|
|
76
106
|
- sse
|
|
107
|
+
|
|
77
108
|
```shell
|
|
78
109
|
docker run -i --rm \
|
|
79
110
|
-e GITLAB_PERSONAL_ACCESS_TOKEN=your_gitlab_token \
|
|
@@ -91,6 +122,7 @@ docker run -i --rm \
|
|
|
91
122
|
{
|
|
92
123
|
"mcpServers": {
|
|
93
124
|
"GitLab communication server": {
|
|
125
|
+
"type": "sse",
|
|
94
126
|
"url": "http://localhost:3333/sse"
|
|
95
127
|
}
|
|
96
128
|
}
|
|
@@ -107,15 +139,19 @@ $ sh scripts/image_push.sh docker_user_name
|
|
|
107
139
|
|
|
108
140
|
- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
|
|
109
141
|
- `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
|
|
142
|
+
- `GITLAB_PROJECT_ID`: Default project ID. If set, Overwrite this value when making an API request.
|
|
110
143
|
- `GITLAB_READ_ONLY_MODE`: When set to 'true', restricts the server to only expose read-only operations. Useful for enhanced security or when write access is not needed. Also useful for using with Cursor and it's 40 tool limit.
|
|
111
144
|
- `USE_GITLAB_WIKI`: When set to 'true', enables the wiki-related tools (list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, delete_wiki_page). By default, wiki features are disabled.
|
|
112
145
|
- `USE_MILESTONE`: When set to 'true', enables the milestone-related tools (list_milestones, get_milestone, create_milestone, edit_milestone, delete_milestone, get_milestone_issue, get_milestone_merge_requests, promote_milestone, get_milestone_burndown_events). By default, milestone features are disabled.
|
|
113
146
|
- `USE_PIPELINE`: When set to 'true', enables the pipeline-related tools (list_pipelines, get_pipeline, list_pipeline_jobs, get_pipeline_job, get_pipeline_job_output, create_pipeline, retry_pipeline, cancel_pipeline). By default, pipeline features are disabled.
|
|
114
147
|
- `GITLAB_AUTH_COOKIE_PATH`: Path to an authentication cookie file for GitLab instances that require cookie-based authentication. When provided, the cookie will be included in all GitLab API requests.
|
|
115
148
|
|
|
149
|
+
[](https://www.star-history.com/#zereight/gitlab-mcp&Date)
|
|
150
|
+
|
|
116
151
|
## Tools 🛠️
|
|
117
152
|
|
|
118
153
|
+<!-- TOOLS-START -->
|
|
154
|
+
|
|
119
155
|
1. `create_or_update_file` - Create or update a single file in a GitLab project
|
|
120
156
|
2. `search_repositories` - Search for GitLab projects
|
|
121
157
|
3. `create_repository` - Create a new GitLab project
|
|
@@ -127,58 +163,59 @@ $ sh scripts/image_push.sh docker_user_name
|
|
|
127
163
|
9. `create_branch` - Create a new branch in a GitLab project
|
|
128
164
|
10. `get_merge_request` - Get details of a merge request (Either mergeRequestIid or branchName must be provided)
|
|
129
165
|
11. `get_merge_request_diffs` - Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)
|
|
130
|
-
12. `
|
|
131
|
-
13. `
|
|
132
|
-
14. `
|
|
133
|
-
15. `
|
|
134
|
-
16. `
|
|
135
|
-
17. `
|
|
136
|
-
18. `
|
|
137
|
-
19. `
|
|
138
|
-
20. `
|
|
139
|
-
21. `
|
|
140
|
-
22. `
|
|
141
|
-
23. `
|
|
142
|
-
24. `
|
|
143
|
-
25. `
|
|
144
|
-
26. `
|
|
145
|
-
27. `
|
|
146
|
-
28. `
|
|
147
|
-
29. `
|
|
148
|
-
30. `
|
|
149
|
-
31. `
|
|
150
|
-
32. `
|
|
151
|
-
33. `
|
|
152
|
-
34. `
|
|
153
|
-
35. `
|
|
154
|
-
36. `
|
|
155
|
-
37. `
|
|
156
|
-
38. `
|
|
157
|
-
39. `
|
|
158
|
-
40. `
|
|
159
|
-
41. `
|
|
160
|
-
42. `
|
|
161
|
-
43. `
|
|
162
|
-
44. `
|
|
163
|
-
45. `
|
|
164
|
-
46. `
|
|
165
|
-
47. `
|
|
166
|
-
48. `
|
|
167
|
-
49. `
|
|
168
|
-
50. `
|
|
169
|
-
51. `
|
|
170
|
-
52. `
|
|
171
|
-
53. `
|
|
172
|
-
54. `
|
|
173
|
-
55. `
|
|
174
|
-
56. `
|
|
175
|
-
57. `
|
|
176
|
-
58. `
|
|
177
|
-
59. `
|
|
178
|
-
60. `
|
|
179
|
-
61. `
|
|
180
|
-
62. `
|
|
181
|
-
63. `
|
|
182
|
-
64. `
|
|
183
|
-
65. `
|
|
166
|
+
12. `list_merge_request_diffs` - List merge request diffs with pagination support (Either mergeRequestIid or branchName must be provided)
|
|
167
|
+
13. `get_branch_diffs` - Get the changes/diffs between two branches or commits in a GitLab project
|
|
168
|
+
14. `update_merge_request` - Update a merge request (Either mergeRequestIid or branchName must be provided)
|
|
169
|
+
15. `create_note` - Create a new note (comment) to an issue or merge request
|
|
170
|
+
16. `create_merge_request_thread` - Create a new thread on a merge request
|
|
171
|
+
17. `mr_discussions` - List discussion items for a merge request
|
|
172
|
+
18. `update_merge_request_note` - Modify an existing merge request thread note
|
|
173
|
+
19. `create_merge_request_note` - Add a new note to an existing merge request thread
|
|
174
|
+
20. `update_issue_note` - Modify an existing issue thread note
|
|
175
|
+
21. `create_issue_note` - Add a new note to an existing issue thread
|
|
176
|
+
22. `list_issues` - List issues in a GitLab project with filtering options
|
|
177
|
+
23. `get_issue` - Get details of a specific issue in a GitLab project
|
|
178
|
+
24. `update_issue` - Update an issue in a GitLab project
|
|
179
|
+
25. `delete_issue` - Delete an issue from a GitLab project
|
|
180
|
+
26. `list_issue_links` - List all issue links for a specific issue
|
|
181
|
+
27. `list_issue_discussions` - List discussions for an issue in a GitLab project
|
|
182
|
+
28. `get_issue_link` - Get a specific issue link
|
|
183
|
+
29. `create_issue_link` - Create an issue link between two issues
|
|
184
|
+
30. `delete_issue_link` - Delete an issue link
|
|
185
|
+
31. `list_namespaces` - List all namespaces available to the current user
|
|
186
|
+
32. `get_namespace` - Get details of a namespace by ID or path
|
|
187
|
+
33. `verify_namespace` - Verify if a namespace path exists
|
|
188
|
+
34. `get_project` - Get details of a specific project
|
|
189
|
+
35. `list_projects` - List projects accessible by the current user
|
|
190
|
+
36. `list_labels` - List labels for a project
|
|
191
|
+
37. `get_label` - Get a single label from a project
|
|
192
|
+
38. `create_label` - Create a new label in a project
|
|
193
|
+
39. `update_label` - Update an existing label in a project
|
|
194
|
+
40. `delete_label` - Delete a label from a project
|
|
195
|
+
41. `list_group_projects` - List projects in a GitLab group with filtering options
|
|
196
|
+
42. `list_wiki_pages` - List wiki pages in a GitLab project
|
|
197
|
+
43. `get_wiki_page` - Get details of a specific wiki page
|
|
198
|
+
44. `create_wiki_page` - Create a new wiki page in a GitLab project
|
|
199
|
+
45. `update_wiki_page` - Update an existing wiki page in a GitLab project
|
|
200
|
+
46. `delete_wiki_page` - Delete a wiki page from a GitLab project
|
|
201
|
+
47. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
|
|
202
|
+
48. `list_pipelines` - List pipelines in a GitLab project with filtering options
|
|
203
|
+
49. `get_pipeline` - Get details of a specific pipeline in a GitLab project
|
|
204
|
+
50. `list_pipeline_jobs` - List all jobs in a specific pipeline
|
|
205
|
+
51. `get_pipeline_job` - Get details of a GitLab pipeline job number
|
|
206
|
+
52. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
|
|
207
|
+
53. `create_pipeline` - Create a new pipeline for a branch or tag
|
|
208
|
+
54. `retry_pipeline` - Retry a failed or canceled pipeline
|
|
209
|
+
55. `cancel_pipeline` - Cancel a running pipeline
|
|
210
|
+
56. `list_merge_requests` - List merge requests in a GitLab project with filtering options
|
|
211
|
+
57. `list_milestones` - List milestones in a GitLab project with filtering options
|
|
212
|
+
58. `get_milestone` - Get details of a specific milestone
|
|
213
|
+
59. `create_milestone` - Create a new milestone in a GitLab project
|
|
214
|
+
60. `edit_milestone` - Edit an existing milestone in a GitLab project
|
|
215
|
+
61. `delete_milestone` - Delete a milestone from a GitLab project
|
|
216
|
+
62. `get_milestone_issue` - Get issues associated with a specific milestone
|
|
217
|
+
63. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
|
|
218
|
+
64. `promote_milestone` - Promote a milestone to the next stage
|
|
219
|
+
65. `get_milestone_burndown_events` - Get burndown events for a specific milestone
|
|
220
|
+
66. `get_users` - Get GitLab user details by usernames
|
|
184
221
|
<!-- TOOLS-END -->
|
package/build/index.js
CHANGED
|
@@ -27,7 +27,7 @@ GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
|
|
|
27
27
|
GitLabDiscussionNoteSchema, // Added
|
|
28
28
|
GitLabDiscussionSchema, PaginatedDiscussionsResponseSchema, UpdateMergeRequestNoteSchema, // Added
|
|
29
29
|
CreateMergeRequestNoteSchema, // Added
|
|
30
|
-
ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, GitLabCompareResultSchema, GetBranchDiffsSchema, } from "./schemas.js";
|
|
30
|
+
ListMergeRequestDiscussionsSchema, UpdateIssueNoteSchema, CreateIssueNoteSchema, ListMergeRequestsSchema, GitLabMilestonesSchema, ListProjectMilestonesSchema, GetProjectMilestoneSchema, CreateProjectMilestoneSchema, EditProjectMilestoneSchema, DeleteProjectMilestoneSchema, GetMilestoneIssuesSchema, GetMilestoneMergeRequestsSchema, PromoteProjectMilestoneSchema, GetMilestoneBurndownEventsSchema, GitLabCompareResultSchema, GetBranchDiffsSchema, ListCommitsSchema, GetCommitSchema, GetCommitDiffSchema, ListMergeRequestDiffsSchema, } from "./schemas.js";
|
|
31
31
|
/**
|
|
32
32
|
* Read version from package.json
|
|
33
33
|
*/
|
|
@@ -242,6 +242,11 @@ const allTools = [
|
|
|
242
242
|
description: "Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)",
|
|
243
243
|
inputSchema: zodToJsonSchema(GetMergeRequestDiffsSchema),
|
|
244
244
|
},
|
|
245
|
+
{
|
|
246
|
+
name: "list_merge_request_diffs",
|
|
247
|
+
description: "List merge request diffs with pagination support (Either mergeRequestIid or branchName must be provided)",
|
|
248
|
+
inputSchema: zodToJsonSchema(ListMergeRequestDiffsSchema),
|
|
249
|
+
},
|
|
245
250
|
{
|
|
246
251
|
name: "get_branch_diffs",
|
|
247
252
|
description: "Get the changes/diffs between two branches or commits in a GitLab project",
|
|
@@ -512,6 +517,21 @@ const allTools = [
|
|
|
512
517
|
description: "Get GitLab user details by usernames",
|
|
513
518
|
inputSchema: zodToJsonSchema(GetUsersSchema),
|
|
514
519
|
},
|
|
520
|
+
{
|
|
521
|
+
name: "list_commits",
|
|
522
|
+
description: "List repository commits with filtering options",
|
|
523
|
+
inputSchema: zodToJsonSchema(ListCommitsSchema),
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: "get_commit",
|
|
527
|
+
description: "Get details of a specific commit",
|
|
528
|
+
inputSchema: zodToJsonSchema(GetCommitSchema),
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
name: "get_commit_diff",
|
|
532
|
+
description: "Get changes/diffs of a specific commit",
|
|
533
|
+
inputSchema: zodToJsonSchema(GetCommitDiffSchema),
|
|
534
|
+
},
|
|
515
535
|
];
|
|
516
536
|
// Define which tools are read-only
|
|
517
537
|
const readOnlyTools = [
|
|
@@ -549,6 +569,9 @@ const readOnlyTools = [
|
|
|
549
569
|
"list_wiki_pages",
|
|
550
570
|
"get_wiki_page",
|
|
551
571
|
"get_users",
|
|
572
|
+
"list_commits",
|
|
573
|
+
"get_commit",
|
|
574
|
+
"get_commit_diff",
|
|
552
575
|
];
|
|
553
576
|
// Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
|
|
554
577
|
const wikiToolNames = [
|
|
@@ -603,6 +626,7 @@ function normalizeGitLabApiUrl(url) {
|
|
|
603
626
|
}
|
|
604
627
|
// Use the normalizeGitLabApiUrl function to handle various URL formats
|
|
605
628
|
const GITLAB_API_URL = normalizeGitLabApiUrl(process.env.GITLAB_API_URL || "");
|
|
629
|
+
const GITLAB_PROJECT_ID = process.env.GITLAB_PROJECT_ID;
|
|
606
630
|
if (!GITLAB_PERSONAL_ACCESS_TOKEN) {
|
|
607
631
|
console.error("GITLAB_PERSONAL_ACCESS_TOKEN environment variable is not set");
|
|
608
632
|
process.exit(1);
|
|
@@ -629,6 +653,13 @@ async function handleGitLabError(response) {
|
|
|
629
653
|
}
|
|
630
654
|
}
|
|
631
655
|
}
|
|
656
|
+
/**
|
|
657
|
+
* @param {string} projectId - The project ID parameter passed to the function
|
|
658
|
+
* @returns {string} The project ID to use for the API call
|
|
659
|
+
*/
|
|
660
|
+
function getEffectiveProjectId(projectId) {
|
|
661
|
+
return GITLAB_PROJECT_ID || projectId;
|
|
662
|
+
}
|
|
632
663
|
/**
|
|
633
664
|
* Create a fork of a GitLab project
|
|
634
665
|
* 프로젝트 포크 생성 (Create a project fork)
|
|
@@ -639,7 +670,8 @@ async function handleGitLabError(response) {
|
|
|
639
670
|
*/
|
|
640
671
|
async function forkProject(projectId, namespace) {
|
|
641
672
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
642
|
-
const
|
|
673
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
674
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/fork`);
|
|
643
675
|
if (namespace) {
|
|
644
676
|
url.searchParams.append("namespace", namespace);
|
|
645
677
|
}
|
|
@@ -665,7 +697,8 @@ async function forkProject(projectId, namespace) {
|
|
|
665
697
|
*/
|
|
666
698
|
async function createBranch(projectId, options) {
|
|
667
699
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
668
|
-
const
|
|
700
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
701
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/repository/branches`);
|
|
669
702
|
const response = await fetch(url.toString(), {
|
|
670
703
|
...DEFAULT_FETCH_CONFIG,
|
|
671
704
|
method: "POST",
|
|
@@ -686,7 +719,8 @@ async function createBranch(projectId, options) {
|
|
|
686
719
|
*/
|
|
687
720
|
async function getDefaultBranchRef(projectId) {
|
|
688
721
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
689
|
-
const
|
|
722
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
723
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}`);
|
|
690
724
|
const response = await fetch(url.toString(), {
|
|
691
725
|
...DEFAULT_FETCH_CONFIG,
|
|
692
726
|
});
|
|
@@ -705,12 +739,13 @@ async function getDefaultBranchRef(projectId) {
|
|
|
705
739
|
*/
|
|
706
740
|
async function getFileContents(projectId, filePath, ref) {
|
|
707
741
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
742
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
708
743
|
const encodedPath = encodeURIComponent(filePath);
|
|
709
744
|
// ref가 없는 경우 default branch를 가져옴
|
|
710
745
|
if (!ref) {
|
|
711
746
|
ref = await getDefaultBranchRef(projectId);
|
|
712
747
|
}
|
|
713
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(
|
|
748
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/repository/files/${encodedPath}`);
|
|
714
749
|
url.searchParams.append("ref", ref);
|
|
715
750
|
const response = await fetch(url.toString(), {
|
|
716
751
|
...DEFAULT_FETCH_CONFIG,
|
|
@@ -739,7 +774,8 @@ async function getFileContents(projectId, filePath, ref) {
|
|
|
739
774
|
*/
|
|
740
775
|
async function createIssue(projectId, options) {
|
|
741
776
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
742
|
-
const
|
|
777
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
778
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
|
|
743
779
|
const response = await fetch(url.toString(), {
|
|
744
780
|
...DEFAULT_FETCH_CONFIG,
|
|
745
781
|
method: "POST",
|
|
@@ -770,7 +806,8 @@ async function createIssue(projectId, options) {
|
|
|
770
806
|
*/
|
|
771
807
|
async function listIssues(projectId, options = {}) {
|
|
772
808
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
773
|
-
const
|
|
809
|
+
const effectiveProjectId = getEffectiveProjectId(projectId);
|
|
810
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(effectiveProjectId)}/issues`);
|
|
774
811
|
// Add all query parameters
|
|
775
812
|
Object.entries(options).forEach(([key, value]) => {
|
|
776
813
|
if (value !== undefined) {
|
|
@@ -807,7 +844,7 @@ async function listIssues(projectId, options = {}) {
|
|
|
807
844
|
*/
|
|
808
845
|
async function listMergeRequests(projectId, options = {}) {
|
|
809
846
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
810
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests`);
|
|
847
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
|
|
811
848
|
// Add all query parameters
|
|
812
849
|
Object.entries(options).forEach(([key, value]) => {
|
|
813
850
|
if (value !== undefined) {
|
|
@@ -837,7 +874,7 @@ async function listMergeRequests(projectId, options = {}) {
|
|
|
837
874
|
*/
|
|
838
875
|
async function getIssue(projectId, issueIid) {
|
|
839
876
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
840
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
|
|
877
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
|
|
841
878
|
const response = await fetch(url.toString(), {
|
|
842
879
|
...DEFAULT_FETCH_CONFIG,
|
|
843
880
|
});
|
|
@@ -856,7 +893,7 @@ async function getIssue(projectId, issueIid) {
|
|
|
856
893
|
*/
|
|
857
894
|
async function updateIssue(projectId, issueIid, options) {
|
|
858
895
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
859
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
|
|
896
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
|
|
860
897
|
// Convert labels array to comma-separated string if present
|
|
861
898
|
const body = { ...options };
|
|
862
899
|
if (body.labels && Array.isArray(body.labels)) {
|
|
@@ -881,7 +918,7 @@ async function updateIssue(projectId, issueIid, options) {
|
|
|
881
918
|
*/
|
|
882
919
|
async function deleteIssue(projectId, issueIid) {
|
|
883
920
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
884
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`);
|
|
921
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`);
|
|
885
922
|
const response = await fetch(url.toString(), {
|
|
886
923
|
...DEFAULT_FETCH_CONFIG,
|
|
887
924
|
method: "DELETE",
|
|
@@ -898,7 +935,7 @@ async function deleteIssue(projectId, issueIid) {
|
|
|
898
935
|
*/
|
|
899
936
|
async function listIssueLinks(projectId, issueIid) {
|
|
900
937
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
901
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
|
|
938
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
|
|
902
939
|
const response = await fetch(url.toString(), {
|
|
903
940
|
...DEFAULT_FETCH_CONFIG,
|
|
904
941
|
});
|
|
@@ -917,7 +954,7 @@ async function listIssueLinks(projectId, issueIid) {
|
|
|
917
954
|
*/
|
|
918
955
|
async function getIssueLink(projectId, issueIid, issueLinkId) {
|
|
919
956
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
920
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links/${issueLinkId}`);
|
|
957
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
|
|
921
958
|
const response = await fetch(url.toString(), {
|
|
922
959
|
...DEFAULT_FETCH_CONFIG,
|
|
923
960
|
});
|
|
@@ -939,7 +976,7 @@ async function getIssueLink(projectId, issueIid, issueLinkId) {
|
|
|
939
976
|
async function createIssueLink(projectId, issueIid, targetProjectId, targetIssueIid, linkType = "relates_to") {
|
|
940
977
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
941
978
|
targetProjectId = decodeURIComponent(targetProjectId); // Decode target project ID as well
|
|
942
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links`);
|
|
979
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links`);
|
|
943
980
|
const response = await fetch(url.toString(), {
|
|
944
981
|
...DEFAULT_FETCH_CONFIG,
|
|
945
982
|
method: "POST",
|
|
@@ -964,7 +1001,7 @@ async function createIssueLink(projectId, issueIid, targetProjectId, targetIssue
|
|
|
964
1001
|
*/
|
|
965
1002
|
async function deleteIssueLink(projectId, issueIid, issueLinkId) {
|
|
966
1003
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
967
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/links/${issueLinkId}`);
|
|
1004
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/links/${issueLinkId}`);
|
|
968
1005
|
const response = await fetch(url.toString(), {
|
|
969
1006
|
...DEFAULT_FETCH_CONFIG,
|
|
970
1007
|
method: "DELETE",
|
|
@@ -981,7 +1018,7 @@ async function deleteIssueLink(projectId, issueIid, issueLinkId) {
|
|
|
981
1018
|
*/
|
|
982
1019
|
async function createMergeRequest(projectId, options) {
|
|
983
1020
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
984
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests`);
|
|
1021
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests`);
|
|
985
1022
|
const response = await fetch(url.toString(), {
|
|
986
1023
|
...DEFAULT_FETCH_CONFIG,
|
|
987
1024
|
method: "POST",
|
|
@@ -1022,7 +1059,7 @@ async function createMergeRequest(projectId, options) {
|
|
|
1022
1059
|
*/
|
|
1023
1060
|
async function listDiscussions(projectId, resourceType, resourceIid, options = {}) {
|
|
1024
1061
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1025
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${resourceType}/${resourceIid}/discussions`);
|
|
1062
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${resourceType}/${resourceIid}/discussions`);
|
|
1026
1063
|
// Add query parameters for pagination and sorting
|
|
1027
1064
|
if (options.page) {
|
|
1028
1065
|
url.searchParams.append("page", options.page.toString());
|
|
@@ -1094,7 +1131,7 @@ async function listIssueDiscussions(projectId, issueIid, options = {}) {
|
|
|
1094
1131
|
*/
|
|
1095
1132
|
async function updateMergeRequestNote(projectId, mergeRequestIid, discussionId, noteId, body, resolved) {
|
|
1096
1133
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1097
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
|
|
1134
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes/${noteId}`);
|
|
1098
1135
|
// Only one of body or resolved can be sent according to GitLab API
|
|
1099
1136
|
const payload = {};
|
|
1100
1137
|
if (body !== undefined) {
|
|
@@ -1123,7 +1160,7 @@ async function updateMergeRequestNote(projectId, mergeRequestIid, discussionId,
|
|
|
1123
1160
|
*/
|
|
1124
1161
|
async function updateIssueNote(projectId, issueIid, discussionId, noteId, body) {
|
|
1125
1162
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1126
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions/${discussionId}/notes/${noteId}`);
|
|
1163
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes/${noteId}`);
|
|
1127
1164
|
const payload = { body };
|
|
1128
1165
|
const response = await fetch(url.toString(), {
|
|
1129
1166
|
...DEFAULT_FETCH_CONFIG,
|
|
@@ -1145,7 +1182,7 @@ async function updateIssueNote(projectId, issueIid, discussionId, noteId, body)
|
|
|
1145
1182
|
*/
|
|
1146
1183
|
async function createIssueNote(projectId, issueIid, discussionId, body, createdAt) {
|
|
1147
1184
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1148
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/discussions/${discussionId}/notes`);
|
|
1185
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}/discussions/${discussionId}/notes`);
|
|
1149
1186
|
const payload = { body };
|
|
1150
1187
|
if (createdAt) {
|
|
1151
1188
|
payload.created_at = createdAt;
|
|
@@ -1172,7 +1209,7 @@ async function createIssueNote(projectId, issueIid, discussionId, body, createdA
|
|
|
1172
1209
|
*/
|
|
1173
1210
|
async function createMergeRequestNote(projectId, mergeRequestIid, discussionId, body, createdAt) {
|
|
1174
1211
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1175
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes`);
|
|
1212
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions/${discussionId}/notes`);
|
|
1176
1213
|
const payload = { body };
|
|
1177
1214
|
if (createdAt) {
|
|
1178
1215
|
payload.created_at = createdAt;
|
|
@@ -1201,7 +1238,7 @@ async function createMergeRequestNote(projectId, mergeRequestIid, discussionId,
|
|
|
1201
1238
|
async function createOrUpdateFile(projectId, filePath, content, commitMessage, branch, previousPath, last_commit_id, commit_id) {
|
|
1202
1239
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1203
1240
|
const encodedPath = encodeURIComponent(filePath);
|
|
1204
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/files/${encodedPath}`);
|
|
1241
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/files/${encodedPath}`);
|
|
1205
1242
|
const body = {
|
|
1206
1243
|
branch,
|
|
1207
1244
|
content,
|
|
@@ -1268,7 +1305,7 @@ async function createOrUpdateFile(projectId, filePath, content, commitMessage, b
|
|
|
1268
1305
|
*/
|
|
1269
1306
|
async function createTree(projectId, files, ref) {
|
|
1270
1307
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1271
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/tree`);
|
|
1308
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/tree`);
|
|
1272
1309
|
if (ref) {
|
|
1273
1310
|
url.searchParams.append("ref", ref);
|
|
1274
1311
|
}
|
|
@@ -1306,7 +1343,7 @@ async function createTree(projectId, files, ref) {
|
|
|
1306
1343
|
*/
|
|
1307
1344
|
async function createCommit(projectId, message, branch, actions) {
|
|
1308
1345
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1309
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/commits`);
|
|
1346
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
|
|
1310
1347
|
const response = await fetch(url.toString(), {
|
|
1311
1348
|
...DEFAULT_FETCH_CONFIG,
|
|
1312
1349
|
method: "POST",
|
|
@@ -1407,10 +1444,10 @@ async function getMergeRequest(projectId, mergeRequestIid, branchName) {
|
|
|
1407
1444
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1408
1445
|
let url;
|
|
1409
1446
|
if (mergeRequestIid) {
|
|
1410
|
-
url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
|
|
1447
|
+
url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
|
|
1411
1448
|
}
|
|
1412
1449
|
else if (branchName) {
|
|
1413
|
-
url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
|
|
1450
|
+
url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests?source_branch=${encodeURIComponent(branchName)}`);
|
|
1414
1451
|
}
|
|
1415
1452
|
else {
|
|
1416
1453
|
throw new Error("Either mergeRequestIid or branchName must be provided");
|
|
@@ -1445,7 +1482,7 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
|
|
|
1445
1482
|
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
1446
1483
|
mergeRequestIid = mergeRequest.iid;
|
|
1447
1484
|
}
|
|
1448
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/changes`);
|
|
1485
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/changes`);
|
|
1449
1486
|
if (view) {
|
|
1450
1487
|
url.searchParams.append("view", view);
|
|
1451
1488
|
}
|
|
@@ -1456,6 +1493,41 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
|
|
|
1456
1493
|
const data = (await response.json());
|
|
1457
1494
|
return z.array(GitLabDiffSchema).parse(data.changes);
|
|
1458
1495
|
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Get merge request changes with detailed information including commits, diff_refs, and more
|
|
1498
|
+
* 마지막으로 추가된 상세한 MR 변경사항 조회 함수 (Detailed merge request changes retrieval function)
|
|
1499
|
+
*
|
|
1500
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
1501
|
+
* @param {number} mergeRequestIid - The internal ID of the merge request (Either mergeRequestIid or branchName must be provided)
|
|
1502
|
+
* @param {string} [branchName] - The name of the branch to search for merge request by branch name (Either mergeRequestIid or branchName must be provided)
|
|
1503
|
+
* @param {boolean} [unidiff] - Return diff in unidiff format
|
|
1504
|
+
* @returns {Promise<any>} The complete merge request changes response
|
|
1505
|
+
*/
|
|
1506
|
+
async function listMergeRequestDiffs(projectId, mergeRequestIid, branchName, page, perPage, unidiff) {
|
|
1507
|
+
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1508
|
+
if (!mergeRequestIid && !branchName) {
|
|
1509
|
+
throw new Error("Either mergeRequestIid or branchName must be provided");
|
|
1510
|
+
}
|
|
1511
|
+
if (branchName && !mergeRequestIid) {
|
|
1512
|
+
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
1513
|
+
mergeRequestIid = mergeRequest.iid;
|
|
1514
|
+
}
|
|
1515
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/diffs`);
|
|
1516
|
+
if (page) {
|
|
1517
|
+
url.searchParams.append("page", page.toString());
|
|
1518
|
+
}
|
|
1519
|
+
if (perPage) {
|
|
1520
|
+
url.searchParams.append("per_page", perPage.toString());
|
|
1521
|
+
}
|
|
1522
|
+
if (unidiff) {
|
|
1523
|
+
url.searchParams.append("unidiff", "true");
|
|
1524
|
+
}
|
|
1525
|
+
const response = await fetch(url.toString(), {
|
|
1526
|
+
...DEFAULT_FETCH_CONFIG,
|
|
1527
|
+
});
|
|
1528
|
+
await handleGitLabError(response);
|
|
1529
|
+
return await response.json(); // Return full response including commits, diff_refs, changes, etc.
|
|
1530
|
+
}
|
|
1459
1531
|
/**
|
|
1460
1532
|
* Get branch comparison diffs
|
|
1461
1533
|
*
|
|
@@ -1467,7 +1539,7 @@ async function getMergeRequestDiffs(projectId, mergeRequestIid, branchName, view
|
|
|
1467
1539
|
*/
|
|
1468
1540
|
async function getBranchDiffs(projectId, from, to, straight) {
|
|
1469
1541
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1470
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/compare`);
|
|
1542
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/compare`);
|
|
1471
1543
|
url.searchParams.append("from", from);
|
|
1472
1544
|
url.searchParams.append("to", to);
|
|
1473
1545
|
if (straight !== undefined) {
|
|
@@ -1502,7 +1574,7 @@ async function updateMergeRequest(projectId, options, mergeRequestIid, branchNam
|
|
|
1502
1574
|
const mergeRequest = await getMergeRequest(projectId, undefined, branchName);
|
|
1503
1575
|
mergeRequestIid = mergeRequest.iid;
|
|
1504
1576
|
}
|
|
1505
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`);
|
|
1577
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}`);
|
|
1506
1578
|
const response = await fetch(url.toString(), {
|
|
1507
1579
|
...DEFAULT_FETCH_CONFIG,
|
|
1508
1580
|
method: "PUT",
|
|
@@ -1526,7 +1598,7 @@ async function createNote(projectId, noteableType, // 'issue' 또는 'merge_requ
|
|
|
1526
1598
|
noteableIid, body) {
|
|
1527
1599
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1528
1600
|
// ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능
|
|
1529
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
|
|
1601
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation
|
|
1530
1602
|
);
|
|
1531
1603
|
const response = await fetch(url.toString(), {
|
|
1532
1604
|
...DEFAULT_FETCH_CONFIG,
|
|
@@ -1558,7 +1630,7 @@ noteableIid, body) {
|
|
|
1558
1630
|
*/
|
|
1559
1631
|
async function createMergeRequestThread(projectId, mergeRequestIid, body, position, createdAt) {
|
|
1560
1632
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1561
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`);
|
|
1633
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/merge_requests/${mergeRequestIid}/discussions`);
|
|
1562
1634
|
const payload = { body };
|
|
1563
1635
|
// Add optional parameters if provided
|
|
1564
1636
|
if (position) {
|
|
@@ -1653,7 +1725,7 @@ async function verifyNamespaceExistence(namespacePath, parentId) {
|
|
|
1653
1725
|
*/
|
|
1654
1726
|
async function getProject(projectId, options = {}) {
|
|
1655
1727
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1656
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}`);
|
|
1728
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}`);
|
|
1657
1729
|
if (options.license) {
|
|
1658
1730
|
url.searchParams.append("license", "true");
|
|
1659
1731
|
}
|
|
@@ -1710,7 +1782,7 @@ async function listProjects(options = {}) {
|
|
|
1710
1782
|
async function listLabels(projectId, options = {}) {
|
|
1711
1783
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1712
1784
|
// Construct the URL with project path
|
|
1713
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`);
|
|
1785
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`);
|
|
1714
1786
|
// Add query parameters
|
|
1715
1787
|
Object.entries(options).forEach(([key, value]) => {
|
|
1716
1788
|
if (value !== undefined) {
|
|
@@ -1742,7 +1814,7 @@ async function listLabels(projectId, options = {}) {
|
|
|
1742
1814
|
*/
|
|
1743
1815
|
async function getLabel(projectId, labelId, includeAncestorGroups) {
|
|
1744
1816
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1745
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`);
|
|
1817
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`);
|
|
1746
1818
|
// Add query parameters
|
|
1747
1819
|
if (includeAncestorGroups !== undefined) {
|
|
1748
1820
|
url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
|
|
@@ -1767,7 +1839,7 @@ async function getLabel(projectId, labelId, includeAncestorGroups) {
|
|
|
1767
1839
|
async function createLabel(projectId, options) {
|
|
1768
1840
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1769
1841
|
// Make the API request
|
|
1770
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`, {
|
|
1842
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels`, {
|
|
1771
1843
|
...DEFAULT_FETCH_CONFIG,
|
|
1772
1844
|
method: "POST",
|
|
1773
1845
|
body: JSON.stringify(options),
|
|
@@ -1789,7 +1861,7 @@ async function createLabel(projectId, options) {
|
|
|
1789
1861
|
async function updateLabel(projectId, labelId, options) {
|
|
1790
1862
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1791
1863
|
// Make the API request
|
|
1792
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
|
|
1864
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
|
|
1793
1865
|
...DEFAULT_FETCH_CONFIG,
|
|
1794
1866
|
method: "PUT",
|
|
1795
1867
|
body: JSON.stringify(options),
|
|
@@ -1809,7 +1881,7 @@ async function updateLabel(projectId, labelId, options) {
|
|
|
1809
1881
|
async function deleteLabel(projectId, labelId) {
|
|
1810
1882
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1811
1883
|
// Make the API request
|
|
1812
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
|
|
1884
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/labels/${encodeURIComponent(String(labelId))}`, {
|
|
1813
1885
|
...DEFAULT_FETCH_CONFIG,
|
|
1814
1886
|
method: "DELETE",
|
|
1815
1887
|
});
|
|
@@ -1870,7 +1942,7 @@ async function listGroupProjects(options) {
|
|
|
1870
1942
|
*/
|
|
1871
1943
|
async function listWikiPages(projectId, options = {}) {
|
|
1872
1944
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1873
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`);
|
|
1945
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`);
|
|
1874
1946
|
if (options.page)
|
|
1875
1947
|
url.searchParams.append("page", options.page.toString());
|
|
1876
1948
|
if (options.per_page)
|
|
@@ -1889,7 +1961,7 @@ async function listWikiPages(projectId, options = {}) {
|
|
|
1889
1961
|
*/
|
|
1890
1962
|
async function getWikiPage(projectId, slug) {
|
|
1891
1963
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1892
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, { ...DEFAULT_FETCH_CONFIG });
|
|
1964
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, { ...DEFAULT_FETCH_CONFIG });
|
|
1893
1965
|
await handleGitLabError(response);
|
|
1894
1966
|
const data = await response.json();
|
|
1895
1967
|
return GitLabWikiPageSchema.parse(data);
|
|
@@ -1902,7 +1974,7 @@ async function createWikiPage(projectId, title, content, format) {
|
|
|
1902
1974
|
const body = { title, content };
|
|
1903
1975
|
if (format)
|
|
1904
1976
|
body.format = format;
|
|
1905
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis`, {
|
|
1977
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis`, {
|
|
1906
1978
|
...DEFAULT_FETCH_CONFIG,
|
|
1907
1979
|
method: "POST",
|
|
1908
1980
|
body: JSON.stringify(body),
|
|
@@ -1923,7 +1995,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
|
|
|
1923
1995
|
body.content = content;
|
|
1924
1996
|
if (format)
|
|
1925
1997
|
body.format = format;
|
|
1926
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
|
|
1998
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
|
|
1927
1999
|
...DEFAULT_FETCH_CONFIG,
|
|
1928
2000
|
method: "PUT",
|
|
1929
2001
|
body: JSON.stringify(body),
|
|
@@ -1937,7 +2009,7 @@ async function updateWikiPage(projectId, slug, title, content, format) {
|
|
|
1937
2009
|
*/
|
|
1938
2010
|
async function deleteWikiPage(projectId, slug) {
|
|
1939
2011
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1940
|
-
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/wikis/${encodeURIComponent(slug)}`, {
|
|
2012
|
+
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/wikis/${encodeURIComponent(slug)}`, {
|
|
1941
2013
|
...DEFAULT_FETCH_CONFIG,
|
|
1942
2014
|
method: "DELETE",
|
|
1943
2015
|
});
|
|
@@ -1952,7 +2024,7 @@ async function deleteWikiPage(projectId, slug) {
|
|
|
1952
2024
|
*/
|
|
1953
2025
|
async function listPipelines(projectId, options = {}) {
|
|
1954
2026
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1955
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines`);
|
|
2027
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines`);
|
|
1956
2028
|
// Add all query parameters
|
|
1957
2029
|
Object.entries(options).forEach(([key, value]) => {
|
|
1958
2030
|
if (value !== undefined) {
|
|
@@ -1975,7 +2047,7 @@ async function listPipelines(projectId, options = {}) {
|
|
|
1975
2047
|
*/
|
|
1976
2048
|
async function getPipeline(projectId, pipelineId) {
|
|
1977
2049
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1978
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}`);
|
|
2050
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}`);
|
|
1979
2051
|
const response = await fetch(url.toString(), {
|
|
1980
2052
|
...DEFAULT_FETCH_CONFIG,
|
|
1981
2053
|
});
|
|
@@ -1996,7 +2068,7 @@ async function getPipeline(projectId, pipelineId) {
|
|
|
1996
2068
|
*/
|
|
1997
2069
|
async function listPipelineJobs(projectId, pipelineId, options = {}) {
|
|
1998
2070
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1999
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/jobs`);
|
|
2071
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/jobs`);
|
|
2000
2072
|
// Add all query parameters
|
|
2001
2073
|
Object.entries(options).forEach(([key, value]) => {
|
|
2002
2074
|
if (value !== undefined) {
|
|
@@ -2020,7 +2092,7 @@ async function listPipelineJobs(projectId, pipelineId, options = {}) {
|
|
|
2020
2092
|
}
|
|
2021
2093
|
async function getPipelineJob(projectId, jobId) {
|
|
2022
2094
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
2023
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/jobs/${jobId}`);
|
|
2095
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}`);
|
|
2024
2096
|
const response = await fetch(url.toString(), {
|
|
2025
2097
|
...DEFAULT_FETCH_CONFIG,
|
|
2026
2098
|
});
|
|
@@ -2042,7 +2114,7 @@ async function getPipelineJob(projectId, jobId) {
|
|
|
2042
2114
|
*/
|
|
2043
2115
|
async function getPipelineJobOutput(projectId, jobId, limit, offset) {
|
|
2044
2116
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
2045
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/trace`);
|
|
2117
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/jobs/${jobId}/trace`);
|
|
2046
2118
|
const response = await fetch(url.toString(), {
|
|
2047
2119
|
...DEFAULT_FETCH_CONFIG,
|
|
2048
2120
|
headers: {
|
|
@@ -2087,7 +2159,7 @@ async function getPipelineJobOutput(projectId, jobId, limit, offset) {
|
|
|
2087
2159
|
*/
|
|
2088
2160
|
async function createPipeline(projectId, ref, variables) {
|
|
2089
2161
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
2090
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipeline`);
|
|
2162
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipeline`);
|
|
2091
2163
|
const body = { ref };
|
|
2092
2164
|
if (variables && variables.length > 0) {
|
|
2093
2165
|
body.variables = variables.reduce((acc, { key, value }) => {
|
|
@@ -2113,7 +2185,7 @@ async function createPipeline(projectId, ref, variables) {
|
|
|
2113
2185
|
*/
|
|
2114
2186
|
async function retryPipeline(projectId, pipelineId) {
|
|
2115
2187
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
2116
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/retry`);
|
|
2188
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/retry`);
|
|
2117
2189
|
const response = await fetch(url.toString(), {
|
|
2118
2190
|
method: "POST",
|
|
2119
2191
|
headers: DEFAULT_HEADERS,
|
|
@@ -2131,7 +2203,7 @@ async function retryPipeline(projectId, pipelineId) {
|
|
|
2131
2203
|
*/
|
|
2132
2204
|
async function cancelPipeline(projectId, pipelineId) {
|
|
2133
2205
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
2134
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`);
|
|
2206
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/pipelines/${pipelineId}/cancel`);
|
|
2135
2207
|
const response = await fetch(url.toString(), {
|
|
2136
2208
|
method: "POST",
|
|
2137
2209
|
headers: DEFAULT_HEADERS,
|
|
@@ -2190,7 +2262,7 @@ async function getRepositoryTree(options) {
|
|
|
2190
2262
|
*/
|
|
2191
2263
|
async function listProjectMilestones(projectId, options) {
|
|
2192
2264
|
projectId = decodeURIComponent(projectId);
|
|
2193
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones`);
|
|
2265
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
|
|
2194
2266
|
Object.entries(options).forEach(([key, value]) => {
|
|
2195
2267
|
if (value !== undefined) {
|
|
2196
2268
|
if (key === "iids" && Array.isArray(value) && value.length > 0) {
|
|
@@ -2218,7 +2290,7 @@ async function listProjectMilestones(projectId, options) {
|
|
|
2218
2290
|
*/
|
|
2219
2291
|
async function getProjectMilestone(projectId, milestoneId) {
|
|
2220
2292
|
projectId = decodeURIComponent(projectId);
|
|
2221
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
|
|
2293
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
|
|
2222
2294
|
const response = await fetch(url.toString(), {
|
|
2223
2295
|
...DEFAULT_FETCH_CONFIG,
|
|
2224
2296
|
});
|
|
@@ -2234,7 +2306,7 @@ async function getProjectMilestone(projectId, milestoneId) {
|
|
|
2234
2306
|
*/
|
|
2235
2307
|
async function createProjectMilestone(projectId, options) {
|
|
2236
2308
|
projectId = decodeURIComponent(projectId);
|
|
2237
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones`);
|
|
2309
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones`);
|
|
2238
2310
|
const response = await fetch(url.toString(), {
|
|
2239
2311
|
...DEFAULT_FETCH_CONFIG,
|
|
2240
2312
|
method: "POST",
|
|
@@ -2253,7 +2325,7 @@ async function createProjectMilestone(projectId, options) {
|
|
|
2253
2325
|
*/
|
|
2254
2326
|
async function editProjectMilestone(projectId, milestoneId, options) {
|
|
2255
2327
|
projectId = decodeURIComponent(projectId);
|
|
2256
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
|
|
2328
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
|
|
2257
2329
|
const response = await fetch(url.toString(), {
|
|
2258
2330
|
...DEFAULT_FETCH_CONFIG,
|
|
2259
2331
|
method: "PUT",
|
|
@@ -2271,7 +2343,7 @@ async function editProjectMilestone(projectId, milestoneId, options) {
|
|
|
2271
2343
|
*/
|
|
2272
2344
|
async function deleteProjectMilestone(projectId, milestoneId) {
|
|
2273
2345
|
projectId = decodeURIComponent(projectId);
|
|
2274
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}`);
|
|
2346
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}`);
|
|
2275
2347
|
const response = await fetch(url.toString(), {
|
|
2276
2348
|
...DEFAULT_FETCH_CONFIG,
|
|
2277
2349
|
method: "DELETE",
|
|
@@ -2286,7 +2358,7 @@ async function deleteProjectMilestone(projectId, milestoneId) {
|
|
|
2286
2358
|
*/
|
|
2287
2359
|
async function getMilestoneIssues(projectId, milestoneId) {
|
|
2288
2360
|
projectId = decodeURIComponent(projectId);
|
|
2289
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/issues`);
|
|
2361
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/issues`);
|
|
2290
2362
|
const response = await fetch(url.toString(), {
|
|
2291
2363
|
...DEFAULT_FETCH_CONFIG,
|
|
2292
2364
|
});
|
|
@@ -2302,7 +2374,7 @@ async function getMilestoneIssues(projectId, milestoneId) {
|
|
|
2302
2374
|
*/
|
|
2303
2375
|
async function getMilestoneMergeRequests(projectId, milestoneId) {
|
|
2304
2376
|
projectId = decodeURIComponent(projectId);
|
|
2305
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/merge_requests`);
|
|
2377
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/merge_requests`);
|
|
2306
2378
|
const response = await fetch(url.toString(), {
|
|
2307
2379
|
...DEFAULT_FETCH_CONFIG,
|
|
2308
2380
|
});
|
|
@@ -2318,7 +2390,7 @@ async function getMilestoneMergeRequests(projectId, milestoneId) {
|
|
|
2318
2390
|
*/
|
|
2319
2391
|
async function promoteProjectMilestone(projectId, milestoneId) {
|
|
2320
2392
|
projectId = decodeURIComponent(projectId);
|
|
2321
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/promote`);
|
|
2393
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/promote`);
|
|
2322
2394
|
const response = await fetch(url.toString(), {
|
|
2323
2395
|
...DEFAULT_FETCH_CONFIG,
|
|
2324
2396
|
method: "POST",
|
|
@@ -2335,7 +2407,7 @@ async function promoteProjectMilestone(projectId, milestoneId) {
|
|
|
2335
2407
|
*/
|
|
2336
2408
|
async function getMilestoneBurndownEvents(projectId, milestoneId) {
|
|
2337
2409
|
projectId = decodeURIComponent(projectId);
|
|
2338
|
-
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/milestones/${milestoneId}/burndown_events`);
|
|
2410
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/milestones/${milestoneId}/burndown_events`);
|
|
2339
2411
|
const response = await fetch(url.toString(), {
|
|
2340
2412
|
...DEFAULT_FETCH_CONFIG,
|
|
2341
2413
|
});
|
|
@@ -2395,6 +2467,89 @@ async function getUsers(usernames) {
|
|
|
2395
2467
|
}
|
|
2396
2468
|
return GitLabUsersResponseSchema.parse(users);
|
|
2397
2469
|
}
|
|
2470
|
+
/**
|
|
2471
|
+
* List repository commits
|
|
2472
|
+
* 저장소 커밋 목록 조회
|
|
2473
|
+
*
|
|
2474
|
+
* @param {string} projectId - Project ID or URL-encoded path
|
|
2475
|
+
* @param {ListCommitsOptions} options - List commits options
|
|
2476
|
+
* @returns {Promise<GitLabCommit[]>} List of commits
|
|
2477
|
+
*/
|
|
2478
|
+
async function listCommits(projectId, options = {}) {
|
|
2479
|
+
projectId = decodeURIComponent(projectId);
|
|
2480
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits`);
|
|
2481
|
+
// Add query parameters
|
|
2482
|
+
if (options.ref_name)
|
|
2483
|
+
url.searchParams.append("ref_name", options.ref_name);
|
|
2484
|
+
if (options.since)
|
|
2485
|
+
url.searchParams.append("since", options.since);
|
|
2486
|
+
if (options.until)
|
|
2487
|
+
url.searchParams.append("until", options.until);
|
|
2488
|
+
if (options.path)
|
|
2489
|
+
url.searchParams.append("path", options.path);
|
|
2490
|
+
if (options.author)
|
|
2491
|
+
url.searchParams.append("author", options.author);
|
|
2492
|
+
if (options.all)
|
|
2493
|
+
url.searchParams.append("all", options.all.toString());
|
|
2494
|
+
if (options.with_stats)
|
|
2495
|
+
url.searchParams.append("with_stats", options.with_stats.toString());
|
|
2496
|
+
if (options.first_parent)
|
|
2497
|
+
url.searchParams.append("first_parent", options.first_parent.toString());
|
|
2498
|
+
if (options.order)
|
|
2499
|
+
url.searchParams.append("order", options.order);
|
|
2500
|
+
if (options.trailers)
|
|
2501
|
+
url.searchParams.append("trailers", options.trailers.toString());
|
|
2502
|
+
if (options.page)
|
|
2503
|
+
url.searchParams.append("page", options.page.toString());
|
|
2504
|
+
if (options.per_page)
|
|
2505
|
+
url.searchParams.append("per_page", options.per_page.toString());
|
|
2506
|
+
const response = await fetch(url.toString(), {
|
|
2507
|
+
...DEFAULT_FETCH_CONFIG,
|
|
2508
|
+
});
|
|
2509
|
+
await handleGitLabError(response);
|
|
2510
|
+
const data = await response.json();
|
|
2511
|
+
return z.array(GitLabCommitSchema).parse(data);
|
|
2512
|
+
}
|
|
2513
|
+
/**
|
|
2514
|
+
* Get a single commit
|
|
2515
|
+
* 단일 커밋 정보 조회
|
|
2516
|
+
*
|
|
2517
|
+
* @param {string} projectId - Project ID or URL-encoded path
|
|
2518
|
+
* @param {string} sha - The commit hash or name of a repository branch or tag
|
|
2519
|
+
* @param {boolean} [stats] - Include commit stats
|
|
2520
|
+
* @returns {Promise<GitLabCommit>} The commit details
|
|
2521
|
+
*/
|
|
2522
|
+
async function getCommit(projectId, sha, stats) {
|
|
2523
|
+
projectId = decodeURIComponent(projectId);
|
|
2524
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}`);
|
|
2525
|
+
if (stats) {
|
|
2526
|
+
url.searchParams.append("stats", "true");
|
|
2527
|
+
}
|
|
2528
|
+
const response = await fetch(url.toString(), {
|
|
2529
|
+
...DEFAULT_FETCH_CONFIG,
|
|
2530
|
+
});
|
|
2531
|
+
await handleGitLabError(response);
|
|
2532
|
+
const data = await response.json();
|
|
2533
|
+
return GitLabCommitSchema.parse(data);
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Get commit diff
|
|
2537
|
+
* 커밋 변경사항 조회
|
|
2538
|
+
*
|
|
2539
|
+
* @param {string} projectId - Project ID or URL-encoded path
|
|
2540
|
+
* @param {string} sha - The commit hash or name of a repository branch or tag
|
|
2541
|
+
* @returns {Promise<GitLabMergeRequestDiff[]>} The commit diffs
|
|
2542
|
+
*/
|
|
2543
|
+
async function getCommitDiff(projectId, sha) {
|
|
2544
|
+
projectId = decodeURIComponent(projectId);
|
|
2545
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/repository/commits/${encodeURIComponent(sha)}/diff`);
|
|
2546
|
+
const response = await fetch(url.toString(), {
|
|
2547
|
+
...DEFAULT_FETCH_CONFIG,
|
|
2548
|
+
});
|
|
2549
|
+
await handleGitLabError(response);
|
|
2550
|
+
const data = await response.json();
|
|
2551
|
+
return z.array(GitLabDiffSchema).parse(data);
|
|
2552
|
+
}
|
|
2398
2553
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
2399
2554
|
// Apply read-only filter first
|
|
2400
2555
|
const tools0 = GITLAB_READ_ONLY_MODE
|
|
@@ -2591,6 +2746,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2591
2746
|
content: [{ type: "text", text: JSON.stringify(diffs, null, 2) }],
|
|
2592
2747
|
};
|
|
2593
2748
|
}
|
|
2749
|
+
case "list_merge_request_diffs": {
|
|
2750
|
+
const args = ListMergeRequestDiffsSchema.parse(request.params.arguments);
|
|
2751
|
+
const changes = await listMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.page, args.per_page, args.unidiff);
|
|
2752
|
+
return {
|
|
2753
|
+
content: [{ type: "text", text: JSON.stringify(changes, null, 2) }],
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2594
2756
|
case "update_merge_request": {
|
|
2595
2757
|
const args = UpdateMergeRequestSchema.parse(request.params.arguments);
|
|
2596
2758
|
const { project_id, merge_request_iid, source_branch, ...options } = args;
|
|
@@ -3088,6 +3250,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3088
3250
|
],
|
|
3089
3251
|
};
|
|
3090
3252
|
}
|
|
3253
|
+
case "list_commits": {
|
|
3254
|
+
const args = ListCommitsSchema.parse(request.params.arguments);
|
|
3255
|
+
const commits = await listCommits(args.project_id, args);
|
|
3256
|
+
return {
|
|
3257
|
+
content: [{ type: "text", text: JSON.stringify(commits, null, 2) }],
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
case "get_commit": {
|
|
3261
|
+
const args = GetCommitSchema.parse(request.params.arguments);
|
|
3262
|
+
const commit = await getCommit(args.project_id, args.sha, args.stats);
|
|
3263
|
+
return {
|
|
3264
|
+
content: [{ type: "text", text: JSON.stringify(commit, null, 2) }],
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
case "get_commit_diff": {
|
|
3268
|
+
const args = GetCommitDiffSchema.parse(request.params.arguments);
|
|
3269
|
+
const diff = await getCommitDiff(args.project_id, args.sha);
|
|
3270
|
+
return {
|
|
3271
|
+
content: [{ type: "text", text: JSON.stringify(diff, null, 2) }],
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3091
3274
|
default:
|
|
3092
3275
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
3093
3276
|
}
|
|
@@ -3136,6 +3319,12 @@ async function runServer() {
|
|
|
3136
3319
|
res.status(400).send("No transport found for sessionId");
|
|
3137
3320
|
}
|
|
3138
3321
|
});
|
|
3322
|
+
app.get("/health", (_, res) => {
|
|
3323
|
+
res.status(200).json({
|
|
3324
|
+
status: "healthy",
|
|
3325
|
+
version: process.env.npm_package_version || "unknown",
|
|
3326
|
+
});
|
|
3327
|
+
});
|
|
3139
3328
|
const PORT = process.env.PORT || 3002;
|
|
3140
3329
|
app.listen(PORT, () => {
|
|
3141
3330
|
console.log(`Server is running on port ${PORT}`);
|
package/build/schemas.js
CHANGED
|
@@ -374,8 +374,17 @@ export const GitLabCommitSchema = z.object({
|
|
|
374
374
|
committer_name: z.string(),
|
|
375
375
|
committer_email: z.string(),
|
|
376
376
|
committed_date: z.string(),
|
|
377
|
+
created_at: z.string().optional(), // Add created_at field
|
|
378
|
+
message: z.string().optional(), // Add full message field
|
|
377
379
|
web_url: z.string(), // Changed from html_url to match GitLab API
|
|
378
380
|
parent_ids: z.array(z.string()), // Changed from parents to match GitLab API
|
|
381
|
+
stats: z.object({
|
|
382
|
+
additions: z.number().optional().nullable(),
|
|
383
|
+
deletions: z.number().optional().nullable(),
|
|
384
|
+
total: z.number().optional().nullable(),
|
|
385
|
+
}).optional(), // Only present when with_stats=true
|
|
386
|
+
trailers: z.record(z.string()).optional().default({}), // Git trailers, may be empty object
|
|
387
|
+
extended_trailers: z.record(z.array(z.string())).optional().default({}), // Extended trailers, may be empty object
|
|
379
388
|
});
|
|
380
389
|
// Reference schema
|
|
381
390
|
export const GitLabReferenceSchema = z.object({
|
|
@@ -596,6 +605,20 @@ export const GitLabMergeRequestSchema = z.object({
|
|
|
596
605
|
squash: z.boolean().optional(),
|
|
597
606
|
labels: z.array(z.string()).optional(),
|
|
598
607
|
});
|
|
608
|
+
export const LineRangeSchema = z.object({
|
|
609
|
+
start: z.object({
|
|
610
|
+
line_code: z.string().nullable().optional().describe("CRITICAL: Line identifier in format '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. USUALLY REQUIRED for GitLab diff comments despite being optional in schema. Example: 'a1b2c3d4e5f6_10_15'. Get this from GitLab diff API response, never fabricate."),
|
|
611
|
+
type: z.enum(["new", "old", "expanded"]).nullable().optional().describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. MUST match the line_code format and old_line/new_line values."),
|
|
612
|
+
old_line: z.number().nullable().optional().describe("Line number in original file (before changes). REQUIRED when type='old', NULL when type='new' (for purely added lines), can be present for context lines."),
|
|
613
|
+
new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). REQUIRED when type='new', NULL when type='old' (for purely deleted lines), can be present for context lines."),
|
|
614
|
+
}).describe("Start line position for multiline comment range. MUST specify either old_line OR new_line (or both for context), never neither."),
|
|
615
|
+
end: z.object({
|
|
616
|
+
line_code: z.string().nullable().optional().describe("CRITICAL: Line identifier in format '{file_path_sha1_hash}_{old_line_number}_{new_line_number}'. USUALLY REQUIRED for GitLab diff comments despite being optional in schema. Example: 'a1b2c3d4e5f6_12_17'. Must be from same file as start.line_code."),
|
|
617
|
+
type: z.enum(["new", "old", "expanded"]).nullable().optional().describe("Line type: 'old' = deleted/original line, 'new' = added/modified line, null = unchanged context. SHOULD MATCH start.type for consistent ranges (don't mix old/new types)."),
|
|
618
|
+
old_line: z.number().nullable().optional().describe("Line number in original file (before changes). REQUIRED when type='old', NULL when type='new' (for purely added lines), can be present for context lines. MUST be >= start.old_line if both specified."),
|
|
619
|
+
new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). REQUIRED when type='new', NULL when type='old' (for purely deleted lines), can be present for context lines. MUST be >= start.new_line if both specified."),
|
|
620
|
+
}).describe("End line position for multiline comment range. MUST specify either old_line OR new_line (or both for context), never neither. Range must be valid (end >= start)."),
|
|
621
|
+
}).describe("Line range for multiline comments on GitLab merge request diffs. VALIDATION RULES: 1) line_code is critical for GitLab API success, 2) start/end must have consistent types, 3) line numbers must form valid range, 4) get line_code from GitLab diff API, never generate manually.");
|
|
599
622
|
// Discussion related schemas
|
|
600
623
|
export const GitLabDiscussionNoteSchema = z.object({
|
|
601
624
|
id: z.number(),
|
|
@@ -620,28 +643,12 @@ export const GitLabDiscussionNoteSchema = z.object({
|
|
|
620
643
|
base_sha: z.string(),
|
|
621
644
|
start_sha: z.string(),
|
|
622
645
|
head_sha: z.string(),
|
|
623
|
-
old_path: z.string(),
|
|
624
|
-
new_path: z.string(),
|
|
646
|
+
old_path: z.string().nullable().optional().describe("File path before change"),
|
|
647
|
+
new_path: z.string().nullable().optional().describe("File path after change"),
|
|
625
648
|
position_type: z.enum(["text", "image", "file"]),
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
line_range:
|
|
629
|
-
.object({
|
|
630
|
-
start: z.object({
|
|
631
|
-
line_code: z.string(),
|
|
632
|
-
type: z.enum(["new", "old", "expanded"]),
|
|
633
|
-
old_line: z.number().nullish(), // This is missing for image diffs
|
|
634
|
-
new_line: z.number().nullish(), // This is missing for image diffs
|
|
635
|
-
}),
|
|
636
|
-
end: z.object({
|
|
637
|
-
line_code: z.string(),
|
|
638
|
-
type: z.enum(["new", "old", "expanded"]),
|
|
639
|
-
old_line: z.number().nullish(), // This is missing for image diffs
|
|
640
|
-
new_line: z.number().nullish(), // This is missing for image diffs
|
|
641
|
-
}),
|
|
642
|
-
})
|
|
643
|
-
.nullable()
|
|
644
|
-
.optional(), // For multi-line diff notes
|
|
649
|
+
new_line: z.number().nullable().optional().describe("Line number in the modified file (after changes). Used for added lines and context lines. Null for deleted lines."),
|
|
650
|
+
old_line: z.number().nullable().optional().describe("Line number in the original file (before changes). Used for deleted lines and context lines. Null for newly added lines."),
|
|
651
|
+
line_range: LineRangeSchema.nullable().optional(), // For multi-line diff notes
|
|
645
652
|
width: z.number().optional(), // For image diff notes
|
|
646
653
|
height: z.number().optional(), // For image diff notes
|
|
647
654
|
x: z.number().optional(), // For image diff notes
|
|
@@ -818,6 +825,11 @@ export const UpdateMergeRequestSchema = GetMergeRequestSchema.extend({
|
|
|
818
825
|
export const GetMergeRequestDiffsSchema = GetMergeRequestSchema.extend({
|
|
819
826
|
view: z.enum(["inline", "parallel"]).optional().describe("Diff view type"),
|
|
820
827
|
});
|
|
828
|
+
export const ListMergeRequestDiffsSchema = GetMergeRequestSchema.extend({
|
|
829
|
+
page: z.number().optional().describe("Page number for pagination (default: 1)"),
|
|
830
|
+
per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
|
|
831
|
+
unidiff: z.boolean().optional().describe("Present diffs in the unified diff format. Default is false. Introduced in GitLab 16.5."),
|
|
832
|
+
});
|
|
821
833
|
export const CreateNoteSchema = z.object({
|
|
822
834
|
project_id: z.string().describe("Project ID or namespace/project_path"),
|
|
823
835
|
noteable_type: z
|
|
@@ -1124,18 +1136,19 @@ export const GitLabWikiPageSchema = z.object({
|
|
|
1124
1136
|
});
|
|
1125
1137
|
// Merge Request Thread position schema - used for diff notes
|
|
1126
1138
|
export const MergeRequestThreadPositionSchema = z.object({
|
|
1127
|
-
base_sha: z.string().describe("Base commit SHA in the source branch"),
|
|
1128
|
-
head_sha: z.string().describe("SHA referencing HEAD of the source branch"),
|
|
1129
|
-
start_sha: z.string().describe("SHA referencing the start commit of the source branch"),
|
|
1130
|
-
position_type: z.enum(["text", "image", "file"]).describe("
|
|
1131
|
-
new_path: z.string().optional().describe("File path after
|
|
1132
|
-
old_path: z.string().optional().describe("File path before
|
|
1133
|
-
new_line: z.number().nullable().optional().describe("Line number after
|
|
1134
|
-
old_line: z.number().nullable().optional().describe("Line number before
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
+
base_sha: z.string().describe("REQUIRED: Base commit SHA in the source branch. Get this from merge request diff_refs.base_sha."),
|
|
1140
|
+
head_sha: z.string().describe("REQUIRED: SHA referencing HEAD of the source branch. Get this from merge request diff_refs.head_sha."),
|
|
1141
|
+
start_sha: z.string().describe("REQUIRED: SHA referencing the start commit of the source branch. Get this from merge request diff_refs.start_sha."),
|
|
1142
|
+
position_type: z.enum(["text", "image", "file"]).describe("REQUIRED: Position type. Use 'text' for code diffs, 'image' for image diffs, 'file' for file-level comments."),
|
|
1143
|
+
new_path: z.string().nullable().optional().describe("File path after changes. REQUIRED for most diff comments. Use same as old_path if file wasn't renamed."),
|
|
1144
|
+
old_path: z.string().nullable().optional().describe("File path before changes. REQUIRED for most diff comments. Use same as new_path if file wasn't renamed."),
|
|
1145
|
+
new_line: z.number().nullable().optional().describe("Line number in modified file (after changes). Use for added lines or context lines. NULL for deleted lines. For single-line comments on new lines."),
|
|
1146
|
+
old_line: z.number().nullable().optional().describe("Line number in original file (before changes). Use for deleted lines or context lines. NULL for added lines. For single-line comments on old lines."),
|
|
1147
|
+
line_range: LineRangeSchema.optional().describe("MULTILINE COMMENTS: Specify start/end line positions for commenting on multiple lines. Alternative to single old_line/new_line."),
|
|
1148
|
+
width: z.number().optional().describe("IMAGE DIFFS ONLY: Width of the image (for position_type='image')."),
|
|
1149
|
+
height: z.number().optional().describe("IMAGE DIFFS ONLY: Height of the image (for position_type='image')."),
|
|
1150
|
+
x: z.number().optional().describe("IMAGE DIFFS ONLY: X coordinate on the image (for position_type='image')."),
|
|
1151
|
+
y: z.number().optional().describe("IMAGE DIFFS ONLY: Y coordinate on the image (for position_type='image')."),
|
|
1139
1152
|
});
|
|
1140
1153
|
// Schema for creating a new merge request thread
|
|
1141
1154
|
export const CreateMergeRequestThreadSchema = ProjectParamsSchema.extend({
|
|
@@ -1202,3 +1215,28 @@ export const GetMilestoneMergeRequestsSchema = GetProjectMilestoneSchema.merge(P
|
|
|
1202
1215
|
export const PromoteProjectMilestoneSchema = GetProjectMilestoneSchema;
|
|
1203
1216
|
// Schema for getting burndown chart events for a milestone
|
|
1204
1217
|
export const GetMilestoneBurndownEventsSchema = GetProjectMilestoneSchema.merge(PaginationOptionsSchema);
|
|
1218
|
+
// Add schemas for commit operations
|
|
1219
|
+
export const ListCommitsSchema = z.object({
|
|
1220
|
+
project_id: z.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1221
|
+
ref_name: z.string().optional().describe("The name of a repository branch, tag or revision range, or if not given the default branch"),
|
|
1222
|
+
since: z.string().optional().describe("Only commits after or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ"),
|
|
1223
|
+
until: z.string().optional().describe("Only commits before or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ"),
|
|
1224
|
+
path: z.string().optional().describe("The file path"),
|
|
1225
|
+
author: z.string().optional().describe("Search commits by commit author"),
|
|
1226
|
+
all: z.boolean().optional().describe("Retrieve every commit from the repository"),
|
|
1227
|
+
with_stats: z.boolean().optional().describe("Stats about each commit are added to the response"),
|
|
1228
|
+
first_parent: z.boolean().optional().describe("Follow only the first parent commit upon seeing a merge commit"),
|
|
1229
|
+
order: z.enum(["default", "topo"]).optional().describe("List commits in order"),
|
|
1230
|
+
trailers: z.boolean().optional().describe("Parse and include Git trailers for every commit"),
|
|
1231
|
+
page: z.number().optional().describe("Page number for pagination (default: 1)"),
|
|
1232
|
+
per_page: z.number().optional().describe("Number of items per page (max: 100, default: 20)"),
|
|
1233
|
+
});
|
|
1234
|
+
export const GetCommitSchema = z.object({
|
|
1235
|
+
project_id: z.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1236
|
+
sha: z.string().describe("The commit hash or name of a repository branch or tag"),
|
|
1237
|
+
stats: z.boolean().optional().describe("Include commit stats"),
|
|
1238
|
+
});
|
|
1239
|
+
export const GetCommitDiffSchema = z.object({
|
|
1240
|
+
project_id: z.string().describe("Project ID or complete URL-encoded path to project"),
|
|
1241
|
+
sha: z.string().describe("The commit hash or name of a repository branch or tag"),
|
|
1242
|
+
});
|