@achieveai/azuredevops-mcp 1.2.0 → 1.2.2
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 +28 -1
- package/dist/Interfaces/Pipelines.js +3 -0
- package/dist/Interfaces/Pipelines.js.map +1 -0
- package/dist/Interfaces/Wiki.js +3 -0
- package/dist/Interfaces/Wiki.js.map +1 -0
- package/dist/Services/AzureDevOpsService.js +6 -5
- package/dist/Services/AzureDevOpsService.js.map +1 -1
- package/dist/Services/BuildService.js +172 -0
- package/dist/Services/BuildService.js.map +1 -0
- package/dist/Services/EntraAuthHandler.js +65 -18
- package/dist/Services/EntraAuthHandler.js.map +1 -1
- package/dist/Services/GitService.js +129 -0
- package/dist/Services/GitService.js.map +1 -1
- package/dist/Services/WikiService.js +90 -0
- package/dist/Services/WikiService.js.map +1 -0
- package/dist/Services/WorkItemService.js +123 -0
- package/dist/Services/WorkItemService.js.map +1 -1
- package/dist/Tools/BuildTools.js +402 -0
- package/dist/Tools/BuildTools.js.map +1 -0
- package/dist/Tools/GitTools.js +187 -41
- package/dist/Tools/GitTools.js.map +1 -1
- package/dist/Tools/WikiTools.js +137 -0
- package/dist/Tools/WikiTools.js.map +1 -0
- package/dist/Tools/WorkItemTools.js +120 -0
- package/dist/Tools/WorkItemTools.js.map +1 -1
- package/dist/config.js +27 -10
- package/dist/config.js.map +1 -1
- package/dist/index.js +297 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The integration is organized into eight main tool categories:
|
|
|
27
27
|
- Add comments to work items
|
|
28
28
|
- Update work item state
|
|
29
29
|
- Assign work items
|
|
30
|
-
- Create links between work items
|
|
30
|
+
- Create links between work items and artifacts (PRs, builds, branches, commits)
|
|
31
31
|
- Bulk create/update work items
|
|
32
32
|
|
|
33
33
|
### Boards & Sprints Tools
|
|
@@ -406,6 +406,33 @@ Once the server is running, you can interact with it using the MCP protocol. The
|
|
|
406
406
|
}
|
|
407
407
|
```
|
|
408
408
|
|
|
409
|
+
### Example: Link a Work Item to a Pull Request
|
|
410
|
+
|
|
411
|
+
The `createLink` tool supports linking work items to other work items or artifacts using prefix notation in the `targetId` field:
|
|
412
|
+
|
|
413
|
+
| Prefix | Target Type | Example | Requires `repository`? |
|
|
414
|
+
|--------|------------|---------|----------------------|
|
|
415
|
+
| `PR#` | Pull Request | `PR#456` | Yes |
|
|
416
|
+
| `BUILD#` | Build | `BUILD#789` | No |
|
|
417
|
+
| `BRANCH#` | Branch | `BRANCH#main` | Yes |
|
|
418
|
+
| `COMMIT#` | Commit | `COMMIT#abc123` | Yes |
|
|
419
|
+
| `WI#` or plain number | Work Item | `WI#123` or `123` | No |
|
|
420
|
+
|
|
421
|
+
```json
|
|
422
|
+
{
|
|
423
|
+
"tool": "createLink",
|
|
424
|
+
"params": {
|
|
425
|
+
"sourceId": 17086,
|
|
426
|
+
"targetId": "PR#10619",
|
|
427
|
+
"linkType": "System.LinkTypes.Related",
|
|
428
|
+
"repository": "MyRepo",
|
|
429
|
+
"comment": "Linked PR with fix"
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Artifact links are bidirectional -- the PR will appear on the work item's Links tab, and the work item will appear in the PR's Work Items section.
|
|
435
|
+
|
|
409
436
|
## Architecture
|
|
410
437
|
|
|
411
438
|
The project is structured as follows:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pipelines.js","sourceRoot":"","sources":["../../src/Interfaces/Pipelines.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Wiki.js","sourceRoot":"","sources":["../../src/Interfaces/Wiki.ts"],"names":[],"mappings":""}
|
|
@@ -40,14 +40,15 @@ class AzureDevOpsService {
|
|
|
40
40
|
constructor(config) {
|
|
41
41
|
this.config = config;
|
|
42
42
|
// Get the appropriate authentication handler
|
|
43
|
-
|
|
43
|
+
const tokenCredentialAuthTypes = ["entra", "azcli", "interactive"];
|
|
44
|
+
if (config.auth?.type && tokenCredentialAuthTypes.includes(config.auth.type)) {
|
|
44
45
|
if (config.isOnPremises) {
|
|
45
|
-
throw new Error(
|
|
46
|
+
throw new Error(`${config.auth.type} authentication is not supported for on-premises Azure DevOps.`);
|
|
46
47
|
}
|
|
47
|
-
if (!config.
|
|
48
|
-
throw new Error(
|
|
48
|
+
if (!config.tokenCredentialAuthHandler) {
|
|
49
|
+
throw new Error(`${config.auth.type} authentication requires a pre-initialized token credential auth handler.`);
|
|
49
50
|
}
|
|
50
|
-
this.authHandler = config.
|
|
51
|
+
this.authHandler = config.tokenCredentialAuthHandler;
|
|
51
52
|
}
|
|
52
53
|
else if (config.isOnPremises && config.auth) {
|
|
53
54
|
switch (config.auth.type) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AzureDevOpsService.js","sourceRoot":"","sources":["../../src/Services/AzureDevOpsService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6DAA+C;AAM/C,yDAIsC;AAMtC,MAAa,kBAAkB;IAK7B,YAAY,MAAyB;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,6CAA6C;QAE7C,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"AzureDevOpsService.js","sourceRoot":"","sources":["../../src/Services/AzureDevOpsService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BuildService = void 0;
|
|
4
|
+
const BuildInterfaces_1 = require("azure-devops-node-api/interfaces/BuildInterfaces");
|
|
5
|
+
const AzureDevOpsService_1 = require("./AzureDevOpsService");
|
|
6
|
+
class BuildService extends AzureDevOpsService_1.AzureDevOpsService {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
}
|
|
10
|
+
async getBuildApi() {
|
|
11
|
+
return await this.connection.getBuildApi();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* List builds with optional filters.
|
|
15
|
+
*/
|
|
16
|
+
async getBuilds(params) {
|
|
17
|
+
const buildApi = await this.getBuildApi();
|
|
18
|
+
const project = params.project || this.config.project;
|
|
19
|
+
const statusFilter = params.statusFilter ? this.parseBuildStatus(params.statusFilter) : undefined;
|
|
20
|
+
const resultFilter = params.resultFilter ? this.parseBuildResult(params.resultFilter) : undefined;
|
|
21
|
+
const queryOrder = params.queryOrder === 'startTimeAscending'
|
|
22
|
+
? BuildInterfaces_1.BuildQueryOrder.StartTimeAscending
|
|
23
|
+
: BuildInterfaces_1.BuildQueryOrder.StartTimeDescending;
|
|
24
|
+
const builds = await buildApi.getBuilds(project, params.definitions, // definitions
|
|
25
|
+
undefined, // queues
|
|
26
|
+
undefined, // buildNumber
|
|
27
|
+
undefined, // minTime
|
|
28
|
+
undefined, // maxTime
|
|
29
|
+
params.requestedFor, // requestedFor
|
|
30
|
+
undefined, // reasonFilter
|
|
31
|
+
statusFilter, // statusFilter
|
|
32
|
+
resultFilter, // resultFilter
|
|
33
|
+
params.tagFilters, // tagFilters
|
|
34
|
+
undefined, // properties
|
|
35
|
+
params.top || 25, // top
|
|
36
|
+
undefined, // continuationToken
|
|
37
|
+
undefined, // maxBuildsPerDefinition
|
|
38
|
+
undefined, // deletedFilter
|
|
39
|
+
queryOrder, // queryOrder
|
|
40
|
+
params.branchName, // branchName
|
|
41
|
+
undefined, // buildIds
|
|
42
|
+
params.repositoryId, // repositoryId
|
|
43
|
+
params.repositoryType);
|
|
44
|
+
return builds || [];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get a single build by ID.
|
|
48
|
+
*/
|
|
49
|
+
async getBuild(params) {
|
|
50
|
+
const buildApi = await this.getBuildApi();
|
|
51
|
+
const project = params.project || this.config.project;
|
|
52
|
+
return await buildApi.getBuild(project, params.buildId);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get build logs. If logId is provided, returns specific log lines; otherwise returns log metadata list.
|
|
56
|
+
*/
|
|
57
|
+
async getBuildLogs(params) {
|
|
58
|
+
const buildApi = await this.getBuildApi();
|
|
59
|
+
const project = params.project || this.config.project;
|
|
60
|
+
if (params.logId !== undefined) {
|
|
61
|
+
// Get specific log lines
|
|
62
|
+
const lines = await buildApi.getBuildLogLines(project, params.buildId, params.logId, params.startLine, params.endLine);
|
|
63
|
+
return { logId: params.logId, lines: lines || [] };
|
|
64
|
+
}
|
|
65
|
+
// Get all log metadata
|
|
66
|
+
const logs = await buildApi.getBuildLogs(project, params.buildId);
|
|
67
|
+
return logs || [];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get changes (commits) associated with a build.
|
|
71
|
+
*/
|
|
72
|
+
async getBuildChanges(params) {
|
|
73
|
+
const buildApi = await this.getBuildApi();
|
|
74
|
+
const project = params.project || this.config.project;
|
|
75
|
+
const changes = await buildApi.getBuildChanges(project, params.buildId, undefined, // continuationToken
|
|
76
|
+
params.top || 50);
|
|
77
|
+
return changes || [];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* List build/pipeline definitions.
|
|
81
|
+
*/
|
|
82
|
+
async getDefinitions(params) {
|
|
83
|
+
const buildApi = await this.getBuildApi();
|
|
84
|
+
const project = params.project || this.config.project;
|
|
85
|
+
const definitions = await buildApi.getDefinitions(project, params.name, // name filter
|
|
86
|
+
params.repositoryId, // repositoryId
|
|
87
|
+
params.repositoryType, // repositoryType
|
|
88
|
+
undefined, // queryOrder
|
|
89
|
+
params.top || 25, // top
|
|
90
|
+
undefined, // continuationToken
|
|
91
|
+
undefined, // minMetricsTime
|
|
92
|
+
undefined, // definitionIds
|
|
93
|
+
params.path, // path
|
|
94
|
+
undefined, // builtAfter
|
|
95
|
+
undefined, // notBuiltAfter
|
|
96
|
+
undefined, // includeAllProperties
|
|
97
|
+
params.includeLatestBuilds);
|
|
98
|
+
return definitions || [];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get a single build definition by ID.
|
|
102
|
+
*/
|
|
103
|
+
async getDefinition(params) {
|
|
104
|
+
const buildApi = await this.getBuildApi();
|
|
105
|
+
const project = params.project || this.config.project;
|
|
106
|
+
return await buildApi.getDefinition(project, params.definitionId, undefined, // revision
|
|
107
|
+
undefined, // minMetricsTime
|
|
108
|
+
undefined, // propertyFilters
|
|
109
|
+
params.includeLatestBuilds);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Queue/trigger a build run.
|
|
113
|
+
*/
|
|
114
|
+
async runPipeline(params) {
|
|
115
|
+
const buildApi = await this.getBuildApi();
|
|
116
|
+
const project = params.project || this.config.project;
|
|
117
|
+
const build = {
|
|
118
|
+
definition: { id: params.definitionId },
|
|
119
|
+
...(params.sourceBranch && { sourceBranch: params.sourceBranch }),
|
|
120
|
+
...(params.parameters && { parameters: JSON.stringify(params.parameters) }),
|
|
121
|
+
};
|
|
122
|
+
return await buildApi.queueBuild(build, project);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* List artifacts for a build.
|
|
126
|
+
*/
|
|
127
|
+
async getBuildArtifacts(params) {
|
|
128
|
+
const buildApi = await this.getBuildApi();
|
|
129
|
+
const project = params.project || this.config.project;
|
|
130
|
+
const artifacts = await buildApi.getArtifacts(project, params.buildId);
|
|
131
|
+
return artifacts || [];
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get build timeline (stages, jobs, tasks).
|
|
135
|
+
*/
|
|
136
|
+
async getBuildTimeline(params) {
|
|
137
|
+
const buildApi = await this.getBuildApi();
|
|
138
|
+
const project = params.project || this.config.project;
|
|
139
|
+
return await buildApi.getBuildTimeline(project, params.buildId);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get work items associated with a build.
|
|
143
|
+
*/
|
|
144
|
+
async getBuildWorkItems(params) {
|
|
145
|
+
const buildApi = await this.getBuildApi();
|
|
146
|
+
const project = params.project || this.config.project;
|
|
147
|
+
const refs = await buildApi.getBuildWorkItemsRefs(project, params.buildId, params.top || 50);
|
|
148
|
+
return refs || [];
|
|
149
|
+
}
|
|
150
|
+
parseBuildStatus(status) {
|
|
151
|
+
const map = {
|
|
152
|
+
'inprogress': BuildInterfaces_1.BuildStatus.InProgress,
|
|
153
|
+
'completed': BuildInterfaces_1.BuildStatus.Completed,
|
|
154
|
+
'cancelling': BuildInterfaces_1.BuildStatus.Cancelling,
|
|
155
|
+
'postponed': BuildInterfaces_1.BuildStatus.Postponed,
|
|
156
|
+
'notstarted': BuildInterfaces_1.BuildStatus.NotStarted,
|
|
157
|
+
'all': BuildInterfaces_1.BuildStatus.All,
|
|
158
|
+
};
|
|
159
|
+
return map[status.toLowerCase()];
|
|
160
|
+
}
|
|
161
|
+
parseBuildResult(result) {
|
|
162
|
+
const map = {
|
|
163
|
+
'succeeded': BuildInterfaces_1.BuildResult.Succeeded,
|
|
164
|
+
'partiallysucceeded': BuildInterfaces_1.BuildResult.PartiallySucceeded,
|
|
165
|
+
'failed': BuildInterfaces_1.BuildResult.Failed,
|
|
166
|
+
'canceled': BuildInterfaces_1.BuildResult.Canceled,
|
|
167
|
+
};
|
|
168
|
+
return map[result.toLowerCase()];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.BuildService = BuildService;
|
|
172
|
+
//# sourceMappingURL=BuildService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BuildService.js","sourceRoot":"","sources":["../../src/Services/BuildService.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,29 +33,63 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.EntraAuthHandler = void 0;
|
|
36
|
+
exports.EntraAuthHandler = exports.TokenCredentialAuthHandler = void 0;
|
|
37
37
|
const identity_1 = require("@azure/identity");
|
|
38
38
|
const azdev = __importStar(require("azure-devops-node-api"));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Azure DevOps scope for token requests.
|
|
41
|
+
*/
|
|
42
|
+
const AZURE_DEVOPS_SCOPE = "499b84ac-1321-427f-aa17-267ca6975798/.default";
|
|
43
|
+
/**
|
|
44
|
+
* Generic auth handler that wraps any @azure/identity TokenCredential.
|
|
45
|
+
* Supports automatic token refresh and re-authentication on 401.
|
|
46
|
+
*/
|
|
47
|
+
class TokenCredentialAuthHandler {
|
|
48
|
+
constructor(credential) {
|
|
49
|
+
this.credential = credential;
|
|
43
50
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Create handler from DefaultAzureCredential (Entra).
|
|
53
|
+
*/
|
|
54
|
+
static async createEntra() {
|
|
55
|
+
const handler = new TokenCredentialAuthHandler(new identity_1.DefaultAzureCredential());
|
|
56
|
+
await handler.ensureToken();
|
|
57
|
+
return handler;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create handler from AzureCliCredential.
|
|
61
|
+
*/
|
|
62
|
+
static async createAzureCli(tenantId) {
|
|
63
|
+
const credential = new identity_1.AzureCliCredential(tenantId ? { tenantId } : undefined);
|
|
64
|
+
const handler = new TokenCredentialAuthHandler(credential);
|
|
65
|
+
await handler.ensureToken();
|
|
66
|
+
return handler;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create handler from InteractiveBrowserCredential (MSAL).
|
|
70
|
+
* Opens a browser window for user login with token caching and silent re-auth.
|
|
71
|
+
*/
|
|
72
|
+
static async createInteractive(options) {
|
|
73
|
+
const credential = new identity_1.InteractiveBrowserCredential({
|
|
74
|
+
...(options?.tenantId && { tenantId: options.tenantId }),
|
|
75
|
+
...(options?.clientId && { clientId: options.clientId }),
|
|
76
|
+
redirectUri: "http://localhost",
|
|
77
|
+
});
|
|
78
|
+
const handler = new TokenCredentialAuthHandler(credential);
|
|
79
|
+
await handler.ensureToken();
|
|
80
|
+
return handler;
|
|
50
81
|
}
|
|
51
82
|
isTokenExpired() {
|
|
52
83
|
const currentTime = new Date().getTime();
|
|
53
|
-
// Check if the token is expired or will expire in the next 60 seconds
|
|
54
84
|
return this.token.expiresOnTimestamp <= currentTime + 60000;
|
|
55
85
|
}
|
|
56
86
|
async ensureToken() {
|
|
57
87
|
if (!this.token || this.isTokenExpired()) {
|
|
58
|
-
|
|
88
|
+
const token = await this.credential.getToken(AZURE_DEVOPS_SCOPE);
|
|
89
|
+
if (!token) {
|
|
90
|
+
throw new Error("Failed to acquire Azure DevOps access token.");
|
|
91
|
+
}
|
|
92
|
+
this.token = token;
|
|
59
93
|
this.authHandler = azdev.getHandlerFromToken(this.token.token);
|
|
60
94
|
}
|
|
61
95
|
}
|
|
@@ -63,16 +97,11 @@ class EntraAuthHandler {
|
|
|
63
97
|
if (this.authHandler) {
|
|
64
98
|
this.authHandler.prepareRequest(options);
|
|
65
99
|
}
|
|
66
|
-
// If no authHandler, the request will be sent unauthenticated.
|
|
67
|
-
// If it fails with 401, canHandleAuthentication and handleAuthentication will be invoked.
|
|
68
100
|
}
|
|
69
101
|
canHandleAuthentication(response) {
|
|
70
102
|
if (this.authHandler) {
|
|
71
103
|
return this.authHandler.canHandleAuthentication(response);
|
|
72
104
|
}
|
|
73
|
-
// If authHandler is not set, we can handle it if it's a 401 error
|
|
74
|
-
// typically handled by token-based authentication.
|
|
75
|
-
// This condition is standard in azure-devops-node-api handlers.
|
|
76
105
|
return response.message.statusCode === 401 &&
|
|
77
106
|
(response.message.statusMessage || "").toLowerCase().indexOf("non-authoritative") === -1;
|
|
78
107
|
}
|
|
@@ -81,5 +110,23 @@ class EntraAuthHandler {
|
|
|
81
110
|
return this.authHandler.handleAuthentication(httpClient, requestInfo, objs);
|
|
82
111
|
}
|
|
83
112
|
}
|
|
113
|
+
exports.TokenCredentialAuthHandler = TokenCredentialAuthHandler;
|
|
114
|
+
/**
|
|
115
|
+
* @deprecated Use TokenCredentialAuthHandler.createEntra() instead.
|
|
116
|
+
* Kept for backward compatibility.
|
|
117
|
+
*/
|
|
118
|
+
class EntraAuthHandler extends TokenCredentialAuthHandler {
|
|
119
|
+
constructor() {
|
|
120
|
+
super(new identity_1.DefaultAzureCredential());
|
|
121
|
+
}
|
|
122
|
+
static async getInstance() {
|
|
123
|
+
if (!EntraAuthHandler.instance) {
|
|
124
|
+
EntraAuthHandler.instance = new EntraAuthHandler();
|
|
125
|
+
}
|
|
126
|
+
// Ensure initial token is acquired
|
|
127
|
+
await EntraAuthHandler.instance.ensureToken();
|
|
128
|
+
return EntraAuthHandler.instance;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
84
131
|
exports.EntraAuthHandler = EntraAuthHandler;
|
|
85
132
|
//# sourceMappingURL=EntraAuthHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntraAuthHandler.js","sourceRoot":"","sources":["../../src/Services/EntraAuthHandler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"EntraAuthHandler.js","sourceRoot":"","sources":["../../src/Services/EntraAuthHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1326,6 +1326,135 @@ Original error: ${errorMessage}`);
|
|
|
1326
1326
|
// The diff itself is self-explanatory with standard +/- markers
|
|
1327
1327
|
return diffContent;
|
|
1328
1328
|
}
|
|
1329
|
+
// ── New PR Enhancement Methods ─────────────────────────────────
|
|
1330
|
+
/**
|
|
1331
|
+
* Update pull request properties (title, description, status, auto-complete, draft).
|
|
1332
|
+
*/
|
|
1333
|
+
async updatePullRequest(params) {
|
|
1334
|
+
const gitApi = await this.getGitApi();
|
|
1335
|
+
const repositoryId = await this.resolveRepositoryId(params.repository);
|
|
1336
|
+
const updatePayload = {};
|
|
1337
|
+
if (params.title !== undefined)
|
|
1338
|
+
updatePayload.title = params.title;
|
|
1339
|
+
if (params.description !== undefined)
|
|
1340
|
+
updatePayload.description = params.description;
|
|
1341
|
+
if (params.isDraft !== undefined)
|
|
1342
|
+
updatePayload.isDraft = params.isDraft;
|
|
1343
|
+
if (params.targetRefName !== undefined)
|
|
1344
|
+
updatePayload.targetRefName = params.targetRefName;
|
|
1345
|
+
if (params.status !== undefined) {
|
|
1346
|
+
const statusMap = { 'active': 1, 'abandoned': 3, 'completed': 2 };
|
|
1347
|
+
updatePayload.status = statusMap[params.status] || 1;
|
|
1348
|
+
}
|
|
1349
|
+
// Auto-complete configuration
|
|
1350
|
+
if (params.autoCompleteSetBy !== undefined || params.mergeStrategy !== undefined || params.deleteSourceBranch !== undefined) {
|
|
1351
|
+
if (params.autoCompleteSetBy) {
|
|
1352
|
+
// Set auto-complete with completion options
|
|
1353
|
+
updatePayload.autoCompleteSetBy = { id: params.autoCompleteSetBy };
|
|
1354
|
+
}
|
|
1355
|
+
const completionOptions = {};
|
|
1356
|
+
if (params.mergeStrategy) {
|
|
1357
|
+
const mergeMap = {
|
|
1358
|
+
'noFastForward': 1, 'squash': 2, 'rebase': 3, 'rebaseMerge': 4,
|
|
1359
|
+
};
|
|
1360
|
+
completionOptions.mergeStrategy = mergeMap[params.mergeStrategy] || 1;
|
|
1361
|
+
}
|
|
1362
|
+
if (params.deleteSourceBranch !== undefined) {
|
|
1363
|
+
completionOptions.deleteSourceBranch = params.deleteSourceBranch;
|
|
1364
|
+
}
|
|
1365
|
+
if (Object.keys(completionOptions).length > 0) {
|
|
1366
|
+
updatePayload.completionOptions = completionOptions;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return await gitApi.updatePullRequest(updatePayload, repositoryId, params.pullRequestId, this.config.project);
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Add or remove reviewers on a pull request.
|
|
1373
|
+
*/
|
|
1374
|
+
async updatePullRequestReviewers(params) {
|
|
1375
|
+
const gitApi = await this.getGitApi();
|
|
1376
|
+
const repositoryId = await this.resolveRepositoryId(params.repository);
|
|
1377
|
+
const results = { added: [], removed: [] };
|
|
1378
|
+
if (params.reviewersToAdd && params.reviewersToAdd.length > 0) {
|
|
1379
|
+
for (const reviewer of params.reviewersToAdd) {
|
|
1380
|
+
const reviewerObj = {
|
|
1381
|
+
id: reviewer,
|
|
1382
|
+
isRequired: params.makeRequired || false,
|
|
1383
|
+
};
|
|
1384
|
+
const result = await gitApi.createPullRequestReviewer(reviewerObj, repositoryId, params.pullRequestId, reviewer, this.config.project);
|
|
1385
|
+
results.added.push(result);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
if (params.reviewersToRemove && params.reviewersToRemove.length > 0) {
|
|
1389
|
+
for (const reviewer of params.reviewersToRemove) {
|
|
1390
|
+
await gitApi.deletePullRequestReviewer(repositoryId, params.pullRequestId, reviewer, this.config.project);
|
|
1391
|
+
results.removed.push(reviewer);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return results;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Reply to an existing comment thread on a PR.
|
|
1398
|
+
*/
|
|
1399
|
+
async replyToComment(params) {
|
|
1400
|
+
const gitApi = await this.getGitApi();
|
|
1401
|
+
const repositoryId = await this.resolveRepositoryId(params.repository);
|
|
1402
|
+
const comment = {
|
|
1403
|
+
content: params.comment,
|
|
1404
|
+
parentCommentId: 0, // 0 = reply to thread
|
|
1405
|
+
commentType: 1, // text comment
|
|
1406
|
+
};
|
|
1407
|
+
return await gitApi.createComment(comment, repositoryId, params.pullRequestId, params.threadId, this.config.project);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Update a comment thread's status (resolve/reactivate).
|
|
1411
|
+
*/
|
|
1412
|
+
async updatePullRequestThread(params) {
|
|
1413
|
+
const gitApi = await this.getGitApi();
|
|
1414
|
+
const repositoryId = await this.resolveRepositoryId(params.repository);
|
|
1415
|
+
const statusMap = {
|
|
1416
|
+
'active': 1,
|
|
1417
|
+
'fixed': 2,
|
|
1418
|
+
'wontFix': 3,
|
|
1419
|
+
'closed': 4,
|
|
1420
|
+
'byDesign': 5,
|
|
1421
|
+
'pending': 6,
|
|
1422
|
+
'unknown': 0,
|
|
1423
|
+
};
|
|
1424
|
+
const threadUpdate = {
|
|
1425
|
+
status: statusMap[params.status] ?? 1,
|
|
1426
|
+
};
|
|
1427
|
+
return await gitApi.updateThread(threadUpdate, repositoryId, params.pullRequestId, params.threadId, this.config.project);
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Create a new branch from a source ref.
|
|
1431
|
+
*/
|
|
1432
|
+
async createBranch(params) {
|
|
1433
|
+
const gitApi = await this.getGitApi();
|
|
1434
|
+
const repositoryId = await this.resolveRepositoryId(params.repository);
|
|
1435
|
+
// Resolve source ref to a commit ID
|
|
1436
|
+
let sourceObjectId = params.sourceRef;
|
|
1437
|
+
// If sourceRef looks like a branch name, resolve it to commit ID
|
|
1438
|
+
if (!sourceObjectId.match(/^[0-9a-f]{40}$/i)) {
|
|
1439
|
+
const branchName = sourceObjectId.replace(/^refs\/heads\//, '');
|
|
1440
|
+
const branches = await gitApi.getBranches(repositoryId, this.config.project);
|
|
1441
|
+
const branch = branches?.find((b) => b.name === branchName || b.name === `refs/heads/${branchName}`);
|
|
1442
|
+
if (!branch || !branch.commit?.commitId) {
|
|
1443
|
+
throw new Error(`Source branch '${branchName}' not found or has no commits.`);
|
|
1444
|
+
}
|
|
1445
|
+
sourceObjectId = branch.commit.commitId;
|
|
1446
|
+
}
|
|
1447
|
+
const branchRef = params.branchName.startsWith('refs/heads/')
|
|
1448
|
+
? params.branchName
|
|
1449
|
+
: `refs/heads/${params.branchName}`;
|
|
1450
|
+
const refUpdate = [{
|
|
1451
|
+
name: branchRef,
|
|
1452
|
+
oldObjectId: '0000000000000000000000000000000000000000',
|
|
1453
|
+
newObjectId: sourceObjectId,
|
|
1454
|
+
}];
|
|
1455
|
+
const result = await gitApi.updateRefs(refUpdate, repositoryId, this.config.project);
|
|
1456
|
+
return result?.[0];
|
|
1457
|
+
}
|
|
1329
1458
|
}
|
|
1330
1459
|
exports.GitService = GitService;
|
|
1331
1460
|
//# sourceMappingURL=GitService.js.map
|