@gitlab/opencode-gitlab-plugin 1.0.1
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/CHANGELOG.md +292 -0
- package/README.md +1283 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +3853 -0
- package/package.json +87 -0
- package/scripts/postinstall.mjs +163 -0
package/README.md
ADDED
|
@@ -0,0 +1,1283 @@
|
|
|
1
|
+
# OpenCode GitLab Plugin
|
|
2
|
+
|
|
3
|
+
[](https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/pipelines)
|
|
4
|
+
[](https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/packages)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
A comprehensive GitLab API plugin for OpenCode that provides AI-powered access to GitLab's REST API. This plugin enables seamless interaction with merge requests, issues, pipelines, repositories, epics, and more through natural language commands.
|
|
8
|
+
|
|
9
|
+
## 📋 Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Features](#-features)
|
|
12
|
+
- [Architecture](#-architecture)
|
|
13
|
+
- [Installation](#-installation)
|
|
14
|
+
- [Configuration](#-configuration)
|
|
15
|
+
- [Available Tools](#-available-tools)
|
|
16
|
+
- [Usage Examples](#-usage-examples)
|
|
17
|
+
- [Development](#-development)
|
|
18
|
+
- [CI/CD Pipeline](#-cicd-pipeline)
|
|
19
|
+
- [API Reference](#-api-reference)
|
|
20
|
+
- [Contributing](#-contributing)
|
|
21
|
+
- [License](#-license)
|
|
22
|
+
|
|
23
|
+
## ✨ Features
|
|
24
|
+
|
|
25
|
+
### Core Capabilities
|
|
26
|
+
|
|
27
|
+
- **🔀 Merge Requests**: Full CRUD operations, discussions, notes, changes, commits, and pipelines
|
|
28
|
+
- **📝 Issues**: Create, read, update, and comment on issues with advanced filtering
|
|
29
|
+
- **🎯 Work Items**: Unified interface for issues, epics, tasks, and other work tracking items
|
|
30
|
+
- **🚀 CI/CD Pipelines**: Monitor, analyze, and retry pipeline jobs with detailed logs
|
|
31
|
+
- **📦 Repository Operations**: File management, commits, branches, and tree navigation
|
|
32
|
+
- **🔍 Advanced Search**: Multi-scope search across projects, code, issues, and merge requests
|
|
33
|
+
- **📊 Epics**: Enterprise-level epic management with issue associations
|
|
34
|
+
- **✅ TODOs**: Personal task management and notifications
|
|
35
|
+
- **🔒 Security**: Vulnerability scanning and security report access
|
|
36
|
+
- **📚 Wiki**: Wiki page content retrieval
|
|
37
|
+
- **👥 Project Management**: Member management and project details
|
|
38
|
+
|
|
39
|
+
### Technical Features
|
|
40
|
+
|
|
41
|
+
- **TypeScript**: Full type safety with comprehensive type definitions
|
|
42
|
+
- **ESM Support**: Modern ES modules for optimal tree-shaking
|
|
43
|
+
- **Zod Validation**: Runtime schema validation for all API inputs
|
|
44
|
+
- **GraphQL Support**: Native GraphQL API support with type-safe mutations and queries
|
|
45
|
+
- **GID Validation**: Automatic validation of GitLab Global IDs with descriptive error messages
|
|
46
|
+
- **Error Handling**: Robust error handling with detailed error messages
|
|
47
|
+
- **Authentication**: Multiple authentication methods (OAuth, API tokens)
|
|
48
|
+
- **Rate Limiting**: Built-in handling for GitLab API rate limits
|
|
49
|
+
- **Caching**: Efficient API response handling
|
|
50
|
+
- **Modular Architecture**: Clean separation of concerns with client and tool modules
|
|
51
|
+
- **Comprehensive Testing**: 142 tests with full coverage of all features
|
|
52
|
+
|
|
53
|
+
## 🏗️ Architecture
|
|
54
|
+
|
|
55
|
+
### System Architecture
|
|
56
|
+
|
|
57
|
+
```mermaid
|
|
58
|
+
graph TB
|
|
59
|
+
subgraph "OpenCode Environment"
|
|
60
|
+
AI[AI Assistant]
|
|
61
|
+
Plugin[GitLab Plugin]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
subgraph "Plugin Components"
|
|
65
|
+
Tools[Tool Definitions]
|
|
66
|
+
Client[GitLab API Client]
|
|
67
|
+
Auth[Authentication Manager]
|
|
68
|
+
Validator[Zod Schema Validator]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
subgraph "GitLab API"
|
|
72
|
+
REST[REST API v4]
|
|
73
|
+
MR[Merge Requests]
|
|
74
|
+
Issues[Issues]
|
|
75
|
+
Pipelines[Pipelines]
|
|
76
|
+
Repos[Repositories]
|
|
77
|
+
Epics[Epics]
|
|
78
|
+
Security[Security]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
AI -->|Natural Language| Plugin
|
|
82
|
+
Plugin --> Tools
|
|
83
|
+
Tools --> Validator
|
|
84
|
+
Validator --> Client
|
|
85
|
+
Client --> Auth
|
|
86
|
+
Auth --> REST
|
|
87
|
+
REST --> MR
|
|
88
|
+
REST --> Issues
|
|
89
|
+
REST --> Pipelines
|
|
90
|
+
REST --> Repos
|
|
91
|
+
REST --> Epics
|
|
92
|
+
REST --> Security
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Plugin Structure
|
|
96
|
+
|
|
97
|
+
```mermaid
|
|
98
|
+
graph LR
|
|
99
|
+
subgraph "src/index.ts"
|
|
100
|
+
A[GitLabApiClient Class]
|
|
101
|
+
B[Authentication Functions]
|
|
102
|
+
C[Tool Definitions]
|
|
103
|
+
D[Plugin Export]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
A --> A1[HTTP Methods]
|
|
107
|
+
A --> A2[Merge Request APIs]
|
|
108
|
+
A --> A3[Issue APIs]
|
|
109
|
+
A --> A4[Pipeline APIs]
|
|
110
|
+
A --> A5[Repository APIs]
|
|
111
|
+
A --> A6[Epic APIs]
|
|
112
|
+
A --> A7[Search APIs]
|
|
113
|
+
|
|
114
|
+
B --> B1[readTokenFromAuthStorage]
|
|
115
|
+
B --> B2[getGitLabClient]
|
|
116
|
+
|
|
117
|
+
C --> C1[60+ Tool Definitions]
|
|
118
|
+
|
|
119
|
+
D --> A
|
|
120
|
+
D --> B
|
|
121
|
+
D --> C
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Authentication Flow
|
|
125
|
+
|
|
126
|
+
```mermaid
|
|
127
|
+
sequenceDiagram
|
|
128
|
+
participant User
|
|
129
|
+
participant Plugin
|
|
130
|
+
participant AuthManager
|
|
131
|
+
participant Storage
|
|
132
|
+
participant GitLab
|
|
133
|
+
|
|
134
|
+
User->>Plugin: Initialize Plugin
|
|
135
|
+
Plugin->>AuthManager: getGitLabClient()
|
|
136
|
+
AuthManager->>AuthManager: Check GITLAB_TOKEN env
|
|
137
|
+
alt Token in Environment
|
|
138
|
+
AuthManager->>GitLab: Use env token
|
|
139
|
+
else No env token
|
|
140
|
+
AuthManager->>Storage: Read ~/.local/share/opencode/auth.json
|
|
141
|
+
Storage->>AuthManager: Return token
|
|
142
|
+
AuthManager->>GitLab: Use stored token
|
|
143
|
+
end
|
|
144
|
+
GitLab->>Plugin: API Response
|
|
145
|
+
Plugin->>User: Tool Result
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 📦 Installation
|
|
149
|
+
|
|
150
|
+
### Prerequisites
|
|
151
|
+
|
|
152
|
+
- Node.js >= 18.0.0
|
|
153
|
+
- npm >= 9.0.0 or Bun
|
|
154
|
+
- GitLab account with API access
|
|
155
|
+
- GitLab Personal Access Token or OAuth token
|
|
156
|
+
|
|
157
|
+
### Install from GitLab Package Registry
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Configure npm to use GitLab Package Registry
|
|
161
|
+
npm config set @gitlab-org:registry https://gitlab.com/api/v4/projects/76386853/packages/npm/
|
|
162
|
+
|
|
163
|
+
# Install the package
|
|
164
|
+
npm install @vglafirov/opencode-gitlab-plugin
|
|
165
|
+
|
|
166
|
+
# Or with Bun
|
|
167
|
+
bun add @vglafirov/opencode-gitlab-plugin
|
|
168
|
+
|
|
169
|
+
# Or with yarn
|
|
170
|
+
yarn add @vglafirov/opencode-gitlab-plugin
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Install from GitLab Repository (Development)
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Using npm
|
|
177
|
+
npm install git+https://gitlab.com/vglafirov/opencode-gitlab-plugin.git
|
|
178
|
+
|
|
179
|
+
# Using Bun
|
|
180
|
+
bun add git+https://gitlab.com/vglafirov/opencode-gitlab-plugin.git
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Install Specific Version
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Install specific version from registry
|
|
187
|
+
npm install @vglafirov/opencode-gitlab-plugin@1.0.0
|
|
188
|
+
|
|
189
|
+
# Install from specific git tag
|
|
190
|
+
npm install git+https://gitlab.com/vglafirov/opencode-gitlab-plugin.git#v1.0.0
|
|
191
|
+
|
|
192
|
+
# Install from specific branch
|
|
193
|
+
npm install git+https://gitlab.com/vglafirov/opencode-gitlab-plugin.git#main
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Using package.json
|
|
197
|
+
|
|
198
|
+
Add to your `package.json`:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"dependencies": {
|
|
203
|
+
"@vglafirov/opencode-gitlab-plugin": "^1.0.0"
|
|
204
|
+
},
|
|
205
|
+
"publishConfig": {
|
|
206
|
+
"@gitlab-org:registry": "https://gitlab.com/api/v4/projects/76386853/packages/npm/"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Then run:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm install
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## ⚙️ Configuration
|
|
218
|
+
|
|
219
|
+
### Environment Variables
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Required: GitLab API Token
|
|
223
|
+
export GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
|
|
224
|
+
|
|
225
|
+
# Optional: Custom GitLab Instance (defaults to https://gitlab.com)
|
|
226
|
+
export GITLAB_INSTANCE_URL=https://gitlab.example.com
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### OpenCode Configuration
|
|
230
|
+
|
|
231
|
+
Add the following plugin to your opencode configuration `~/.config/opencode/opencode.json`:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"$schema": "https://opencode.ai/config.json",
|
|
236
|
+
"plugin": ["@vglafirov/opencode-gitlab-plugin"]
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Authentication Storage
|
|
241
|
+
|
|
242
|
+
The plugin supports reading tokens from OpenCode's auth storage:
|
|
243
|
+
|
|
244
|
+
**Location**: `~/.local/share/opencode/auth.json`
|
|
245
|
+
|
|
246
|
+
**Format**:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"gitlab": {
|
|
251
|
+
"type": "oauth",
|
|
252
|
+
"access": "your-oauth-token"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Or for API tokens:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"gitlab": {
|
|
262
|
+
"type": "api",
|
|
263
|
+
"key": "glpat-xxxxxxxxxxxxxxxxxxxx"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Token Priority
|
|
269
|
+
|
|
270
|
+
1. `GITLAB_TOKEN` environment variable (highest priority)
|
|
271
|
+
2. OpenCode auth storage (`~/.local/share/opencode/auth.json`)
|
|
272
|
+
3. Error if no token found
|
|
273
|
+
|
|
274
|
+
## 🛠️ Available Tools
|
|
275
|
+
|
|
276
|
+
The plugin provides **68+ tools** organized into the following categories:
|
|
277
|
+
|
|
278
|
+
### Merge Request Tools (13 tools)
|
|
279
|
+
|
|
280
|
+
```mermaid
|
|
281
|
+
graph LR
|
|
282
|
+
MR[Merge Requests] --> Get[gitlab_get_merge_request]
|
|
283
|
+
MR --> List[gitlab_list_merge_requests]
|
|
284
|
+
MR --> Create[gitlab_create_merge_request]
|
|
285
|
+
MR --> Update[gitlab_update_merge_request]
|
|
286
|
+
MR --> Changes[gitlab_get_mr_changes]
|
|
287
|
+
MR --> Discussions[gitlab_list_mr_discussions]
|
|
288
|
+
MR --> Notes[gitlab_list_mr_notes]
|
|
289
|
+
MR --> CreateNote[gitlab_create_mr_note]
|
|
290
|
+
MR --> Commits[gitlab_get_mr_commits]
|
|
291
|
+
MR --> Pipelines[gitlab_get_mr_pipelines]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
| Tool | Description |
|
|
295
|
+
| ----------------------------- | ------------------------------------------ |
|
|
296
|
+
| `gitlab_get_merge_request` | Get details of a specific merge request |
|
|
297
|
+
| `gitlab_list_merge_requests` | List merge requests with filtering |
|
|
298
|
+
| `gitlab_create_merge_request` | Create a new merge request |
|
|
299
|
+
| `gitlab_update_merge_request` | Update an existing merge request |
|
|
300
|
+
| `gitlab_get_mr_changes` | Get file changes/diff for a merge request |
|
|
301
|
+
| `gitlab_list_mr_discussions` | List discussion threads on a merge request |
|
|
302
|
+
| `gitlab_list_mr_notes` | List all comments in flat structure |
|
|
303
|
+
| `gitlab_create_mr_note` | Add a comment to a merge request |
|
|
304
|
+
| `gitlab_get_mr_commits` | Get commits in a merge request |
|
|
305
|
+
| `gitlab_get_mr_pipelines` | Get pipelines for a merge request |
|
|
306
|
+
|
|
307
|
+
### Issue Tools (5 tools)
|
|
308
|
+
|
|
309
|
+
| Tool | Description |
|
|
310
|
+
| -------------------------- | ------------------------------- |
|
|
311
|
+
| `gitlab_create_issue` | Create a new issue in a project |
|
|
312
|
+
| `gitlab_get_issue` | Get details of a specific issue |
|
|
313
|
+
| `gitlab_list_issues` | List issues with filtering |
|
|
314
|
+
| `gitlab_list_issue_notes` | List all comments on an issue |
|
|
315
|
+
| `gitlab_create_issue_note` | Add a comment to an issue |
|
|
316
|
+
|
|
317
|
+
### Work Item Tools (7 tools)
|
|
318
|
+
|
|
319
|
+
| Tool | Description |
|
|
320
|
+
| ------------------------------ | ------------------------------------------ |
|
|
321
|
+
| `gitlab_get_work_item` | Get a single work item (issue, epic, task) |
|
|
322
|
+
| `gitlab_list_work_items` | List work items in a project or group |
|
|
323
|
+
| `gitlab_get_work_item_notes` | Get all comments for a work item |
|
|
324
|
+
| `gitlab_create_work_item` | Create a new work item |
|
|
325
|
+
| `gitlab_update_work_item` | Update an existing work item |
|
|
326
|
+
| `gitlab_create_work_item_note` | Add a comment to a work item |
|
|
327
|
+
|
|
328
|
+
### Pipeline Tools (6 tools)
|
|
329
|
+
|
|
330
|
+
| Tool | Description |
|
|
331
|
+
| ---------------------------------- | ---------------------------------- |
|
|
332
|
+
| `gitlab_list_pipelines` | List pipelines for a project |
|
|
333
|
+
| `gitlab_get_pipeline` | Get details of a specific pipeline |
|
|
334
|
+
| `gitlab_list_pipeline_jobs` | List jobs for a pipeline |
|
|
335
|
+
| `gitlab_get_job_log` | Get log output of a CI job |
|
|
336
|
+
| `gitlab_retry_job` | Retry a failed or canceled job |
|
|
337
|
+
| `gitlab_get_pipeline_failing_jobs` | Get all failed jobs in a pipeline |
|
|
338
|
+
|
|
339
|
+
### Repository Tools (9 tools)
|
|
340
|
+
|
|
341
|
+
| Tool | Description |
|
|
342
|
+
| ----------------------------- | ------------------------------------------ |
|
|
343
|
+
| `gitlab_get_file` | Get contents of a file from repository |
|
|
344
|
+
| `gitlab_get_commit` | Get a single commit with full details |
|
|
345
|
+
| `gitlab_list_commits` | List commits with filtering |
|
|
346
|
+
| `gitlab_get_commit_diff` | Get diff for a specific commit |
|
|
347
|
+
| `gitlab_create_commit` | Create a commit with multiple file actions |
|
|
348
|
+
| `gitlab_list_repository_tree` | List files and directories |
|
|
349
|
+
| `gitlab_list_branches` | List branches in a repository |
|
|
350
|
+
|
|
351
|
+
### Search Tools (4 tools)
|
|
352
|
+
|
|
353
|
+
| Tool | Description |
|
|
354
|
+
| ----------------------------- | -------------------------------------------------------- |
|
|
355
|
+
| `gitlab_search` | Search across GitLab (projects, issues, MRs, code, etc.) |
|
|
356
|
+
| `gitlab_issue_search` | Specialized issue search |
|
|
357
|
+
| `gitlab_blob_search` | Search file content in repositories |
|
|
358
|
+
| `gitlab_merge_request_search` | Specialized merge request search |
|
|
359
|
+
|
|
360
|
+
### Epic Tools (9 tools)
|
|
361
|
+
|
|
362
|
+
| Tool | Description |
|
|
363
|
+
| ------------------------------- | -------------------------------------- |
|
|
364
|
+
| `gitlab_get_epic` | Get details of a specific epic |
|
|
365
|
+
| `gitlab_list_epics` | List epics for a group |
|
|
366
|
+
| `gitlab_create_epic` | Create a new epic |
|
|
367
|
+
| `gitlab_update_epic` | Update an existing epic |
|
|
368
|
+
| `gitlab_list_epic_issues` | Get all issues associated with an epic |
|
|
369
|
+
| `gitlab_add_issue_to_epic` | Link an issue to an epic |
|
|
370
|
+
| `gitlab_remove_issue_from_epic` | Unlink an issue from an epic |
|
|
371
|
+
| `gitlab_list_epic_notes` | List all comments on an epic |
|
|
372
|
+
| `gitlab_create_epic_note` | Add a comment to an epic |
|
|
373
|
+
|
|
374
|
+
### TODO Tools (4 tools)
|
|
375
|
+
|
|
376
|
+
| Tool | Description |
|
|
377
|
+
| ---------------------------- | -------------------------------- |
|
|
378
|
+
| `gitlab_list_todos` | List TODO items for current user |
|
|
379
|
+
| `gitlab_mark_todo_done` | Mark a specific TODO as done |
|
|
380
|
+
| `gitlab_mark_all_todos_done` | Mark all TODOs as done |
|
|
381
|
+
| `gitlab_get_todo_count` | Get count of pending TODOs |
|
|
382
|
+
|
|
383
|
+
### Project & User Tools (3 tools)
|
|
384
|
+
|
|
385
|
+
| Tool | Description |
|
|
386
|
+
| ----------------------------- | --------------------------------- |
|
|
387
|
+
| `gitlab_get_project` | Get details of a specific project |
|
|
388
|
+
| `gitlab_list_project_members` | List members of a project |
|
|
389
|
+
| `gitlab_get_current_user` | Get current user information |
|
|
390
|
+
|
|
391
|
+
### Security Tools (8 tools)
|
|
392
|
+
|
|
393
|
+
| Tool | Description |
|
|
394
|
+
| ----------------------------------------- | ------------------------------------------------ |
|
|
395
|
+
| `gitlab_list_vulnerabilities` | List security vulnerabilities |
|
|
396
|
+
| `gitlab_get_vulnerability_details` | Get details for a specific vulnerability |
|
|
397
|
+
| `gitlab_create_vulnerability_issue` | Create issue linked to vulnerabilities (GraphQL) |
|
|
398
|
+
| `gitlab_dismiss_vulnerability` | Dismiss vulnerability with reason (GraphQL) |
|
|
399
|
+
| `gitlab_confirm_vulnerability` | Confirm a security vulnerability (GraphQL) |
|
|
400
|
+
| `gitlab_revert_vulnerability_to_detected` | Revert vulnerability to detected state (GraphQL) |
|
|
401
|
+
| `gitlab_update_vulnerability_severity` | Update vulnerability severity level (GraphQL) |
|
|
402
|
+
| `gitlab_link_vulnerability_to_issue` | Link existing issue to vulnerabilities (GraphQL) |
|
|
403
|
+
|
|
404
|
+
**Note**: All GraphQL-based security tools include automatic GID (Global ID) format validation to ensure correct parameter formats before making API calls.
|
|
405
|
+
|
|
406
|
+
### Wiki Tools (1 tool)
|
|
407
|
+
|
|
408
|
+
| Tool | Description |
|
|
409
|
+
| ---------------------- | -------------------------------- |
|
|
410
|
+
| `gitlab_get_wiki_page` | Get a wiki page with its content |
|
|
411
|
+
|
|
412
|
+
## 💡 Usage Examples
|
|
413
|
+
|
|
414
|
+
### Example 1: Create and Manage Issues
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
import gitlabPlugin from '@vglafirov/opencode-gitlab-plugin';
|
|
418
|
+
|
|
419
|
+
const plugin = await gitlabPlugin({});
|
|
420
|
+
|
|
421
|
+
// Create a new issue
|
|
422
|
+
const issue = await plugin.tool.gitlab_create_issue.execute({
|
|
423
|
+
project_id: 'my-group/my-project',
|
|
424
|
+
title: 'Fix authentication bug',
|
|
425
|
+
description:
|
|
426
|
+
'## Problem\n\nUsers cannot login with OAuth.\n\n## Steps to Reproduce\n1. Go to login page\n2. Click OAuth button\n3. Error occurs',
|
|
427
|
+
labels: 'bug,authentication,priority::high',
|
|
428
|
+
assignee_ids: [42],
|
|
429
|
+
milestone_id: 10,
|
|
430
|
+
due_date: '2025-12-31',
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
console.log(`Issue created: ${issue.web_url}`);
|
|
434
|
+
|
|
435
|
+
// Add a comment to the issue
|
|
436
|
+
await plugin.tool.gitlab_create_issue_note.execute({
|
|
437
|
+
project_id: 'my-group/my-project',
|
|
438
|
+
issue_iid: issue.iid,
|
|
439
|
+
body: 'I will start working on this today.',
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// List all issues with specific labels
|
|
443
|
+
const issues = await plugin.tool.gitlab_list_issues.execute({
|
|
444
|
+
project_id: 'my-group/my-project',
|
|
445
|
+
labels: 'bug',
|
|
446
|
+
state: 'opened',
|
|
447
|
+
});
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Example 2: Review Merge Request
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
// Get merge request details
|
|
454
|
+
const mr = await plugin.tool.gitlab_get_merge_request.execute({
|
|
455
|
+
project_id: 'gitlab-org/gitlab',
|
|
456
|
+
mr_iid: 12345,
|
|
457
|
+
include_changes: true,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Get discussions
|
|
461
|
+
const discussions = await plugin.tool.gitlab_list_mr_discussions.execute({
|
|
462
|
+
project_id: 'gitlab-org/gitlab',
|
|
463
|
+
mr_iid: 12345,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Add a review comment
|
|
467
|
+
await plugin.tool.gitlab_create_mr_note.execute({
|
|
468
|
+
project_id: 'gitlab-org/gitlab',
|
|
469
|
+
mr_iid: 12345,
|
|
470
|
+
body: 'LGTM! Great work on this feature.',
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Example 3: Debug Failed Pipeline
|
|
475
|
+
|
|
476
|
+
```javascript
|
|
477
|
+
// List recent pipelines
|
|
478
|
+
const pipelines = await plugin.tool.gitlab_list_pipelines.execute({
|
|
479
|
+
project_id: 'my-group/my-project',
|
|
480
|
+
status: 'failed',
|
|
481
|
+
limit: 5,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Get failed jobs
|
|
485
|
+
const failedJobs = await plugin.tool.gitlab_get_pipeline_failing_jobs.execute({
|
|
486
|
+
project_id: 'my-group/my-project',
|
|
487
|
+
pipeline_id: pipelines[0].id,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// Get job logs
|
|
491
|
+
for (const job of failedJobs) {
|
|
492
|
+
const log = await plugin.tool.gitlab_get_job_log.execute({
|
|
493
|
+
project_id: 'my-group/my-project',
|
|
494
|
+
job_id: job.id,
|
|
495
|
+
});
|
|
496
|
+
console.log(`Job ${job.name} failed with:\n${log}`);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Retry failed jobs
|
|
500
|
+
await plugin.tool.gitlab_retry_job.execute({
|
|
501
|
+
project_id: 'my-group/my-project',
|
|
502
|
+
job_id: failedJobs[0].id,
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Example 4: Create and Manage Epic
|
|
507
|
+
|
|
508
|
+
```javascript
|
|
509
|
+
// Create an epic
|
|
510
|
+
const epic = await plugin.tool.gitlab_create_epic.execute({
|
|
511
|
+
group_id: 'my-group',
|
|
512
|
+
title: 'Q1 2025 Features',
|
|
513
|
+
description: 'All features planned for Q1 2025',
|
|
514
|
+
start_date: '2025-01-01',
|
|
515
|
+
end_date: '2025-03-31',
|
|
516
|
+
labels: 'Q1,planning',
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Add issues to epic
|
|
520
|
+
await plugin.tool.gitlab_add_issue_to_epic.execute({
|
|
521
|
+
group_id: 'my-group',
|
|
522
|
+
epic_iid: epic.iid,
|
|
523
|
+
issue_id: 123,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// List all issues in epic
|
|
527
|
+
const epicIssues = await plugin.tool.gitlab_list_epic_issues.execute({
|
|
528
|
+
group_id: 'my-group',
|
|
529
|
+
epic_iid: epic.iid,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Add a comment
|
|
533
|
+
await plugin.tool.gitlab_create_epic_note.execute({
|
|
534
|
+
group_id: 'my-group',
|
|
535
|
+
epic_iid: epic.iid,
|
|
536
|
+
body: 'Epic created and issues linked successfully!',
|
|
537
|
+
});
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Example 5: Search and Analyze Code
|
|
541
|
+
|
|
542
|
+
```javascript
|
|
543
|
+
// Search for code containing specific patterns
|
|
544
|
+
const codeResults = await plugin.tool.gitlab_blob_search.execute({
|
|
545
|
+
search: 'async function processPayment',
|
|
546
|
+
project_id: 'my-group/my-project',
|
|
547
|
+
limit: 10,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Search for related issues
|
|
551
|
+
const issues = await plugin.tool.gitlab_issue_search.execute({
|
|
552
|
+
search: 'payment processing bug',
|
|
553
|
+
project_id: 'my-group/my-project',
|
|
554
|
+
state: 'opened',
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Get file content
|
|
558
|
+
const fileContent = await plugin.tool.gitlab_get_file.execute({
|
|
559
|
+
project_id: 'my-group/my-project',
|
|
560
|
+
file_path: 'src/payment/processor.ts',
|
|
561
|
+
ref: 'main',
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Example 6: Manage TODOs
|
|
566
|
+
|
|
567
|
+
```javascript
|
|
568
|
+
// Get TODO count
|
|
569
|
+
const todoCount = await plugin.tool.gitlab_get_todo_count.execute({});
|
|
570
|
+
|
|
571
|
+
// List pending TODOs
|
|
572
|
+
const todos = await plugin.tool.gitlab_list_todos.execute({
|
|
573
|
+
state: 'pending',
|
|
574
|
+
type: 'MergeRequest',
|
|
575
|
+
limit: 20,
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
// Mark specific TODO as done
|
|
579
|
+
await plugin.tool.gitlab_mark_todo_done.execute({
|
|
580
|
+
todo_id: todos[0].id,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// Mark all TODOs as done
|
|
584
|
+
await plugin.tool.gitlab_mark_all_todos_done.execute({});
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Example 7: Create Commit with Multiple Files
|
|
588
|
+
|
|
589
|
+
```javascript
|
|
590
|
+
// Create a commit with multiple file operations
|
|
591
|
+
const commit = await plugin.tool.gitlab_create_commit.execute({
|
|
592
|
+
project_id: 'my-group/my-project',
|
|
593
|
+
branch: 'feature/new-api',
|
|
594
|
+
commit_message: 'feat: add new API endpoints',
|
|
595
|
+
actions: [
|
|
596
|
+
{
|
|
597
|
+
action: 'create',
|
|
598
|
+
file_path: 'src/api/v2/users.ts',
|
|
599
|
+
content: 'export const getUsers = async () => { ... }',
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
action: 'update',
|
|
603
|
+
file_path: 'src/api/index.ts',
|
|
604
|
+
content: 'export * from "./v2/users";',
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
action: 'delete',
|
|
608
|
+
file_path: 'src/api/deprecated.ts',
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
author_name: 'John Doe',
|
|
612
|
+
author_email: 'john@example.com',
|
|
613
|
+
});
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Example 8: Manage Security Vulnerabilities
|
|
617
|
+
|
|
618
|
+
```javascript
|
|
619
|
+
// List vulnerabilities in a project
|
|
620
|
+
const vulnerabilities = await plugin.tool.gitlab_list_vulnerabilities.execute({
|
|
621
|
+
project_id: 'my-group/my-project',
|
|
622
|
+
state: 'detected',
|
|
623
|
+
severity: 'high',
|
|
624
|
+
report_type: 'sast',
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// Create an issue for critical vulnerabilities
|
|
628
|
+
const issue = await plugin.tool.gitlab_create_vulnerability_issue.execute({
|
|
629
|
+
project_path: 'my-group/my-project',
|
|
630
|
+
vulnerability_ids: ['gid://gitlab/Vulnerability/123', 'gid://gitlab/Vulnerability/124'],
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Dismiss a false positive
|
|
634
|
+
await plugin.tool.gitlab_dismiss_vulnerability.execute({
|
|
635
|
+
vulnerability_id: 'gid://gitlab/Vulnerability/125',
|
|
636
|
+
reason: 'FALSE_POSITIVE',
|
|
637
|
+
comment: 'This is a test file and not part of production code',
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
// Confirm a real vulnerability
|
|
641
|
+
await plugin.tool.gitlab_confirm_vulnerability.execute({
|
|
642
|
+
vulnerability_id: 'gid://gitlab/Vulnerability/126',
|
|
643
|
+
comment: 'Confirmed - needs immediate attention',
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// Update severity based on assessment
|
|
647
|
+
await plugin.tool.gitlab_update_vulnerability_severity.execute({
|
|
648
|
+
vulnerability_ids: ['gid://gitlab/Vulnerability/127'],
|
|
649
|
+
severity: 'CRITICAL',
|
|
650
|
+
comment: 'Upgrading to critical - affects production authentication',
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// Link vulnerabilities to existing issue
|
|
654
|
+
await plugin.tool.gitlab_link_vulnerability_to_issue.execute({
|
|
655
|
+
issue_id: 'gid://gitlab/Issue/42',
|
|
656
|
+
vulnerability_ids: ['gid://gitlab/Vulnerability/128', 'gid://gitlab/Vulnerability/129'],
|
|
657
|
+
});
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
## 🔧 Development
|
|
661
|
+
|
|
662
|
+
### Project Structure
|
|
663
|
+
|
|
664
|
+
```
|
|
665
|
+
opencode-gitlab-plugin/
|
|
666
|
+
├── src/
|
|
667
|
+
│ ├── client/ # API client modules
|
|
668
|
+
│ │ ├── base.ts # Base client with HTTP & GraphQL methods
|
|
669
|
+
│ │ ├── security.ts # Security/vulnerability management
|
|
670
|
+
│ │ ├── issues.ts # Issue management
|
|
671
|
+
│ │ ├── merge-requests.ts # Merge request operations
|
|
672
|
+
│ │ ├── pipelines.ts # CI/CD pipeline operations
|
|
673
|
+
│ │ ├── repository.ts # Repository operations
|
|
674
|
+
│ │ ├── epics.ts # Epic management
|
|
675
|
+
│ │ ├── search.ts # Search operations
|
|
676
|
+
│ │ ├── todos.ts # TODO management
|
|
677
|
+
│ │ ├── wikis.ts # Wiki operations
|
|
678
|
+
│ │ ├── work-items.ts # Work item operations
|
|
679
|
+
│ │ ├── audit.ts # Audit events
|
|
680
|
+
│ │ ├── git.ts # Git operations
|
|
681
|
+
│ │ └── index.ts # Client exports
|
|
682
|
+
│ ├── tools/ # Tool definitions
|
|
683
|
+
│ │ ├── security.ts # Security tool definitions
|
|
684
|
+
│ │ ├── issues.ts # Issue tool definitions
|
|
685
|
+
│ │ ├── merge-requests.ts # MR tool definitions
|
|
686
|
+
│ │ ├── pipelines.ts # Pipeline tool definitions
|
|
687
|
+
│ │ ├── repository.ts # Repository tool definitions
|
|
688
|
+
│ │ └── ... # Other tool definitions
|
|
689
|
+
│ ├── index.ts # Main plugin entry point
|
|
690
|
+
│ ├── utils.ts # Utility functions
|
|
691
|
+
│ └── validation.ts # GID validation utilities
|
|
692
|
+
├── tests/ # Test suite (142 tests)
|
|
693
|
+
│ ├── client/ # Client tests
|
|
694
|
+
│ ├── tools/ # Tool tests
|
|
695
|
+
│ ├── validation.test.ts # Validation tests
|
|
696
|
+
│ └── utils.test.ts # Utility tests
|
|
697
|
+
├── dist/ # Compiled output (generated)
|
|
698
|
+
│ ├── index.js # ESM bundle
|
|
699
|
+
│ └── index.d.ts # TypeScript definitions
|
|
700
|
+
├── .husky/ # Git hooks
|
|
701
|
+
│ ├── commit-msg # Commitlint hook
|
|
702
|
+
│ └── pre-commit # Lint-staged hook
|
|
703
|
+
├── .gitlab-ci.yml # CI/CD pipeline configuration
|
|
704
|
+
├── package.json # Package metadata
|
|
705
|
+
├── tsconfig.json # TypeScript configuration
|
|
706
|
+
├── vitest.config.ts # Vitest test configuration
|
|
707
|
+
├── .eslintrc.json # ESLint configuration
|
|
708
|
+
├── .prettierrc.json # Prettier configuration
|
|
709
|
+
├── .commitlintrc.json # Commitlint configuration
|
|
710
|
+
├── .releaserc.json # Semantic-release configuration
|
|
711
|
+
├── CHANGELOG.md # Auto-generated changelog
|
|
712
|
+
├── INSTALLATION.md # Installation guide
|
|
713
|
+
└── README.md # This file
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Setup Development Environment
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
# Clone the repository
|
|
720
|
+
git clone https://gitlab.com/vglafirov/opencode-gitlab-plugin.git
|
|
721
|
+
cd opencode-gitlab-plugin
|
|
722
|
+
|
|
723
|
+
# Install dependencies
|
|
724
|
+
npm install
|
|
725
|
+
|
|
726
|
+
# Build the plugin
|
|
727
|
+
npm run build
|
|
728
|
+
|
|
729
|
+
# Watch mode for development
|
|
730
|
+
npm run dev
|
|
731
|
+
|
|
732
|
+
# Run linting
|
|
733
|
+
npm run lint
|
|
734
|
+
|
|
735
|
+
# Fix linting issues
|
|
736
|
+
npm run lint:fix
|
|
737
|
+
|
|
738
|
+
# Format code
|
|
739
|
+
npm run format
|
|
740
|
+
|
|
741
|
+
# Check formatting
|
|
742
|
+
npm run format:check
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Git Hooks
|
|
746
|
+
|
|
747
|
+
The project uses Husky for Git hooks:
|
|
748
|
+
|
|
749
|
+
- **pre-commit**: Runs lint-staged to lint and format staged files
|
|
750
|
+
- **commit-msg**: Validates commit messages using commitlint
|
|
751
|
+
|
|
752
|
+
### Commit Message Convention
|
|
753
|
+
|
|
754
|
+
This project follows [Conventional Commits](https://www.conventionalcommits.org/):
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
<type>(<scope>): <subject>
|
|
758
|
+
|
|
759
|
+
<body>
|
|
760
|
+
|
|
761
|
+
<footer>
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
**Types:**
|
|
765
|
+
|
|
766
|
+
- `feat`: New feature
|
|
767
|
+
- `fix`: Bug fix
|
|
768
|
+
- `docs`: Documentation changes
|
|
769
|
+
- `style`: Code style changes (formatting, etc.)
|
|
770
|
+
- `refactor`: Code refactoring
|
|
771
|
+
- `perf`: Performance improvements
|
|
772
|
+
- `test`: Adding or updating tests
|
|
773
|
+
- `build`: Build system changes
|
|
774
|
+
- `ci`: CI/CD changes
|
|
775
|
+
- `chore`: Other changes (dependencies, etc.)
|
|
776
|
+
- `revert`: Revert a previous commit
|
|
777
|
+
|
|
778
|
+
**Examples:**
|
|
779
|
+
|
|
780
|
+
```bash
|
|
781
|
+
git commit -m "feat: add support for GitLab wiki pages"
|
|
782
|
+
git commit -m "fix: handle empty API responses correctly"
|
|
783
|
+
git commit -m "docs: update installation instructions"
|
|
784
|
+
git commit -m "ci: add automated release workflow"
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Building
|
|
788
|
+
|
|
789
|
+
```bash
|
|
790
|
+
# Build for production
|
|
791
|
+
npm run build
|
|
792
|
+
|
|
793
|
+
# The build process:
|
|
794
|
+
# 1. Compiles TypeScript to ESM
|
|
795
|
+
# 2. Generates type definitions
|
|
796
|
+
# 3. Outputs to dist/ directory
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### Testing
|
|
800
|
+
|
|
801
|
+
```bash
|
|
802
|
+
# Run tests
|
|
803
|
+
npm test
|
|
804
|
+
|
|
805
|
+
# Run tests in watch mode
|
|
806
|
+
npm run test:watch
|
|
807
|
+
|
|
808
|
+
# Run tests with coverage
|
|
809
|
+
npm run test:coverage
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
**Test Coverage:**
|
|
813
|
+
|
|
814
|
+
- **142 tests** across 18 test files
|
|
815
|
+
- Client tests for all API methods
|
|
816
|
+
- Tool tests for all tool definitions
|
|
817
|
+
- Validation tests for GID utilities
|
|
818
|
+
- GraphQL method tests
|
|
819
|
+
- All tests passing ✅
|
|
820
|
+
|
|
821
|
+
## 🚀 CI/CD Pipeline
|
|
822
|
+
|
|
823
|
+
### Pipeline Stages
|
|
824
|
+
|
|
825
|
+
```mermaid
|
|
826
|
+
graph LR
|
|
827
|
+
A[Test] --> B[Build]
|
|
828
|
+
B --> C[Release]
|
|
829
|
+
|
|
830
|
+
A --> A1[Lint]
|
|
831
|
+
A --> A2[Format Check]
|
|
832
|
+
A --> A3[Unit Tests]
|
|
833
|
+
|
|
834
|
+
B --> B1[TypeScript Build]
|
|
835
|
+
B --> B2[Generate Types]
|
|
836
|
+
|
|
837
|
+
C --> C1[Semantic Release]
|
|
838
|
+
C --> C2[Publish to Registry]
|
|
839
|
+
C --> C3[Create Git Tag]
|
|
840
|
+
C --> C4[Update Changelog]
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Pipeline Configuration
|
|
844
|
+
|
|
845
|
+
The `.gitlab-ci.yml` defines the following stages:
|
|
846
|
+
|
|
847
|
+
#### 1. Test Stage
|
|
848
|
+
|
|
849
|
+
- **test:lint**: Runs ESLint on source code
|
|
850
|
+
- **test:format**: Checks code formatting with Prettier
|
|
851
|
+
- **test:unit**: Runs unit tests (placeholder)
|
|
852
|
+
|
|
853
|
+
#### 2. Build Stage
|
|
854
|
+
|
|
855
|
+
- **build**: Compiles TypeScript and generates artifacts
|
|
856
|
+
- Uses tsup for bundling
|
|
857
|
+
- Generates ESM output
|
|
858
|
+
- Creates TypeScript definitions
|
|
859
|
+
- Artifacts expire in 1 week
|
|
860
|
+
|
|
861
|
+
#### 3. Release Stage
|
|
862
|
+
|
|
863
|
+
- **release**: Automated versioning and publishing
|
|
864
|
+
- Only runs on main branch
|
|
865
|
+
- Uses semantic-release
|
|
866
|
+
- Publishes to GitLab Package Registry
|
|
867
|
+
- Creates Git tags
|
|
868
|
+
- Updates CHANGELOG.md
|
|
869
|
+
- Artifacts never expire
|
|
870
|
+
|
|
871
|
+
### Semantic Release Workflow
|
|
872
|
+
|
|
873
|
+
```mermaid
|
|
874
|
+
sequenceDiagram
|
|
875
|
+
participant Dev as Developer
|
|
876
|
+
participant Git as Git Repository
|
|
877
|
+
participant CI as GitLab CI
|
|
878
|
+
participant SR as Semantic Release
|
|
879
|
+
participant Reg as Package Registry
|
|
880
|
+
|
|
881
|
+
Dev->>Git: Push to main branch
|
|
882
|
+
Git->>CI: Trigger pipeline
|
|
883
|
+
CI->>CI: Run tests
|
|
884
|
+
CI->>CI: Build package
|
|
885
|
+
CI->>SR: Run semantic-release
|
|
886
|
+
SR->>SR: Analyze commits
|
|
887
|
+
SR->>SR: Determine version
|
|
888
|
+
SR->>SR: Generate changelog
|
|
889
|
+
SR->>Git: Create tag & commit
|
|
890
|
+
SR->>Reg: Publish package
|
|
891
|
+
SR->>Git: Create GitLab release
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### Release Process
|
|
895
|
+
|
|
896
|
+
The release process is fully automated using semantic-release:
|
|
897
|
+
|
|
898
|
+
1. **Commit Analysis**: Analyzes commit messages since last release
|
|
899
|
+
2. **Version Calculation**: Determines next version based on commit types
|
|
900
|
+
- `fix:` → Patch version (1.0.x)
|
|
901
|
+
- `feat:` → Minor version (1.x.0)
|
|
902
|
+
- `BREAKING CHANGE:` → Major version (x.0.0)
|
|
903
|
+
3. **Changelog Generation**: Updates CHANGELOG.md
|
|
904
|
+
4. **Package Publishing**: Publishes to GitLab Package Registry
|
|
905
|
+
5. **Git Tagging**: Creates and pushes version tag
|
|
906
|
+
6. **GitLab Release**: Creates release notes on GitLab
|
|
907
|
+
|
|
908
|
+
### Environment Variables
|
|
909
|
+
|
|
910
|
+
The CI/CD pipeline uses the following variables:
|
|
911
|
+
|
|
912
|
+
- `CI_JOB_TOKEN`: GitLab CI token (automatic)
|
|
913
|
+
- `CI_PROJECT_ID`: Project ID (automatic)
|
|
914
|
+
- `CI_API_V4_URL`: GitLab API URL (automatic)
|
|
915
|
+
- `HUSKY`: Set to `0` to disable hooks in CI
|
|
916
|
+
|
|
917
|
+
## 📚 API Reference
|
|
918
|
+
|
|
919
|
+
### GitLabApiClient Class
|
|
920
|
+
|
|
921
|
+
The core API client that handles all GitLab REST API interactions.
|
|
922
|
+
|
|
923
|
+
#### Constructor
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
constructor(instanceUrl: string, token: string)
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
#### HTTP Methods
|
|
930
|
+
|
|
931
|
+
```typescript
|
|
932
|
+
async fetch<T>(method: string, path: string, body?: unknown): Promise<T>
|
|
933
|
+
async fetchText(method: string, path: string): Promise<string>
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
#### GraphQL Methods
|
|
937
|
+
|
|
938
|
+
```typescript
|
|
939
|
+
/**
|
|
940
|
+
* Execute a GraphQL query or mutation
|
|
941
|
+
* @template T - The expected type of the data field in the GraphQL response
|
|
942
|
+
* @param query - The GraphQL query or mutation string
|
|
943
|
+
* @param variables - Optional variables for the query
|
|
944
|
+
* @returns The data from the GraphQL response
|
|
945
|
+
*/
|
|
946
|
+
async fetchGraphQL<T>(query: string, variables?: Record<string, unknown>): Promise<T>
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Features:**
|
|
950
|
+
|
|
951
|
+
- Full TypeScript type safety with generic return types
|
|
952
|
+
- Automatic error handling for both HTTP and GraphQL errors
|
|
953
|
+
- Support for query variables
|
|
954
|
+
- Used by all GraphQL-based security tools
|
|
955
|
+
|
|
956
|
+
**Example:**
|
|
957
|
+
|
|
958
|
+
```typescript
|
|
959
|
+
const result = await client.fetchGraphQL<{ vulnerability: { id: string } }>(
|
|
960
|
+
`mutation($id: VulnerabilityID!) {
|
|
961
|
+
vulnerabilityConfirm(input: { id: $id }) {
|
|
962
|
+
vulnerability { id state }
|
|
963
|
+
errors
|
|
964
|
+
}
|
|
965
|
+
}`,
|
|
966
|
+
{ id: 'gid://gitlab/Vulnerability/123' }
|
|
967
|
+
);
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
#### Project ID Encoding
|
|
971
|
+
|
|
972
|
+
```typescript
|
|
973
|
+
private encodeProjectId(projectId: string): string
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
Handles URL encoding for project paths (e.g., `gitlab-org/gitlab` → `gitlab-org%2Fgitlab`)
|
|
977
|
+
|
|
978
|
+
### Authentication Functions
|
|
979
|
+
|
|
980
|
+
#### readTokenFromAuthStorage
|
|
981
|
+
|
|
982
|
+
```typescript
|
|
983
|
+
function readTokenFromAuthStorage(): string | undefined;
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
Reads GitLab token from OpenCode auth storage (`~/.local/share/opencode/auth.json`).
|
|
987
|
+
|
|
988
|
+
Supports:
|
|
989
|
+
|
|
990
|
+
- OAuth tokens: `{ type: "oauth", access: "token" }`
|
|
991
|
+
- API tokens: `{ type: "api", key: "token" }`
|
|
992
|
+
|
|
993
|
+
#### getGitLabClient
|
|
994
|
+
|
|
995
|
+
```typescript
|
|
996
|
+
function getGitLabClient(): GitLabApiClient;
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
Creates and returns a configured GitLab API client.
|
|
1000
|
+
|
|
1001
|
+
Priority:
|
|
1002
|
+
|
|
1003
|
+
1. `GITLAB_TOKEN` environment variable
|
|
1004
|
+
2. OpenCode auth storage
|
|
1005
|
+
3. Throws error if no token found
|
|
1006
|
+
|
|
1007
|
+
### Validation Functions
|
|
1008
|
+
|
|
1009
|
+
#### isValidGid
|
|
1010
|
+
|
|
1011
|
+
```typescript
|
|
1012
|
+
function isValidGid(gid: string, expectedType?: string): boolean;
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
Validates GitLab Global ID (GID) format.
|
|
1016
|
+
|
|
1017
|
+
**Parameters:**
|
|
1018
|
+
|
|
1019
|
+
- `gid` - The GID to validate (e.g., `gid://gitlab/Vulnerability/123`)
|
|
1020
|
+
- `expectedType` - Optional expected resource type (e.g., `'Vulnerability'`, `'Issue'`)
|
|
1021
|
+
|
|
1022
|
+
**Returns:** `true` if valid, `false` otherwise
|
|
1023
|
+
|
|
1024
|
+
**Example:**
|
|
1025
|
+
|
|
1026
|
+
```typescript
|
|
1027
|
+
isValidGid('gid://gitlab/Vulnerability/123'); // true
|
|
1028
|
+
isValidGid('gid://gitlab/Issue/456', 'Issue'); // true
|
|
1029
|
+
isValidGid('gid://gitlab/Vulnerability/123', 'Issue'); // false (wrong type)
|
|
1030
|
+
isValidGid('invalid-gid'); // false
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
#### validateGid
|
|
1034
|
+
|
|
1035
|
+
```typescript
|
|
1036
|
+
function validateGid(gid: string, expectedType?: string): void;
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
Validates GID format and throws descriptive error if invalid.
|
|
1040
|
+
|
|
1041
|
+
**Parameters:**
|
|
1042
|
+
|
|
1043
|
+
- `gid` - The GID to validate
|
|
1044
|
+
- `expectedType` - Optional expected resource type
|
|
1045
|
+
|
|
1046
|
+
**Throws:** `Error` with descriptive message if GID format is invalid
|
|
1047
|
+
|
|
1048
|
+
**Example:**
|
|
1049
|
+
|
|
1050
|
+
```typescript
|
|
1051
|
+
validateGid('gid://gitlab/Vulnerability/123'); // No error
|
|
1052
|
+
validateGid('invalid-gid'); // Throws: "Invalid GitLab Global ID: 'invalid-gid'. Expected format: gid://gitlab/ResourceType/{id}"
|
|
1053
|
+
validateGid('gid://gitlab/Issue/123', 'Vulnerability'); // Throws: "Invalid GitLab Global ID of type 'Vulnerability'..."
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
**Usage in Security Tools:**
|
|
1057
|
+
|
|
1058
|
+
All GraphQL-based security tools automatically validate GID parameters:
|
|
1059
|
+
|
|
1060
|
+
- `createVulnerabilityIssue()` - validates vulnerability IDs
|
|
1061
|
+
- `dismissVulnerability()` - validates vulnerability ID
|
|
1062
|
+
- `confirmVulnerability()` - validates vulnerability ID
|
|
1063
|
+
- `revertVulnerability()` - validates vulnerability ID
|
|
1064
|
+
- `updateVulnerabilitySeverity()` - validates vulnerability IDs
|
|
1065
|
+
- `linkVulnerabilityToIssue()` - validates both issue ID and vulnerability IDs
|
|
1066
|
+
|
|
1067
|
+
### Tool Schema Validation
|
|
1068
|
+
|
|
1069
|
+
All tools use Zod for runtime validation:
|
|
1070
|
+
|
|
1071
|
+
```typescript
|
|
1072
|
+
import { tool } from '@opencode-ai/plugin';
|
|
1073
|
+
const z = tool.schema; // Zod v4 compatible
|
|
1074
|
+
|
|
1075
|
+
// Example tool definition
|
|
1076
|
+
gitlab_get_merge_request: tool({
|
|
1077
|
+
description: 'Get details of a specific merge request',
|
|
1078
|
+
args: {
|
|
1079
|
+
project_id: z.string().describe('The project ID or URL-encoded path'),
|
|
1080
|
+
mr_iid: z.number().describe('The internal ID of the merge request'),
|
|
1081
|
+
include_changes: z.boolean().optional().describe('Include file changes'),
|
|
1082
|
+
},
|
|
1083
|
+
execute: async (args, ctx) => {
|
|
1084
|
+
// Implementation
|
|
1085
|
+
},
|
|
1086
|
+
});
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
### Error Handling
|
|
1090
|
+
|
|
1091
|
+
All API calls include comprehensive error handling:
|
|
1092
|
+
|
|
1093
|
+
```typescript
|
|
1094
|
+
if (!response.ok) {
|
|
1095
|
+
const errorText = await response.text();
|
|
1096
|
+
throw new Error(`GitLab API error ${response.status}: ${errorText}`);
|
|
1097
|
+
}
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
### Response Formatting
|
|
1101
|
+
|
|
1102
|
+
All tool responses are JSON-formatted:
|
|
1103
|
+
|
|
1104
|
+
```typescript
|
|
1105
|
+
return JSON.stringify(result, null, 2);
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
## 🔐 Security Considerations
|
|
1109
|
+
|
|
1110
|
+
### Token Storage
|
|
1111
|
+
|
|
1112
|
+
- **Environment Variables**: Recommended for CI/CD and production
|
|
1113
|
+
- **Auth Storage**: Convenient for local development
|
|
1114
|
+
- **Never commit tokens**: Use `.gitignore` for sensitive files
|
|
1115
|
+
|
|
1116
|
+
### API Permissions
|
|
1117
|
+
|
|
1118
|
+
The plugin requires a GitLab token with appropriate scopes:
|
|
1119
|
+
|
|
1120
|
+
- `api`: Full API access (recommended)
|
|
1121
|
+
- `read_api`: Read-only access (limited functionality)
|
|
1122
|
+
- `read_repository`: Repository read access
|
|
1123
|
+
- `write_repository`: Repository write access (for commits)
|
|
1124
|
+
|
|
1125
|
+
### Rate Limiting
|
|
1126
|
+
|
|
1127
|
+
GitLab API has rate limits:
|
|
1128
|
+
|
|
1129
|
+
- **Authenticated requests**: 2,000 requests per minute
|
|
1130
|
+
- **Unauthenticated requests**: 10 requests per minute
|
|
1131
|
+
|
|
1132
|
+
The plugin does not implement rate limiting logic. Consider implementing retry logic in your application.
|
|
1133
|
+
|
|
1134
|
+
### HTTPS Only
|
|
1135
|
+
|
|
1136
|
+
The plugin enforces HTTPS for all API calls. HTTP URLs are not supported.
|
|
1137
|
+
|
|
1138
|
+
## 🤝 Contributing
|
|
1139
|
+
|
|
1140
|
+
Contributions are welcome! Please follow these guidelines:
|
|
1141
|
+
|
|
1142
|
+
### Contribution Workflow
|
|
1143
|
+
|
|
1144
|
+
```mermaid
|
|
1145
|
+
graph TD
|
|
1146
|
+
A[Fork Repository] --> B[Create Feature Branch]
|
|
1147
|
+
B --> C[Make Changes]
|
|
1148
|
+
C --> D[Write Tests]
|
|
1149
|
+
D --> E[Run Linting]
|
|
1150
|
+
E --> F[Commit with Convention]
|
|
1151
|
+
F --> G[Push to Fork]
|
|
1152
|
+
G --> H[Create Merge Request]
|
|
1153
|
+
H --> I[Code Review]
|
|
1154
|
+
I --> J{Approved?}
|
|
1155
|
+
J -->|Yes| K[Merge to Main]
|
|
1156
|
+
J -->|No| C
|
|
1157
|
+
K --> L[Automated Release]
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
### Steps to Contribute
|
|
1161
|
+
|
|
1162
|
+
1. **Fork the repository**
|
|
1163
|
+
|
|
1164
|
+
```bash
|
|
1165
|
+
# Fork on GitLab UI, then clone
|
|
1166
|
+
git clone https://gitlab.com/YOUR_USERNAME/opencode-gitlab-plugin.git
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
2. **Create a feature branch**
|
|
1170
|
+
|
|
1171
|
+
```bash
|
|
1172
|
+
git checkout -b feat/my-new-feature
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
3. **Make your changes**
|
|
1176
|
+
- Follow TypeScript best practices
|
|
1177
|
+
- Add JSDoc comments for public APIs
|
|
1178
|
+
- Update documentation if needed
|
|
1179
|
+
|
|
1180
|
+
4. **Run quality checks**
|
|
1181
|
+
|
|
1182
|
+
```bash
|
|
1183
|
+
npm run lint
|
|
1184
|
+
npm run format
|
|
1185
|
+
npm run build
|
|
1186
|
+
npm test
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
5. **Commit with conventional commits**
|
|
1190
|
+
|
|
1191
|
+
```bash
|
|
1192
|
+
git commit -m "feat: add new tool for project milestones"
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
6. **Push and create MR**
|
|
1196
|
+
|
|
1197
|
+
```bash
|
|
1198
|
+
git push origin feat/my-new-feature
|
|
1199
|
+
# Create merge request on GitLab
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
### Code Style
|
|
1203
|
+
|
|
1204
|
+
- **TypeScript**: Strict mode enabled
|
|
1205
|
+
- **ESLint**: Follow configured rules
|
|
1206
|
+
- **Prettier**: Auto-format on commit
|
|
1207
|
+
- **Line Length**: 100 characters max
|
|
1208
|
+
- **Indentation**: 2 spaces, no tabs
|
|
1209
|
+
|
|
1210
|
+
### Adding New Tools
|
|
1211
|
+
|
|
1212
|
+
To
|
|
1213
|
+
|
|
1214
|
+
---
|
|
1215
|
+
|
|
1216
|
+
## Assistant
|
|
1217
|
+
|
|
1218
|
+
add a new GitLab API tool:
|
|
1219
|
+
|
|
1220
|
+
1. **Add API method to GitLabApiClient**
|
|
1221
|
+
|
|
1222
|
+
```typescript
|
|
1223
|
+
async getProjectMilestones(projectId: string) {
|
|
1224
|
+
const encodedProject = this.encodeProjectId(projectId);
|
|
1225
|
+
return this.fetch<Record<string, unknown>[]>(
|
|
1226
|
+
'GET',
|
|
1227
|
+
`/projects/${encodedProject}/milestones`
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
```
|
|
1231
|
+
|
|
1232
|
+
2. **Add tool definition**
|
|
1233
|
+
|
|
1234
|
+
```typescript
|
|
1235
|
+
gitlab_list_milestones: tool({
|
|
1236
|
+
description: 'List milestones for a project',
|
|
1237
|
+
args: {
|
|
1238
|
+
project_id: z.string().describe('The project ID or path'),
|
|
1239
|
+
state: z.enum(['active', 'closed', 'all']).optional(),
|
|
1240
|
+
},
|
|
1241
|
+
execute: async (args, _ctx) => {
|
|
1242
|
+
const client = getGitLabClient();
|
|
1243
|
+
const milestones = await client.getProjectMilestones(args.project_id);
|
|
1244
|
+
return JSON.stringify(milestones, null, 2);
|
|
1245
|
+
},
|
|
1246
|
+
});
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
3. **Update documentation**
|
|
1250
|
+
- Add tool to README.md
|
|
1251
|
+
- Update tool count
|
|
1252
|
+
- Add usage example if applicable
|
|
1253
|
+
|
|
1254
|
+
## 🔗 Links
|
|
1255
|
+
|
|
1256
|
+
- **Repository**: <https://gitlab.com/vglafirov/opencode-gitlab-plugin>
|
|
1257
|
+
- **Package Registry**: <https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/packages>
|
|
1258
|
+
- **Issues**: <https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/issues>
|
|
1259
|
+
- **Merge Requests**: <https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/merge_requests>
|
|
1260
|
+
- **CI/CD Pipelines**: <https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/pipelines>
|
|
1261
|
+
- **GitLab API Documentation**: <https://docs.gitlab.com/ee/api/>
|
|
1262
|
+
- **OpenCode Plugin SDK**: <https://github.com/opencode-ai/plugin>
|
|
1263
|
+
|
|
1264
|
+
## 🙏 Acknowledgments
|
|
1265
|
+
|
|
1266
|
+
- **OpenCode Team**: For the plugin SDK and framework
|
|
1267
|
+
- **GitLab**: For the comprehensive REST API
|
|
1268
|
+
- **Contributors**: All contributors to this project
|
|
1269
|
+
|
|
1270
|
+
## 📞 Support
|
|
1271
|
+
|
|
1272
|
+
For questions, issues, or feature requests:
|
|
1273
|
+
|
|
1274
|
+
1. **Check existing issues**: <https://gitlab.com/vglafirov/opencode-gitlab-plugin/-/issues>
|
|
1275
|
+
2. **Create new issue**: Use issue templates for bugs or features
|
|
1276
|
+
3. **Discussions**: Use GitLab discussions for questions
|
|
1277
|
+
4. **Email**: Contact the maintainer via GitLab profile
|
|
1278
|
+
|
|
1279
|
+
---
|
|
1280
|
+
|
|
1281
|
+
**Made with ❤️ for the OpenCode community**
|
|
1282
|
+
|
|
1283
|
+
---
|