mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
821 lines
24 KiB
TypeScript
821 lines
24 KiB
TypeScript
import {
|
|
GetCodeRepositoryPassword,
|
|
GetCodeRepositoryUsername,
|
|
GetLocalRepositoryPath,
|
|
GetOneUptimeURL,
|
|
GetRepositorySecretKey,
|
|
} from "../Config";
|
|
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
|
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
|
import URL from "Common/Types/API/URL";
|
|
import CodeRepositoryType from "Common/Types/CodeRepository/CodeRepositoryType";
|
|
import PullRequest from "Common/Types/CodeRepository/PullRequest";
|
|
import PullRequestState from "Common/Types/CodeRepository/PullRequestState";
|
|
import BadDataException from "Common/Types/Exception/BadDataException";
|
|
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
|
import API from "Common/Utils/API";
|
|
import CodeRepositoryServerUtil from "Common/Server/Utils/CodeRepository/CodeRepository";
|
|
import GitHubUtil from "Common/Server/Utils/CodeRepository/GitHub/GitHub";
|
|
import LocalFile from "Common/Server/Utils/LocalFile";
|
|
import logger from "Common/Server/Utils/Logger";
|
|
import CopilotCodeRepository from "Common/Models/DatabaseModels/CopilotCodeRepository";
|
|
import ServiceCopilotCodeRepository from "Common/Models/DatabaseModels/ServiceCopilotCodeRepository";
|
|
import Text from "Common/Types/Text";
|
|
import Execute from "Common/Server/Utils/Execute";
|
|
import CopilotPullRequestService from "../Service/CopilotPullRequest";
|
|
import CopilotPullRequest from "Common/Models/DatabaseModels/CopilotPullRequest";
|
|
|
|
export interface CodeRepositoryResult {
|
|
codeRepository: CopilotCodeRepository;
|
|
serviceRepositories: Array<ServiceCopilotCodeRepository>;
|
|
}
|
|
|
|
export interface ServiceToImproveResult {
|
|
serviceRepository: ServiceCopilotCodeRepository;
|
|
numberOfOpenPullRequests: number;
|
|
pullRequests: Array<CopilotPullRequest>;
|
|
}
|
|
|
|
export enum RepoScriptType {
|
|
OnAfterClone = "onAfterClone",
|
|
OnBeforeCommit = "onBeforeCommit",
|
|
OnAfterCommit = "OnAfterCommit",
|
|
OnBeforeCodeChange = "OnBeforeCodeChange",
|
|
OnAfterCodeChange = "OnAfterCodeChange",
|
|
}
|
|
|
|
export default class CodeRepositoryUtil {
|
|
public static codeRepositoryResult: CodeRepositoryResult | null = null;
|
|
public static gitHubUtil: GitHubUtil | null = null;
|
|
public static folderNameOfClonedRepository: string | null = null;
|
|
|
|
public static async getCurrentCommitHash(): Promise<string> {
|
|
return await CodeRepositoryServerUtil.getCurrentCommitHash({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
});
|
|
}
|
|
|
|
public static isRepoCloned(): boolean {
|
|
return Boolean(this.folderNameOfClonedRepository);
|
|
}
|
|
|
|
public static async getOpenSetupPullRequest(): Promise<CopilotPullRequest | null> {
|
|
const openPullRequests: Array<CopilotPullRequest> =
|
|
await CopilotPullRequestService.getOpenPullRequestsFromDatabase();
|
|
|
|
for (const pullRequest of openPullRequests) {
|
|
if (pullRequest.isSetupPullRequest) {
|
|
return pullRequest;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static getLocalRepositoryPath(): string {
|
|
if (this.folderNameOfClonedRepository) {
|
|
return LocalFile.sanitizeFilePath(
|
|
GetLocalRepositoryPath() + "/" + this.folderNameOfClonedRepository,
|
|
);
|
|
}
|
|
|
|
return GetLocalRepositoryPath();
|
|
}
|
|
|
|
public static async discardAllChangesOnCurrentBranch(): Promise<void> {
|
|
await CodeRepositoryServerUtil.discardAllChangesOnCurrentBranch({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
});
|
|
}
|
|
|
|
public static async setAuthorIdentity(data: {
|
|
name: string;
|
|
email: string;
|
|
}): Promise<void> {
|
|
await CodeRepositoryServerUtil.setAuthorIdentity({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
authorName: data.name,
|
|
authorEmail: data.email,
|
|
});
|
|
}
|
|
|
|
public static async getPullRequestState(data: {
|
|
pullRequestId: string;
|
|
}): Promise<PullRequestState> {
|
|
// check if org name and repo name is present.
|
|
|
|
if (!this.codeRepositoryResult?.codeRepository.organizationName) {
|
|
throw new BadDataException("Organization Name is required");
|
|
}
|
|
|
|
if (!this.codeRepositoryResult?.codeRepository.repositoryName) {
|
|
throw new BadDataException("Repository Name is required");
|
|
}
|
|
|
|
const githubUtil: GitHubUtil = this.getGitHubUtil();
|
|
|
|
if (!githubUtil) {
|
|
throw new BadDataException("GitHub Util is required");
|
|
}
|
|
|
|
const pullRequest: PullRequest | undefined =
|
|
await githubUtil.getPullRequestByNumber({
|
|
organizationName:
|
|
this.codeRepositoryResult.codeRepository.organizationName,
|
|
repositoryName: this.codeRepositoryResult.codeRepository.repositoryName,
|
|
pullRequestId: data.pullRequestId,
|
|
});
|
|
|
|
if (!pullRequest) {
|
|
throw new BadDataException("Pull Request not found");
|
|
}
|
|
|
|
return pullRequest.state;
|
|
}
|
|
|
|
public static async setUpRepo(): Promise<PullRequest> {
|
|
// check if the repository is setup properly.
|
|
const isRepoSetupProperly: boolean = await this.isRepoSetupProperly();
|
|
|
|
if (isRepoSetupProperly) {
|
|
throw new BadDataException("Repository is already setup properly.");
|
|
}
|
|
|
|
// otherwise, we copy the folder /usr/src/app/Templates/.oneuptime to the repository folder.
|
|
|
|
const templateFolderPath: string = LocalFile.sanitizeFilePath(
|
|
"/usr/src/app/Templates/.oneuptime",
|
|
);
|
|
|
|
const oneUptimeConfigPath: string = LocalFile.sanitizeFilePath(
|
|
this.getLocalRepositoryPath() + "/.oneuptime",
|
|
);
|
|
|
|
// create a new branch called oneuptime-copilot-setup
|
|
|
|
const branchName: string = "setup-" + Text.generateRandomText(5);
|
|
|
|
await this.createBranch({
|
|
branchName: branchName,
|
|
});
|
|
|
|
await LocalFile.makeDirectory(oneUptimeConfigPath);
|
|
|
|
await LocalFile.copyDirectory({
|
|
source: templateFolderPath,
|
|
destination: oneUptimeConfigPath,
|
|
});
|
|
|
|
// add all the files to the git.
|
|
|
|
await this.addAllChangedFilesToGit();
|
|
|
|
// commit the changes.
|
|
|
|
await this.commitChanges({
|
|
message: "OneUptime Copilot Setup",
|
|
});
|
|
|
|
// push changes to the repo.
|
|
|
|
await this.pushChanges({
|
|
branchName: branchName,
|
|
});
|
|
|
|
// create a pull request.
|
|
|
|
const pullRequest: PullRequest = await this.createPullRequest({
|
|
branchName: branchName,
|
|
title: "OneUptime Copilot Setup",
|
|
body: "This pull request is created by OneUptime Copilot to setup the repository.",
|
|
});
|
|
|
|
// save this to the database.
|
|
|
|
await CopilotPullRequestService.addPullRequestToDatabase({
|
|
pullRequest: pullRequest,
|
|
isSetupPullRequest: true,
|
|
});
|
|
|
|
return pullRequest;
|
|
}
|
|
|
|
public static async isRepoSetupProperly(): Promise<boolean> {
|
|
// check if .oneuptime folder exists.
|
|
|
|
const repoPath: string = this.getLocalRepositoryPath();
|
|
|
|
const oneUptimeFolderPath: string = LocalFile.sanitizeFilePath(
|
|
`${repoPath}/.oneuptime`,
|
|
);
|
|
|
|
const doesDirectoryExist: boolean =
|
|
await LocalFile.doesDirectoryExist(oneUptimeFolderPath);
|
|
|
|
if (!doesDirectoryExist) {
|
|
return false;
|
|
}
|
|
|
|
// check if .oneuptime/scripts folder exists.
|
|
|
|
const oneuptimeScriptsPath: string = LocalFile.sanitizeFilePath(
|
|
`${oneUptimeFolderPath}/scripts`,
|
|
);
|
|
|
|
const doesScriptsDirectoryExist: boolean =
|
|
await LocalFile.doesDirectoryExist(oneuptimeScriptsPath);
|
|
|
|
if (!doesScriptsDirectoryExist) {
|
|
return false;
|
|
}
|
|
|
|
return true; // return true if all checks pass.
|
|
}
|
|
|
|
public static addAllChangedFilesToGit(): Promise<void> {
|
|
return CodeRepositoryServerUtil.addAllChangedFilesToGit({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
});
|
|
}
|
|
|
|
// returns the folder name of the cloned repository.
|
|
public static async cloneRepository(data: {
|
|
codeRepository: CopilotCodeRepository;
|
|
}): Promise<void> {
|
|
// make sure this.getLocalRepositoryPath() is empty.
|
|
const repoLocalPath: string = this.getLocalRepositoryPath();
|
|
|
|
await LocalFile.deleteAllDataInDirectory(repoLocalPath);
|
|
await LocalFile.makeDirectory(repoLocalPath);
|
|
|
|
// check if the data in the directory eixsts, if it does then delete it.
|
|
|
|
if (!data.codeRepository.repositoryHostedAt) {
|
|
throw new BadDataException("Repository Hosted At is required");
|
|
}
|
|
|
|
if (!data.codeRepository.mainBranchName) {
|
|
throw new BadDataException("Main Branch Name is required");
|
|
}
|
|
|
|
if (!data.codeRepository.organizationName) {
|
|
throw new BadDataException("Organization Name is required");
|
|
}
|
|
|
|
if (!data.codeRepository.repositoryName) {
|
|
throw new BadDataException("Repository Name is required");
|
|
}
|
|
|
|
const CodeRepositoryUsername: string | null = GetCodeRepositoryUsername();
|
|
|
|
if (!CodeRepositoryUsername) {
|
|
throw new BadDataException("Code Repository Username is required");
|
|
}
|
|
|
|
const CodeRepositoryPassword: string | null = GetCodeRepositoryPassword();
|
|
|
|
if (!CodeRepositoryPassword) {
|
|
throw new BadDataException("Code Repository Password is required");
|
|
}
|
|
|
|
const repoUrl: string = `https://${CodeRepositoryUsername}:${CodeRepositoryPassword}@${
|
|
data.codeRepository.repositoryHostedAt === CodeRepositoryType.GitHub
|
|
? "github.com"
|
|
: ""
|
|
}/${data.codeRepository.organizationName}/${data.codeRepository.repositoryName}.git`;
|
|
|
|
const folderName: string = await CodeRepositoryServerUtil.cloneRepository({
|
|
repoUrl: repoUrl,
|
|
repoPath: repoLocalPath,
|
|
});
|
|
|
|
this.folderNameOfClonedRepository = folderName;
|
|
|
|
logger.debug(`Repository cloned to ${repoLocalPath}/${folderName}`);
|
|
}
|
|
|
|
public static async executeScript(data: { script: string }): Promise<string> {
|
|
const commands: Array<string> = data.script
|
|
.split("\n")
|
|
.filter((command: string) => {
|
|
return command.trim() !== "" && !command.startsWith("#");
|
|
});
|
|
|
|
const results: Array<string> = [];
|
|
|
|
for (const command of commands) {
|
|
logger.info(`Executing command: ${command}`);
|
|
const commandResult: string = await Execute.executeCommand(
|
|
`cd ${this.getLocalRepositoryPath()} && ${command}`,
|
|
);
|
|
if (commandResult) {
|
|
logger.info(`Command result: ${commandResult}`);
|
|
results.push(commandResult);
|
|
}
|
|
}
|
|
|
|
return results.join("\n");
|
|
}
|
|
|
|
public static async getRepoScript(data: {
|
|
scriptType: RepoScriptType;
|
|
}): Promise<string | null> {
|
|
const repoPath: string = this.getLocalRepositoryPath();
|
|
|
|
const oneUptimeFolderPath: string = LocalFile.sanitizeFilePath(
|
|
`${repoPath}/.oneuptime`,
|
|
);
|
|
|
|
const doesDirectoryExist: boolean =
|
|
await LocalFile.doesDirectoryExist(oneUptimeFolderPath);
|
|
|
|
if (!doesDirectoryExist) {
|
|
return null;
|
|
}
|
|
|
|
const oneuptimeScriptsPath: string = LocalFile.sanitizeFilePath(
|
|
`${oneUptimeFolderPath}/scripts`,
|
|
);
|
|
|
|
const doesScriptsDirectoryExist: boolean =
|
|
await LocalFile.doesDirectoryExist(oneuptimeScriptsPath);
|
|
|
|
if (!doesScriptsDirectoryExist) {
|
|
return null;
|
|
}
|
|
|
|
const scriptPath: string = LocalFile.sanitizeFilePath(
|
|
`${oneuptimeScriptsPath}/${Text.fromPascalCaseToDashes(data.scriptType)}.sh`,
|
|
);
|
|
|
|
const doesScriptExist: boolean = await LocalFile.doesFileExist(scriptPath);
|
|
|
|
if (!doesScriptExist) {
|
|
return null;
|
|
}
|
|
|
|
const scriptContent: string = await LocalFile.read(scriptPath);
|
|
|
|
return scriptContent.trim() || null;
|
|
}
|
|
|
|
public static hasOpenPRForFile(data: {
|
|
filePath: string;
|
|
pullRequests: Array<PullRequest>;
|
|
}): boolean {
|
|
const pullRequests: Array<PullRequest> = this.getOpenPRForFile(data);
|
|
return pullRequests.length > 0;
|
|
}
|
|
|
|
public static getOpenPRForFile(data: {
|
|
filePath: string;
|
|
pullRequests: Array<PullRequest>;
|
|
}): Array<PullRequest> {
|
|
const pullRequests: Array<PullRequest> = [];
|
|
|
|
for (const pullRequest of data.pullRequests) {
|
|
if (pullRequest.title.includes(data.filePath)) {
|
|
pullRequests.push(pullRequest);
|
|
}
|
|
}
|
|
|
|
return pullRequests;
|
|
}
|
|
|
|
public static async listFilesInDirectory(data: {
|
|
directoryPath: string;
|
|
}): Promise<Array<string>> {
|
|
return await CodeRepositoryServerUtil.listFilesInDirectory({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
directoryPath: data.directoryPath,
|
|
});
|
|
}
|
|
|
|
public static getGitHubUtil(): GitHubUtil {
|
|
if (!this.gitHubUtil) {
|
|
const gitHubToken: string | null = GetCodeRepositoryPassword();
|
|
|
|
const gitHubUsername: string | null = GetCodeRepositoryUsername();
|
|
|
|
if (!gitHubUsername) {
|
|
throw new BadDataException("GitHub Username is required");
|
|
}
|
|
|
|
if (!gitHubToken) {
|
|
throw new BadDataException("GitHub Token is required");
|
|
}
|
|
|
|
this.gitHubUtil = new GitHubUtil({
|
|
authToken: gitHubToken,
|
|
username: gitHubUsername!,
|
|
});
|
|
}
|
|
|
|
return this.gitHubUtil;
|
|
}
|
|
|
|
public static async pullChanges(): Promise<void> {
|
|
await CodeRepositoryServerUtil.pullChanges({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
});
|
|
}
|
|
|
|
public static getBranchName(data: { branchName: string }): string {
|
|
return "oneuptime-copilot-" + data.branchName;
|
|
}
|
|
|
|
public static async createBranch(data: {
|
|
branchName: string;
|
|
}): Promise<void> {
|
|
const branchName: string = this.getBranchName({
|
|
branchName: data.branchName,
|
|
});
|
|
|
|
await CodeRepositoryServerUtil.createBranch({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
branchName: branchName,
|
|
});
|
|
}
|
|
|
|
public static async createOrCheckoutBranch(data: {
|
|
branchName: string;
|
|
}): Promise<void> {
|
|
const branchName: string = this.getBranchName({
|
|
branchName: data.branchName,
|
|
});
|
|
|
|
await CodeRepositoryServerUtil.createOrCheckoutBranch({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
branchName: branchName,
|
|
});
|
|
}
|
|
|
|
public static async writeToFile(data: {
|
|
filePath: string;
|
|
content: string;
|
|
}): Promise<void> {
|
|
await CodeRepositoryServerUtil.writeToFile({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
filePath: data.filePath,
|
|
content: data.content,
|
|
});
|
|
}
|
|
|
|
public static async createDirectory(data: {
|
|
directoryPath: string;
|
|
}): Promise<void> {
|
|
await CodeRepositoryServerUtil.createDirectory({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
directoryPath: data.directoryPath,
|
|
});
|
|
}
|
|
|
|
public static async deleteFile(data: { filePath: string }): Promise<void> {
|
|
await CodeRepositoryServerUtil.deleteFile({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
filePath: data.filePath,
|
|
});
|
|
}
|
|
|
|
public static async deleteDirectory(data: {
|
|
directoryPath: string;
|
|
}): Promise<void> {
|
|
await CodeRepositoryServerUtil.deleteDirectory({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
directoryPath: data.directoryPath,
|
|
});
|
|
}
|
|
|
|
public static async discardChanges(): Promise<void> {
|
|
if (this.isRepoCloned()) {
|
|
await CodeRepositoryServerUtil.discardChanges({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
});
|
|
}
|
|
}
|
|
|
|
public static async checkoutBranch(data: {
|
|
branchName: string;
|
|
}): Promise<void> {
|
|
if (this.isRepoCloned()) {
|
|
await CodeRepositoryServerUtil.checkoutBranch({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
branchName: data.branchName,
|
|
});
|
|
}
|
|
}
|
|
|
|
public static async checkoutMainBranch(): Promise<void> {
|
|
if (!this.isRepoCloned()) {
|
|
return;
|
|
}
|
|
|
|
const codeRepository: CopilotCodeRepository =
|
|
await this.getCodeRepository();
|
|
|
|
if (!codeRepository.mainBranchName) {
|
|
throw new BadDataException("Main Branch Name is required");
|
|
}
|
|
|
|
await this.checkoutBranch({
|
|
branchName: codeRepository.mainBranchName!,
|
|
});
|
|
}
|
|
|
|
public static async addFilesToGit(data: {
|
|
filePaths: Array<string>;
|
|
}): Promise<void> {
|
|
await CodeRepositoryServerUtil.addFilesToGit({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
filePaths: data.filePaths,
|
|
});
|
|
}
|
|
|
|
public static async commitChanges(data: { message: string }): Promise<void> {
|
|
let username: string | null = null;
|
|
|
|
if (
|
|
this.codeRepositoryResult?.codeRepository.repositoryHostedAt ===
|
|
CodeRepositoryType.GitHub
|
|
) {
|
|
username = GetCodeRepositoryUsername();
|
|
}
|
|
|
|
if (!username) {
|
|
throw new BadDataException("Username is required");
|
|
}
|
|
|
|
await CodeRepositoryServerUtil.commitChanges({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
message: data.message,
|
|
});
|
|
}
|
|
|
|
public static async pushChanges(data: { branchName: string }): Promise<void> {
|
|
const branchName: string = this.getBranchName({
|
|
branchName: data.branchName,
|
|
});
|
|
|
|
const codeRepository: CopilotCodeRepository =
|
|
await this.getCodeRepository();
|
|
|
|
if (!codeRepository.mainBranchName) {
|
|
throw new BadDataException("Main Branch Name is required");
|
|
}
|
|
|
|
if (!codeRepository.organizationName) {
|
|
throw new BadDataException("Organization Name is required");
|
|
}
|
|
|
|
if (!codeRepository.repositoryName) {
|
|
throw new BadDataException("Repository Name is required");
|
|
}
|
|
|
|
if (codeRepository.repositoryHostedAt === CodeRepositoryType.GitHub) {
|
|
return await this.getGitHubUtil().pushChanges({
|
|
repoPath: this.getLocalRepositoryPath(),
|
|
branchName: branchName,
|
|
organizationName: codeRepository.organizationName,
|
|
repositoryName: codeRepository.repositoryName,
|
|
});
|
|
}
|
|
}
|
|
|
|
public static async switchToMainBranch(): Promise<void> {
|
|
const codeRepository: CopilotCodeRepository =
|
|
await this.getCodeRepository();
|
|
|
|
if (!codeRepository.mainBranchName) {
|
|
throw new BadDataException("Main Branch Name is required");
|
|
}
|
|
|
|
await this.checkoutBranch({
|
|
branchName: codeRepository.mainBranchName!,
|
|
});
|
|
}
|
|
|
|
public static async createPullRequest(data: {
|
|
branchName: string;
|
|
title: string;
|
|
body: string;
|
|
}): Promise<PullRequest> {
|
|
const branchName: string = this.getBranchName({
|
|
branchName: data.branchName,
|
|
});
|
|
|
|
const codeRepository: CopilotCodeRepository =
|
|
await this.getCodeRepository();
|
|
|
|
if (!codeRepository.mainBranchName) {
|
|
throw new BadDataException("Main Branch Name is required");
|
|
}
|
|
|
|
if (!codeRepository.organizationName) {
|
|
throw new BadDataException("Organization Name is required");
|
|
}
|
|
|
|
if (!codeRepository.repositoryName) {
|
|
throw new BadDataException("Repository Name is required");
|
|
}
|
|
|
|
if (codeRepository.repositoryHostedAt === CodeRepositoryType.GitHub) {
|
|
return await this.getGitHubUtil().createPullRequest({
|
|
headBranchName: branchName,
|
|
baseBranchName: codeRepository.mainBranchName,
|
|
organizationName: codeRepository.organizationName,
|
|
repositoryName: codeRepository.repositoryName,
|
|
title: data.title,
|
|
body: data.body,
|
|
});
|
|
}
|
|
throw new BadDataException("Code Repository type not supported");
|
|
}
|
|
|
|
public static async getServicesToImproveCode(data: {
|
|
codeRepository: CopilotCodeRepository;
|
|
serviceRepositories: Array<ServiceCopilotCodeRepository>;
|
|
openPullRequests: Array<CopilotPullRequest>;
|
|
}): Promise<Array<ServiceToImproveResult>> {
|
|
const servicesToImproveCode: Array<ServiceToImproveResult> = [];
|
|
|
|
for (const service of data.serviceRepositories) {
|
|
if (!data.codeRepository.mainBranchName) {
|
|
throw new BadDataException("Main Branch Name is required");
|
|
}
|
|
|
|
if (!data.codeRepository.organizationName) {
|
|
throw new BadDataException("Organization Name is required");
|
|
}
|
|
|
|
if (!data.codeRepository.repositoryName) {
|
|
throw new BadDataException("Repository Name is required");
|
|
}
|
|
|
|
if (!service.limitNumberOfOpenPullRequestsCount) {
|
|
throw new BadDataException(
|
|
"Limit Number Of Open Pull Requests Count is required",
|
|
);
|
|
}
|
|
|
|
if (
|
|
data.codeRepository.repositoryHostedAt === CodeRepositoryType.GitHub
|
|
) {
|
|
const gitHuhbToken: string | null = GetCodeRepositoryPassword();
|
|
|
|
if (!gitHuhbToken) {
|
|
throw new BadDataException("GitHub Token is required");
|
|
}
|
|
|
|
const pullRequestByService: Array<CopilotPullRequest> =
|
|
data.openPullRequests.filter((pullRequest: CopilotPullRequest) => {
|
|
return (
|
|
pullRequest.serviceRepositoryId?.toString() ===
|
|
service.id?.toString()
|
|
);
|
|
});
|
|
|
|
const numberOfPullRequestForThisService: number =
|
|
pullRequestByService.length;
|
|
|
|
if (
|
|
numberOfPullRequestForThisService <
|
|
service.limitNumberOfOpenPullRequestsCount
|
|
) {
|
|
servicesToImproveCode.push({
|
|
serviceRepository: service,
|
|
numberOfOpenPullRequests: numberOfPullRequestForThisService,
|
|
pullRequests: pullRequestByService,
|
|
});
|
|
logger.info(
|
|
`Service ${service.serviceCatalog?.name} has ${numberOfPullRequestForThisService} open pull requests. Limit is ${service.limitNumberOfOpenPullRequestsCount}. Adding to the list to improve code...`,
|
|
);
|
|
} else {
|
|
logger.warn(
|
|
`Service ${service.serviceCatalog?.name} has ${numberOfPullRequestForThisService} open pull requests. Limit is ${service.limitNumberOfOpenPullRequestsCount}. Skipping...`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return servicesToImproveCode;
|
|
}
|
|
|
|
public static async getCodeRepositoryResult(): Promise<CodeRepositoryResult> {
|
|
if (this.codeRepositoryResult) {
|
|
return this.codeRepositoryResult;
|
|
}
|
|
|
|
logger.info("Fetching Code Repository...");
|
|
|
|
const repositorySecretKey: string | null = GetRepositorySecretKey();
|
|
|
|
if (!repositorySecretKey) {
|
|
throw new BadDataException("Repository Secret Key is required");
|
|
}
|
|
|
|
const url: URL = URL.fromString(
|
|
GetOneUptimeURL().toString() + "/api",
|
|
).addRoute(
|
|
`${new CopilotCodeRepository()
|
|
.getCrudApiPath()
|
|
?.toString()}/get-code-repository/${repositorySecretKey}`,
|
|
);
|
|
|
|
const codeRepositoryResult: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
await API.get(url);
|
|
|
|
if (codeRepositoryResult instanceof HTTPErrorResponse) {
|
|
throw codeRepositoryResult;
|
|
}
|
|
|
|
const codeRepository: CopilotCodeRepository =
|
|
CopilotCodeRepository.fromJSON(
|
|
codeRepositoryResult.data["codeRepository"] as JSONObject,
|
|
CopilotCodeRepository,
|
|
) as CopilotCodeRepository;
|
|
|
|
const servicesRepository: Array<ServiceCopilotCodeRepository> = (
|
|
codeRepositoryResult.data["servicesRepository"] as JSONArray
|
|
).map((serviceRepository: JSONObject) => {
|
|
return ServiceCopilotCodeRepository.fromJSON(
|
|
serviceRepository,
|
|
ServiceCopilotCodeRepository,
|
|
) as ServiceCopilotCodeRepository;
|
|
});
|
|
|
|
if (!codeRepository) {
|
|
throw new BadDataException(
|
|
"Code Repository not found with the secret key provided.",
|
|
);
|
|
}
|
|
|
|
if (!servicesRepository || servicesRepository.length === 0) {
|
|
throw new BadDataException(
|
|
"No services attached to this repository. Please attach services to this repository on OneUptime Dashboard.",
|
|
);
|
|
}
|
|
|
|
logger.info(`Code Repository found: ${codeRepository.name}`);
|
|
|
|
logger.info("Services found in the repository:");
|
|
|
|
servicesRepository.forEach(
|
|
(serviceRepository: ServiceCopilotCodeRepository) => {
|
|
logger.info(`- ${serviceRepository.serviceCatalog?.name}`);
|
|
},
|
|
);
|
|
|
|
this.codeRepositoryResult = {
|
|
codeRepository,
|
|
serviceRepositories: servicesRepository,
|
|
};
|
|
|
|
return this.codeRepositoryResult;
|
|
}
|
|
|
|
public static async getCodeRepository(): Promise<CopilotCodeRepository> {
|
|
if (!this.codeRepositoryResult) {
|
|
const result: CodeRepositoryResult = await this.getCodeRepositoryResult();
|
|
return result.codeRepository;
|
|
}
|
|
|
|
return this.codeRepositoryResult.codeRepository;
|
|
}
|
|
|
|
public static getCodeFileExtentions(): Array<string> {
|
|
const extensions: Array<string> = [
|
|
"ts",
|
|
"js",
|
|
"tsx",
|
|
"jsx",
|
|
"py",
|
|
"go",
|
|
"java",
|
|
"c",
|
|
"cpp",
|
|
"cs",
|
|
"swift",
|
|
"php",
|
|
"rb",
|
|
"rs",
|
|
"kt",
|
|
"dart",
|
|
"sh",
|
|
"pl",
|
|
"lua",
|
|
"r",
|
|
"scala",
|
|
"ts",
|
|
"js",
|
|
"tsx",
|
|
"jsx",
|
|
];
|
|
|
|
return extensions;
|
|
}
|
|
|
|
public static getReadmeFileExtentions(): Array<string> {
|
|
return ["md"];
|
|
}
|
|
}
|