@ryancardin/azuredevops-mcp-server
Version:
MCP server for Azure DevOps integration - provides seamless access to work items, repositories, projects, boards, and sprints
393 lines • 14.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitService = void 0;
const AzureDevOpsService_1 = require("./AzureDevOpsService");
class GitService extends AzureDevOpsService_1.AzureDevOpsService {
constructor(config) {
super(config);
}
/**
* Get the Git API client
*/
async getGitApi() {
return await this.connection.getGitApi();
}
/**
* List all repositories
*/
async listRepositories(params) {
try {
const gitApi = await this.getGitApi();
const repositories = await gitApi.getRepositories(params.projectId || this.config.project, params.includeHidden, params.includeAllUrls);
return repositories;
}
catch (error) {
console.error('Error listing repositories:', error);
throw error;
}
}
/**
* Get repository details
*/
async getRepository(params) {
try {
const gitApi = await this.getGitApi();
const repository = await gitApi.getRepository(params.repositoryId, params.projectId || this.config.project);
return repository;
}
catch (error) {
console.error(`Error getting repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Create a repository
*/
async createRepository(params) {
try {
const gitApi = await this.getGitApi();
const repository = await gitApi.createRepository({
name: params.name,
project: {
id: params.projectId || this.config.project
}
}, params.projectId || this.config.project);
return repository;
}
catch (error) {
console.error(`Error creating repository ${params.name}:`, error);
throw error;
}
}
/**
* List branches
*/
async listBranches(params) {
try {
const gitApi = await this.getGitApi();
const branches = await gitApi.getBranches(params.repositoryId, params.filter);
if (params.top && branches.length > params.top) {
return branches.slice(0, params.top);
}
return branches;
}
catch (error) {
console.error(`Error listing branches for repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Search code (Note: This uses a simplified approach as the full-text search API
* might require additional setup)
*/
async searchCode(params) {
try {
const gitApi = await this.getGitApi();
// This is a simplified implementation using item search
// For more comprehensive code search, you'd use the Search API
const items = await gitApi.getItems(params.repositoryId || "", undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined);
// Simple filter based on the search text and file extension
let filteredItems = items;
if (params.searchText) {
filteredItems = filteredItems.filter(item => item.path && item.path.toLowerCase().includes(params.searchText.toLowerCase()));
}
if (params.fileExtension) {
filteredItems = filteredItems.filter(item => item.path && item.path.endsWith(params.fileExtension || ""));
}
// Limit results if top is specified
if (params.top && filteredItems.length > params.top) {
filteredItems = filteredItems.slice(0, params.top);
}
return filteredItems;
}
catch (error) {
console.error(`Error searching code in repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Browse repository
*/
async browseRepository(params) {
try {
const gitApi = await this.getGitApi();
const items = await gitApi.getItems(params.repositoryId, undefined, params.path, undefined, true, undefined, undefined, undefined, undefined, undefined);
return items;
}
catch (error) {
console.error(`Error browsing repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Get file content
*/
async getFileContent(params) {
try {
const gitApi = await this.getGitApi();
// Get the file content as a stream
const content = await gitApi.getItemContent(params.repositoryId, params.path, undefined, undefined);
let fileContent = '';
// Handle different content types
if (Buffer.isBuffer(content)) {
fileContent = content.toString('utf8');
}
else if (typeof content === 'string') {
fileContent = content;
}
else if (content && typeof content === 'object' && 'pipe' in content && typeof content.pipe === 'function') {
// Handle stream content
const chunks = [];
const stream = content;
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
stream.destroy();
reject(new Error(`Stream timeout for ${params.path}`));
}, 30000);
stream.on('data', (chunk) => {
chunks.push(chunk);
});
stream.on('end', () => {
clearTimeout(timeout);
const buffer = Buffer.concat(chunks);
const fileContent = buffer.toString('utf8');
resolve({
content: fileContent
});
});
stream.on('error', (error) => {
clearTimeout(timeout);
console.error(`Error reading stream for ${params.path}:`, error);
reject(error);
});
});
}
else {
// If it's some other type, return a placeholder
fileContent = "[Content not available in this format]";
}
return {
content: fileContent
};
}
catch (error) {
console.error(`Error getting file content for ${params.path}:`, error);
throw error;
}
}
/**
* Get commit history
*/
async getCommitHistory(params) {
try {
const gitApi = await this.getGitApi();
// Get commits without search criteria
const commits = await gitApi.getCommits(params.repositoryId, {} // Empty search criteria
);
// Filter by path if provided
let filteredCommits = commits;
if (params.itemPath) {
filteredCommits = commits.filter(commit => commit.comment && commit.comment.includes(params.itemPath || ""));
}
// Apply pagination if specified
if (params.skip && params.skip > 0) {
filteredCommits = filteredCommits.slice(params.skip);
}
if (params.top && params.top > 0) {
filteredCommits = filteredCommits.slice(0, params.top);
}
return filteredCommits;
}
catch (error) {
console.error(`Error getting commit history for repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Get commits
*/
async getCommits(params) {
try {
const gitApi = await this.getGitApi();
// Get commits without search criteria
const commits = await gitApi.getCommits(params.repositoryId, {} // Empty search criteria
);
// Filter by path if provided
let filteredCommits = commits;
if (params.path) {
filteredCommits = commits.filter(commit => commit.comment && commit.comment.includes(params.path || ""));
}
return filteredCommits;
}
catch (error) {
console.error(`Error getting commits for repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Get pull requests
*/
async getPullRequests(params) {
try {
const gitApi = await this.getGitApi();
// Create search criteria with proper types
const searchCriteria = {
repositoryId: params.repositoryId,
creatorId: params.creatorId,
reviewerId: params.reviewerId,
sourceRefName: params.sourceRefName,
targetRefName: params.targetRefName
};
// Convert string status to number if provided
if (params.status) {
if (params.status === 'active')
searchCriteria.status = 1;
else if (params.status === 'abandoned')
searchCriteria.status = 2;
else if (params.status === 'completed')
searchCriteria.status = 3;
else if (params.status === 'notSet')
searchCriteria.status = 0;
// 'all' doesn't need to be set
}
const pullRequests = await gitApi.getPullRequests(params.repositoryId, searchCriteria);
return pullRequests;
}
catch (error) {
console.error(`Error getting pull requests for repository ${params.repositoryId}:`, error);
throw error;
}
}
/**
* Create pull request
*/
async createPullRequest(params) {
try {
const gitApi = await this.getGitApi();
const pullRequest = {
sourceRefName: params.sourceRefName,
targetRefName: params.targetRefName,
title: params.title,
description: params.description,
reviewers: params.reviewers ? params.reviewers.map(id => ({ id })) : undefined
};
const createdPullRequest = await gitApi.createPullRequest(pullRequest, params.repositoryId, this.config.project);
return createdPullRequest;
}
catch (error) {
console.error('Error creating pull request:', error);
throw error;
}
}
/**
* Get pull request by ID
*/
async getPullRequest(params) {
try {
const gitApi = await this.getGitApi();
const pullRequest = await gitApi.getPullRequest(params.repositoryId, params.pullRequestId, this.config.project);
return pullRequest;
}
catch (error) {
console.error(`Error getting pull request ${params.pullRequestId}:`, error);
throw error;
}
}
/**
* Get pull request comments
*/
async getPullRequestComments(params) {
try {
const gitApi = await this.getGitApi();
if (params.threadId) {
const thread = await gitApi.getPullRequestThread(params.repositoryId, params.pullRequestId, params.threadId, this.config.project);
return thread;
}
else {
const threads = await gitApi.getThreads(params.repositoryId, params.pullRequestId, this.config.project);
return threads;
}
}
catch (error) {
console.error(`Error getting comments for pull request ${params.pullRequestId}:`, error);
throw error;
}
}
/**
* Approve pull request
*/
async approvePullRequest(params) {
try {
const gitApi = await this.getGitApi();
const vote = {
vote: 10
};
const result = await gitApi.createPullRequestReviewer(vote, params.repositoryId, params.pullRequestId, "me", this.config.project);
return result;
}
catch (error) {
console.error(`Error approving pull request ${params.pullRequestId}:`, error);
throw error;
}
}
/**
* Merge pull request
*/
async mergePullRequest(params) {
try {
const gitApi = await this.getGitApi();
// Convert string merge strategy to number
let mergeStrategy = 1; // Default to noFastForward
if (params.mergeStrategy === 'rebase')
mergeStrategy = 2;
else if (params.mergeStrategy === 'rebaseMerge')
mergeStrategy = 3;
else if (params.mergeStrategy === 'squash')
mergeStrategy = 4;
const result = await gitApi.updatePullRequest({
status: 3, // 3 = completed in PullRequestStatus enum
completionOptions: {
mergeStrategy: mergeStrategy
}
}, params.repositoryId, params.pullRequestId, this.config.project);
return result;
}
catch (error) {
console.error(`Error merging pull request ${params.pullRequestId}:`, error);
throw error;
}
}
/**
* Complete pull request
*/
async completePullRequest(params) {
try {
const gitApi = await this.getGitApi();
// Get the current pull request
const pullRequest = await gitApi.getPullRequestById(params.pullRequestId);
// Convert string merge strategy to number
let mergeStrategy = 1; // Default to noFastForward
if (params.mergeStrategy === 'rebase')
mergeStrategy = 2;
else if (params.mergeStrategy === 'rebaseMerge')
mergeStrategy = 3;
else if (params.mergeStrategy === 'squash')
mergeStrategy = 4;
// Update the pull request to completed status
const updatedPullRequest = await gitApi.updatePullRequest({
status: 3, // 3 = completed in PullRequestStatus enum
completionOptions: {
mergeStrategy: mergeStrategy,
deleteSourceBranch: params.deleteSourceBranch
}
}, params.repositoryId, params.pullRequestId);
return updatedPullRequest;
}
catch (error) {
console.error(`Error completing pull request ${params.pullRequestId}:`, error);
throw error;
}
}
}
exports.GitService = GitService;
//# sourceMappingURL=GitService.js.map