git-aiflow
Version:
🚀 An AI-powered workflow automation tool for effortless Git-based development, combining smart GitLab/GitHub merge & pull request creation with Conan package management.
159 lines • 7.53 kB
JavaScript
import { GitPlatformService } from './git-platform-service.js';
import { logger } from '../logger.js';
/**
* GitHub platform service implementation
*/
export class GithubPlatformService extends GitPlatformService {
constructor(token, baseUrl, gitService, http) {
super(token, baseUrl, gitService, http);
}
getPlatformName() {
return 'github';
}
async getProjectByPath(projectPath) {
const apiUrl = `${this.getApiBaseUrl()}/repos/${projectPath}`;
logger.info(`🔍 Fetching GitHub repository info from: ${apiUrl}`);
try {
const repo = await this.http.requestJson(apiUrl, 'GET', {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json'
});
logger.info(`✅ Found GitHub repository: ${repo.name} (ID: ${repo.id})`);
logger.info(`📋 Full name: ${repo.full_name}`);
return {
id: repo.id.toString(),
name: repo.name,
full_name: repo.full_name,
web_url: repo.html_url
};
}
catch (error) {
throw new Error(`Failed to get GitHub repository info for path "${projectPath}": ${error}`);
}
}
async createMergeRequestInternal(sourceBranch, targetBranch, title, options = {}) {
// Get repository information
const project = await this.getProject();
// Extract options with defaults
const { assignee_id, assignee_ids, reviewer_ids, squash = true, removeSourceBranch = true, description = '' } = options;
// Build assignees array for GitHub (GitHub uses username strings, but we'll try with IDs first)
const assignees = [];
// Note: GitHub API typically expects usernames, not user IDs for assignees
// For now, we'll convert IDs to strings and let the API handle validation
if (assignee_id && assignee_id > 0) {
assignees.push(assignee_id.toString());
logger.info(`📋 Setting assignee ID: ${assignee_id}`);
}
if (assignee_ids && assignee_ids.length > 0) {
const validAssigneeIds = assignee_ids.filter(id => id > 0).map(id => id.toString());
assignees.push(...validAssigneeIds);
logger.info(`📋 Setting assignee IDs: ${validAssigneeIds.join(', ')}`);
}
// GitHub uses different terminology: Pull Request instead of Merge Request
const requestBody = {
title: title,
head: sourceBranch, // Source branch
base: targetBranch, // Target branch
body: `Auto-generated pull request created by AIFlow.\n\nSource: ${sourceBranch}\nTarget: ${targetBranch}\n\nSquash commits: ${squash ? 'Yes' : 'No'}\nDelete source branch: ${removeSourceBranch ? 'Yes' : 'No'}`,
maintainer_can_modify: true // Allow maintainer to modify the PR
};
// Add assignees if specified
if (assignees.length > 0) {
requestBody.assignees = assignees;
}
// Add description if specified
if (description) {
requestBody.body = description;
logger.info(`📋 Setting description: ${description}`);
}
const apiUrl = `${this.getApiBaseUrl()}/repos/${project.full_name}/pulls`;
logger.info(`📋 Creating GitHub pull request for repository ${project.full_name}`);
try {
const resp = await this.http.requestJson(apiUrl, 'POST', {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json'
}, JSON.stringify(requestBody));
logger.info(`✅ Created GitHub pull request: ${resp.html_url}`);
// Add reviewers if specified (GitHub requires separate API call)
if (reviewer_ids && reviewer_ids.length > 0) {
const validReviewerIds = reviewer_ids.filter(id => id > 0);
if (validReviewerIds.length > 0) {
try {
logger.info(`📋 Setting reviewer IDs: ${validReviewerIds.join(', ')}`);
await this.addReviewersToRequest(project.full_name, resp.number, validReviewerIds);
}
catch (error) {
logger.warn(`⚠️ Failed to set reviewers: ${error}. PR created successfully but reviewers not assigned.`);
}
}
}
// Note: GitHub doesn't support auto-squash and auto-delete via API during PR creation
// These settings would need to be configured in the repository settings or during merge
if (squash || removeSourceBranch) {
logger.info(`💡 Note: GitHub squash (${squash}) and delete branch (${removeSourceBranch}) settings will apply during merge`);
}
// Convert to unified response format
return {
web_url: resp.html_url, // Map html_url to web_url for consistency
id: resp.id,
title: resp.title,
number: resp.number
};
}
catch (error) {
throw new Error(`Failed to create GitHub pull request: ${error}`);
}
}
/**
* Add reviewers to a pull request
* @param repoFullName Full repository name (owner/repo)
* @param prNumber Pull request number
* @param reviewerIds Array of reviewer user IDs
*/
async addReviewersToRequest(repoFullName, prNumber, reviewerIds) {
const apiUrl = `${this.getApiBaseUrl()}/repos/${repoFullName}/pulls/${prNumber}/requested_reviewers`;
// GitHub API expects usernames, not user IDs, but we'll try with IDs converted to strings
// In a real implementation, you might want to fetch user info by ID to get usernames
const reviewers = reviewerIds.map(id => id.toString());
const requestBody = {
reviewers: reviewers // GitHub expects usernames, but we're sending IDs as strings
};
try {
await this.http.requestJson(apiUrl, 'POST', {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/vnd.github.v3+json',
'Content-Type': 'application/json'
}, JSON.stringify(requestBody));
logger.info(`✅ Successfully added reviewers to PR #${prNumber}`);
}
catch (error) {
// Don't throw here, just log the warning since the PR was already created successfully
logger.warn(`⚠️ Could not add reviewers to PR #${prNumber}: ${error}`);
logger.warn(`💡 Note: GitHub API requires usernames for reviewers, not user IDs. Consider using usernames in configuration.`);
}
}
/**
* Get GitHub API base URL
* For github.com, use api.github.com
* For GitHub Enterprise, use hostname/api/v3
*/
getApiBaseUrl() {
let hostname;
try {
hostname = new URL(this.baseUrl).hostname;
}
catch {
hostname = '';
}
if (hostname === 'github.com') {
return 'https://api.github.com';
}
else {
// GitHub Enterprise
return `${this.baseUrl}/api/v3`;
}
}
}
//# sourceMappingURL=github-platform-service.js.map