mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Merge branch 'ai-copilot-main'
This commit is contained in:
6
Common/Types/Copilot/CopilotEventStatus.ts
Normal file
6
Common/Types/Copilot/CopilotEventStatus.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
enum CopilotEventStatus {
|
||||
PR_CREATED = 'Pull Request Created', // PR created and waiting for review
|
||||
NO_ACTION_REQUIRED = 'No Action Required', // No PR needed. All is good.
|
||||
}
|
||||
|
||||
export default CopilotEventStatus;
|
||||
20
Common/Types/ServiceCatalog/ServiceLanguage.ts
Normal file
20
Common/Types/ServiceCatalog/ServiceLanguage.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export enum ServiceLanguage {
|
||||
NodeJS = 'NodeJS',
|
||||
React = 'React',
|
||||
Python = 'Python',
|
||||
Ruby = 'Ruby',
|
||||
Go = 'Go',
|
||||
Java = 'Java',
|
||||
PHP = 'PHP',
|
||||
CSharp = 'C#',
|
||||
CPlusPlus = 'C++',
|
||||
Rust = 'Rust',
|
||||
Swift = 'Swift',
|
||||
Kotlin = 'Kotlin',
|
||||
TypeScript = 'TypeScript',
|
||||
JavaScript = 'JavaScript',
|
||||
Shell = 'Shell',
|
||||
Other = 'Other',
|
||||
}
|
||||
|
||||
export default ServiceLanguage;
|
||||
@@ -2,6 +2,7 @@ import UserMiddleware from '../Middleware/UserAuthorization';
|
||||
import CodeRepositoryService, {
|
||||
Service as CodeRepositoryServiceType,
|
||||
} from '../Services/CodeRepositoryService';
|
||||
import CopilotEventService from '../Services/CopilotEventService';
|
||||
import ServiceRepositoryService from '../Services/ServiceRepositoryService';
|
||||
import {
|
||||
ExpressRequest,
|
||||
@@ -14,6 +15,7 @@ import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import CodeRepository from 'Model/Models/CodeRepository';
|
||||
import CopilotEvent from 'Model/Models/CopilotEvent';
|
||||
import ServiceRepository from 'Model/Models/ServiceRepository';
|
||||
|
||||
export default class CodeRepositoryAPI extends BaseAPI<
|
||||
@@ -48,6 +50,9 @@ export default class CodeRepositoryAPI extends BaseAPI<
|
||||
select: {
|
||||
name: true,
|
||||
mainBranchName: true,
|
||||
organizationName: true,
|
||||
repositoryHostedAt: true,
|
||||
repositoryName: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@@ -55,7 +60,9 @@ export default class CodeRepositoryAPI extends BaseAPI<
|
||||
});
|
||||
|
||||
if (!codeRepository) {
|
||||
throw new BadDataException('Code repository not found');
|
||||
throw new BadDataException(
|
||||
'Code repository not found. Secret key is invalid.'
|
||||
);
|
||||
}
|
||||
|
||||
const servicesRepository: Array<ServiceRepository> =
|
||||
@@ -68,6 +75,7 @@ export default class CodeRepositoryAPI extends BaseAPI<
|
||||
serviceCatalog: {
|
||||
name: true,
|
||||
_id: true,
|
||||
serviceLanguage: true,
|
||||
},
|
||||
servicePathInRepository: true,
|
||||
limitNumberOfOpenPullRequestsCount: true,
|
||||
@@ -94,5 +102,93 @@ export default class CodeRepositoryAPI extends BaseAPI<
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.router.get(
|
||||
`${new this.entityType()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/get-copilot-events-by-file/:secretkey`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
const secretkey: string = req.params['secretkey']!;
|
||||
|
||||
if (!secretkey) {
|
||||
throw new BadDataException('Secret key is required');
|
||||
}
|
||||
|
||||
const filePath: string = req.body['filePath']!;
|
||||
|
||||
if (!filePath) {
|
||||
throw new BadDataException('File path is required');
|
||||
}
|
||||
|
||||
const serviceCatalogId: string =
|
||||
req.body['serviceCatalogId']!;
|
||||
|
||||
if (!serviceCatalogId) {
|
||||
throw new BadDataException(
|
||||
'Service catalog id is required'
|
||||
);
|
||||
}
|
||||
|
||||
const codeRepository: CodeRepository | null =
|
||||
await CodeRepositoryService.findOneBy({
|
||||
query: {
|
||||
secretToken: new ObjectID(secretkey),
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!codeRepository) {
|
||||
throw new BadDataException(
|
||||
'Code repository not found. Secret key is invalid.'
|
||||
);
|
||||
}
|
||||
|
||||
const copilotEvents: Array<CopilotEvent> =
|
||||
await CopilotEventService.findBy({
|
||||
query: {
|
||||
codeRepositoryId: codeRepository.id!,
|
||||
filePath: filePath,
|
||||
serviceCatalogId: new ObjectID(
|
||||
serviceCatalogId
|
||||
),
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
codeRepositoryId: true,
|
||||
serviceCatalogId: true,
|
||||
filePath: true,
|
||||
copilotEventStatus: true,
|
||||
copilotEventType: true,
|
||||
createdAt: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
copilotEvents: CopilotEvent.toJSONArray(
|
||||
copilotEvents,
|
||||
CopilotEvent
|
||||
),
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
43
CommonServer/Docs/CodeRepository.md
Normal file
43
CommonServer/Docs/CodeRepository.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Code Repository
|
||||
|
||||
```javascript
|
||||
const branchName: string = 'test-branch-11';
|
||||
|
||||
await CodeRepositoryUtil.createOrCheckoutBranch({
|
||||
serviceRepository: serviceRepository,
|
||||
branchName: branchName,
|
||||
});
|
||||
|
||||
// test code from here.
|
||||
const file: CodeRepositoryFile | undefined =
|
||||
filesInService[Object.keys(filesInService)[0]!];
|
||||
|
||||
await CodeRepositoryUtil.writeToFile({
|
||||
filePath: file!.filePath!,
|
||||
content: 'Hello World',
|
||||
});
|
||||
|
||||
// commit the changes
|
||||
|
||||
await CodeRepositoryUtil.addFilesToGit({
|
||||
filePaths: [file!.filePath!],
|
||||
});
|
||||
|
||||
await CodeRepositoryUtil.commitChanges({
|
||||
message: 'Test commit',
|
||||
});
|
||||
|
||||
await CodeRepositoryUtil.pushChanges({
|
||||
branchName: branchName,
|
||||
serviceRepository: serviceRepository,
|
||||
});
|
||||
|
||||
// create a pull request
|
||||
|
||||
await CodeRepositoryUtil.createPullRequest({
|
||||
title: 'Test PR',
|
||||
body: 'Test PR body',
|
||||
branchName: branchName,
|
||||
serviceRepository: serviceRepository,
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class MigrationName1718285877004 implements MigrationInterface {
|
||||
public name = 'MigrationName1718285877004';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "CopilotEvent" ADD "pullRequestId" character varying`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "CopilotEvent" ADD "copilotEventStatus" character varying NOT NULL`
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "CopilotEvent" DROP COLUMN "copilotEventStatus"`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "CopilotEvent" DROP COLUMN "pullRequestId"`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { MigrationName1718124277321 } from './1718124277321-MigrationName';
|
||||
import { MigrationName1718126316684 } from './1718126316684-MigrationName';
|
||||
import { MigrationName1718188920011 } from './1718188920011-MigrationName';
|
||||
import { MigrationName1718203144945 } from './1718203144945-MigrationName';
|
||||
import { MigrationName1718285877004 } from './1718285877004-MigrationName';
|
||||
|
||||
export default [
|
||||
InitialMigration,
|
||||
@@ -26,4 +27,5 @@ export default [
|
||||
MigrationName1718126316684,
|
||||
MigrationName1718188920011,
|
||||
MigrationName1718203144945,
|
||||
MigrationName1718285877004,
|
||||
];
|
||||
|
||||
@@ -1,29 +1,238 @@
|
||||
import Execute from '../Execute';
|
||||
import LocalFile from '../LocalFile';
|
||||
import logger from '../Logger';
|
||||
import CodeRepositoryFile from './CodeRepositoryFile';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
|
||||
export default class CodeRepositoryUtil {
|
||||
public static async createOrCheckoutBranch(data: {
|
||||
repoPath: string;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout ${data.branchName} || git checkout -b ${data.branchName}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
// discard all changes in the working directory
|
||||
public static async discardChanges(data: {
|
||||
repoPath: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout .`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async writeToFile(data: {
|
||||
filePath: string;
|
||||
repoPath: string;
|
||||
content: string;
|
||||
}): Promise<void> {
|
||||
const totalPath: string = LocalFile.sanitizeFilePath(
|
||||
`${data.repoPath}/${data.filePath}`
|
||||
);
|
||||
|
||||
const command: string = `echo "${data.content}" > ${totalPath}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async createDirectory(data: {
|
||||
repoPath: string;
|
||||
directoryPath: string;
|
||||
}): Promise<void> {
|
||||
const totalPath: string = LocalFile.sanitizeFilePath(
|
||||
`${data.repoPath}/${data.directoryPath}`
|
||||
);
|
||||
|
||||
const command: string = `mkdir ${totalPath}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async deleteFile(data: {
|
||||
repoPath: string;
|
||||
filePath: string;
|
||||
}): Promise<void> {
|
||||
const totalPath: string = LocalFile.sanitizeFilePath(
|
||||
`${data.repoPath}/${data.filePath}`
|
||||
);
|
||||
|
||||
const command: string = `rm ${totalPath}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async deleteDirectory(data: {
|
||||
repoPath: string;
|
||||
directoryPath: string;
|
||||
}): Promise<void> {
|
||||
const totalPath: string = LocalFile.sanitizeFilePath(
|
||||
`${data.repoPath}/${data.directoryPath}`
|
||||
);
|
||||
|
||||
const command: string = `rm -rf ${totalPath}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async createBranch(data: {
|
||||
repoPath: string;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout -b ${data.branchName}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async checkoutBranch(data: {
|
||||
repoPath: string;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git checkout ${data.branchName}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async addFilesToGit(data: {
|
||||
repoPath: string;
|
||||
filePaths: Array<string>;
|
||||
}): Promise<void> {
|
||||
const filePaths: Array<string> = data.filePaths.map(
|
||||
(filePath: string) => {
|
||||
if (filePath.startsWith('/')) {
|
||||
// remove the leading slash and return
|
||||
return filePath.substring(1);
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
);
|
||||
|
||||
const command: string = `cd ${
|
||||
data.repoPath
|
||||
} && git add ${filePaths.join(' ')}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async setUsername(data: {
|
||||
repoPath: string;
|
||||
username: string;
|
||||
}): Promise<void> {
|
||||
const command: string = `cd ${data.repoPath} && git config user.name "${data.username}"`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async commitChanges(data: {
|
||||
repoPath: string;
|
||||
message: string;
|
||||
username: string;
|
||||
}): Promise<void> {
|
||||
// set the username and email
|
||||
|
||||
await this.setUsername({
|
||||
repoPath: data.repoPath,
|
||||
username: data.username,
|
||||
});
|
||||
|
||||
const command: string = `cd ${data.repoPath} && git commit -m "${data.message}"`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const stdout: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(stdout);
|
||||
}
|
||||
|
||||
public static async getGitCommitHashForFile(data: {
|
||||
repoPath: string;
|
||||
filePath: string;
|
||||
}): Promise<string> {
|
||||
if (!data.filePath.startsWith('/')) {
|
||||
data.filePath = '/' + data.filePath;
|
||||
}
|
||||
|
||||
if (!data.repoPath.startsWith('/')) {
|
||||
data.repoPath = '/' + data.repoPath;
|
||||
}
|
||||
|
||||
const { repoPath, filePath } = data;
|
||||
|
||||
return await Execute.executeCommand(
|
||||
`cd ${repoPath} && git log -1 --pretty=format:"%H" "${filePath}"`
|
||||
);
|
||||
const command: string = `cd ${repoPath} && git log -1 --pretty=format:"%H" ".${filePath}"`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const hash: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(hash);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static async getFilesInDirectory(data: {
|
||||
directoryPath: string;
|
||||
repoPath: string;
|
||||
acceptedFileExtensions?: Array<string>;
|
||||
ignoreFilesOrDirectories: Array<string>;
|
||||
}): Promise<{
|
||||
files: Dictionary<CodeRepositoryFile>;
|
||||
subDirectories: Array<string>;
|
||||
}> {
|
||||
if (!data.directoryPath.startsWith('/')) {
|
||||
data.directoryPath = '/' + data.directoryPath;
|
||||
}
|
||||
|
||||
if (!data.repoPath.startsWith('/')) {
|
||||
data.repoPath = '/' + data.repoPath;
|
||||
}
|
||||
|
||||
const { directoryPath, repoPath } = data;
|
||||
|
||||
const totalPath: string = `${repoPath}/${directoryPath}`;
|
||||
let totalPath: string = `${repoPath}/${directoryPath}`;
|
||||
|
||||
totalPath = LocalFile.sanitizeFilePath(totalPath); // clean up the path
|
||||
|
||||
const files: Dictionary<CodeRepositoryFile> = {};
|
||||
const output: string = await Execute.executeCommand(`ls ${totalPath}`);
|
||||
@@ -37,23 +246,55 @@ export default class CodeRepositoryUtil {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isDirectory: boolean = (
|
||||
await Execute.executeCommand(`file "${totalPath}/${fileName}"`)
|
||||
).includes('directory');
|
||||
const filePath: string = LocalFile.sanitizeFilePath(
|
||||
`${directoryPath}/${fileName}`
|
||||
);
|
||||
|
||||
if (isDirectory) {
|
||||
subDirectories.push(`${totalPath}/${fileName}`);
|
||||
if (data.ignoreFilesOrDirectories.includes(fileName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath: string = `${totalPath}/${fileName}`;
|
||||
const isDirectory: boolean = (
|
||||
await Execute.executeCommand(
|
||||
`file "${LocalFile.sanitizeFilePath(
|
||||
`${totalPath}/${fileName}`
|
||||
)}"`
|
||||
)
|
||||
).includes('directory');
|
||||
|
||||
if (isDirectory) {
|
||||
subDirectories.push(
|
||||
LocalFile.sanitizeFilePath(`${directoryPath}/${fileName}`)
|
||||
);
|
||||
continue;
|
||||
} else if (
|
||||
data.acceptedFileExtensions &&
|
||||
data.acceptedFileExtensions.length > 0
|
||||
) {
|
||||
let shouldSkip: boolean = true;
|
||||
|
||||
for (const fileExtension of data.acceptedFileExtensions) {
|
||||
if (fileName.endsWith(fileExtension)) {
|
||||
shouldSkip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSkip) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const gitCommitHash: string = await this.getGitCommitHashForFile({
|
||||
filePath,
|
||||
repoPath,
|
||||
});
|
||||
|
||||
const fileExtension: string = fileName.split('.').pop() || '';
|
||||
files[filePath] = {
|
||||
filePath,
|
||||
filePath: LocalFile.sanitizeFilePath(
|
||||
`${directoryPath}/${fileName}`
|
||||
),
|
||||
gitCommitHash,
|
||||
fileExtension,
|
||||
fileName,
|
||||
@@ -69,6 +310,8 @@ export default class CodeRepositoryUtil {
|
||||
public static async getFilesInDirectoryRecursive(data: {
|
||||
repoPath: string;
|
||||
directoryPath: string;
|
||||
acceptedFileExtensions: Array<string>;
|
||||
ignoreFilesOrDirectories: Array<string>;
|
||||
}): Promise<Dictionary<CodeRepositoryFile>> {
|
||||
let files: Dictionary<CodeRepositoryFile> = {};
|
||||
|
||||
@@ -76,6 +319,8 @@ export default class CodeRepositoryUtil {
|
||||
await this.getFilesInDirectory({
|
||||
directoryPath: data.directoryPath,
|
||||
repoPath: data.repoPath,
|
||||
acceptedFileExtensions: data.acceptedFileExtensions,
|
||||
ignoreFilesOrDirectories: data.ignoreFilesOrDirectories,
|
||||
});
|
||||
|
||||
files = {
|
||||
@@ -89,6 +334,8 @@ export default class CodeRepositoryUtil {
|
||||
...(await this.getFilesInDirectoryRecursive({
|
||||
repoPath: data.repoPath,
|
||||
directoryPath: subDirectory,
|
||||
acceptedFileExtensions: data.acceptedFileExtensions,
|
||||
ignoreFilesOrDirectories: data.ignoreFilesOrDirectories,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import Execute from '../../Execute';
|
||||
import logger from '../../Logger';
|
||||
import HostedCodeRepository from '../HostedCodeRepository/HostedCodeRepository';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
@@ -135,9 +137,7 @@ export default class GitHubUtil extends HostedCodeRepository {
|
||||
|
||||
// Fetch all pull requests by paginating through the results
|
||||
// 100 pull requests per page is the limit of the GitHub API
|
||||
while (pullRequests.length === page * 100) {
|
||||
allPullRequests.push(...pullRequests);
|
||||
page++;
|
||||
while (pullRequests.length === page * 100 || page === 1) {
|
||||
pullRequests = await this.getPullRequestsByPage({
|
||||
organizationName: data.organizationName,
|
||||
repositoryName: data.repositoryName,
|
||||
@@ -145,8 +145,94 @@ export default class GitHubUtil extends HostedCodeRepository {
|
||||
baseBranchName: data.baseBranchName,
|
||||
page: page,
|
||||
});
|
||||
page++;
|
||||
allPullRequests.push(...pullRequests);
|
||||
}
|
||||
|
||||
return allPullRequests;
|
||||
}
|
||||
|
||||
public override async addRemote(data: {
|
||||
remoteName: string;
|
||||
organizationName: string;
|
||||
repositoryName: string;
|
||||
}): Promise<void> {
|
||||
const url: URL = URL.fromString(
|
||||
`https://github.com/${data.organizationName}/${data.repositoryName}.git`
|
||||
);
|
||||
|
||||
const command: string = `git remote add ${
|
||||
data.remoteName
|
||||
} ${url.toString()}`;
|
||||
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const result: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(result);
|
||||
}
|
||||
|
||||
public override async pushChanges(data: {
|
||||
branchName: string;
|
||||
organizationName: string;
|
||||
repositoryName: string;
|
||||
repoPath: string;
|
||||
}): Promise<void> {
|
||||
const branchName: string = data.branchName;
|
||||
|
||||
const username: string = this.username;
|
||||
const password: string = this.authToken;
|
||||
|
||||
logger.debug(
|
||||
'Pushing changes to remote repository with username: ' + username
|
||||
);
|
||||
|
||||
const command: string = `cd ${data.repoPath} && git push -u https://${username}:${password}@github.com/${data.organizationName}/${data.repositoryName}.git ${branchName}`;
|
||||
logger.debug('Executing command: ' + command);
|
||||
|
||||
const result: string = await Execute.executeCommand(command);
|
||||
|
||||
logger.debug(result);
|
||||
}
|
||||
|
||||
public override async createPullRequest(data: {
|
||||
baseBranchName: string;
|
||||
headBranchName: string;
|
||||
organizationName: string;
|
||||
repositoryName: string;
|
||||
title: string;
|
||||
body: string;
|
||||
}): Promise<PullRequest> {
|
||||
const gitHubToken: string = this.authToken;
|
||||
|
||||
const url: URL = URL.fromString(
|
||||
`https://api.github.com/repos/${data.organizationName}/${data.repositoryName}/pulls`
|
||||
);
|
||||
|
||||
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post(
|
||||
url,
|
||||
{
|
||||
base: data.baseBranchName,
|
||||
head: data.headBranchName,
|
||||
title: data.title,
|
||||
body: data.body,
|
||||
},
|
||||
{
|
||||
Authorization: `Bearer ${gitHubToken}`,
|
||||
Accept: 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
}
|
||||
);
|
||||
|
||||
if (result instanceof HTTPErrorResponse) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
return this.getPullRequestFromJSONObject({
|
||||
pullRequest: result.data,
|
||||
organizationName: data.organizationName,
|
||||
repositoryName: data.repositoryName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,22 @@ import NotImplementedException from 'Common/Types/Exception/NotImplementedExcept
|
||||
import ServiceRepository from 'Model/Models/ServiceRepository';
|
||||
|
||||
export default class HostedCodeRepository {
|
||||
public constructor(data: { authToken: string }) {
|
||||
public constructor(data: { authToken: string; username: string }) {
|
||||
if (!data.authToken) {
|
||||
throw new BadDataException('authToken is required');
|
||||
}
|
||||
|
||||
if (!data.username) {
|
||||
throw new BadDataException('username is required');
|
||||
}
|
||||
|
||||
this.username = data.username;
|
||||
|
||||
this.authToken = data.authToken;
|
||||
}
|
||||
|
||||
public authToken: string = '';
|
||||
public username: string = '';
|
||||
|
||||
public async getNumberOfPullRequestsExistForService(data: {
|
||||
serviceRepository: ServiceRepository;
|
||||
@@ -78,4 +85,31 @@ export default class HostedCodeRepository {
|
||||
}): Promise<Array<PullRequest>> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async createPullRequest(_data: {
|
||||
baseBranchName: string;
|
||||
headBranchName: string;
|
||||
organizationName: string;
|
||||
repositoryName: string;
|
||||
title: string;
|
||||
body: string;
|
||||
}): Promise<PullRequest> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async pushChanges(_data: {
|
||||
branchName: string;
|
||||
organizationName: string;
|
||||
repositoryName: string;
|
||||
}): Promise<void> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async addRemote(_data: {
|
||||
remoteName: string;
|
||||
organizationName: string;
|
||||
repositoryName: string;
|
||||
}): Promise<void> {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@ import { PromiseRejectErrorFunction } from 'Common/Types/FunctionTypes';
|
||||
import fs from 'fs';
|
||||
|
||||
export default class LocalFile {
|
||||
public static sanitizeFilePath(filePath: string): string {
|
||||
// remove double slashes
|
||||
return filePath.replace(/\/\//g, '/');
|
||||
}
|
||||
|
||||
public static async makeDirectory(path: string): Promise<void> {
|
||||
return new Promise(
|
||||
(resolve: VoidFunction, reject: PromiseRejectErrorFunction) => {
|
||||
|
||||
@@ -2,3 +2,4 @@ ONEUPTIME_URL=https://oneuptime.com
|
||||
ONEUPTIME_REPOSITORY_SECRET_KEY=your-repository-secret-key
|
||||
ONEUPTIME_LOCAL_REPOSITORY_PATH=/repository
|
||||
GITHUB_TOKEN=
|
||||
GITHUB_USERNAME=
|
||||
|
||||
@@ -24,3 +24,8 @@ export const GetGitHubToken: GetStringOrNullFunction = (): string | null => {
|
||||
const token: string | null = process.env['GITHUB_TOKEN'] || null;
|
||||
return token;
|
||||
};
|
||||
|
||||
export const GetGitHubUsername: GetStringOrNullFunction = (): string | null => {
|
||||
const username: string | null = process.env['GITHUB_USERNAME'] || null;
|
||||
return username;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { CodeRepositoryResult } from './Utils/CodeRepository';
|
||||
import CodeRepositoryUtil, {
|
||||
CodeRepositoryResult,
|
||||
} from './Utils/CodeRepository';
|
||||
import InitUtil from './Utils/Init';
|
||||
import ServiceRepositoryUtil from './Utils/ServiceRepository';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import { PromiseVoidFunction } from 'Common/Types/FunctionTypes';
|
||||
import CodeRepositoryFile from 'CommonServer/Utils/CodeRepository/CodeRepositoryFile';
|
||||
@@ -32,8 +35,25 @@ init()
|
||||
.then(() => {
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
.catch(async (error: Error | HTTPErrorResponse) => {
|
||||
try {
|
||||
await CodeRepositoryUtil.discardChanges();
|
||||
|
||||
// change back to main branch.
|
||||
await CodeRepositoryUtil.checkoutMainBranch();
|
||||
} catch (e) {
|
||||
// do nothing.
|
||||
}
|
||||
|
||||
logger.error('Error in starting OneUptime Copilot: ');
|
||||
logger.error(error);
|
||||
|
||||
if (error instanceof HTTPErrorResponse) {
|
||||
logger.error(error.message);
|
||||
} else if (error instanceof Error) {
|
||||
logger.error(error.message);
|
||||
} else {
|
||||
logger.error(error);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {
|
||||
GetGitHubToken,
|
||||
GetGitHubUsername,
|
||||
GetLocalRepositoryPath,
|
||||
GetOneUptimeURL,
|
||||
GetRepositorySecretKey,
|
||||
} from '../Config';
|
||||
@@ -7,10 +9,12 @@ 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 'CommonServer/Utils/CodeRepository/CodeRepository';
|
||||
import GitHubUtil from 'CommonServer/Utils/CodeRepository/GitHub/GitHub';
|
||||
import logger from 'CommonServer/Utils/Logger';
|
||||
import CodeRepositoryModel from 'Model/Models/CodeRepository';
|
||||
@@ -23,6 +27,249 @@ export interface CodeRepositoryResult {
|
||||
|
||||
export default class CodeRepositoryUtil {
|
||||
public static codeRepositoryResult: CodeRepositoryResult | null = null;
|
||||
public static gitHubUtil: GitHubUtil | null = null;
|
||||
|
||||
public static getGitHubUtil(): GitHubUtil {
|
||||
if (!this.gitHubUtil) {
|
||||
const gitHubToken: string | null = GetGitHubToken();
|
||||
|
||||
const gitHubUsername: string | null = GetGitHubUsername();
|
||||
|
||||
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 getBranchName(data: {
|
||||
branchName: string;
|
||||
serviceRepository: ServiceRepository;
|
||||
}): string {
|
||||
if (!data.serviceRepository.serviceCatalog) {
|
||||
throw new BadDataException('Service Catalog is required');
|
||||
}
|
||||
|
||||
if (!data.serviceRepository.serviceCatalog.name) {
|
||||
throw new BadDataException('Service Catalog Name is required');
|
||||
}
|
||||
|
||||
return (
|
||||
'oneuptime-' +
|
||||
data.serviceRepository.serviceCatalog?.name?.toLowerCase() +
|
||||
'-' +
|
||||
data.branchName
|
||||
);
|
||||
}
|
||||
|
||||
public static async createBranch(data: {
|
||||
branchName: string;
|
||||
serviceRepository: ServiceRepository;
|
||||
}): Promise<void> {
|
||||
const branchName: string = this.getBranchName({
|
||||
branchName: data.branchName,
|
||||
serviceRepository: data.serviceRepository,
|
||||
});
|
||||
|
||||
await CodeRepositoryServerUtil.createBranch({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
branchName: branchName,
|
||||
});
|
||||
}
|
||||
|
||||
public static async createOrCheckoutBranch(data: {
|
||||
serviceRepository: ServiceRepository;
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
const branchName: string = this.getBranchName({
|
||||
branchName: data.branchName,
|
||||
serviceRepository: data.serviceRepository,
|
||||
});
|
||||
|
||||
await CodeRepositoryServerUtil.createOrCheckoutBranch({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
branchName: branchName,
|
||||
});
|
||||
}
|
||||
|
||||
public static async writeToFile(data: {
|
||||
filePath: string;
|
||||
content: string;
|
||||
}): Promise<void> {
|
||||
await CodeRepositoryServerUtil.writeToFile({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
filePath: data.filePath,
|
||||
content: data.content,
|
||||
});
|
||||
}
|
||||
|
||||
public static async createDirectory(data: {
|
||||
directoryPath: string;
|
||||
}): Promise<void> {
|
||||
await CodeRepositoryServerUtil.createDirectory({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
directoryPath: data.directoryPath,
|
||||
});
|
||||
}
|
||||
|
||||
public static async deleteFile(data: { filePath: string }): Promise<void> {
|
||||
await CodeRepositoryServerUtil.deleteFile({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
filePath: data.filePath,
|
||||
});
|
||||
}
|
||||
|
||||
public static async deleteDirectory(data: {
|
||||
directoryPath: string;
|
||||
}): Promise<void> {
|
||||
await CodeRepositoryServerUtil.deleteDirectory({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
directoryPath: data.directoryPath,
|
||||
});
|
||||
}
|
||||
|
||||
public static async discardChanges(): Promise<void> {
|
||||
await CodeRepositoryServerUtil.discardChanges({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
});
|
||||
}
|
||||
|
||||
public static async checkoutBranch(data: {
|
||||
branchName: string;
|
||||
}): Promise<void> {
|
||||
await CodeRepositoryServerUtil.checkoutBranch({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
branchName: data.branchName,
|
||||
});
|
||||
}
|
||||
|
||||
public static async checkoutMainBranch(): Promise<void> {
|
||||
const codeRepository: CodeRepositoryModel =
|
||||
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: 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 = GetGitHubUsername();
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
throw new BadDataException('Username is required');
|
||||
}
|
||||
|
||||
await CodeRepositoryServerUtil.commitChanges({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
message: data.message,
|
||||
username: username,
|
||||
});
|
||||
}
|
||||
|
||||
public static async pushChanges(data: {
|
||||
branchName: string;
|
||||
serviceRepository: ServiceRepository;
|
||||
}): Promise<void> {
|
||||
const branchName: string = this.getBranchName({
|
||||
branchName: data.branchName,
|
||||
serviceRepository: data.serviceRepository,
|
||||
});
|
||||
|
||||
const codeRepository: CodeRepositoryModel =
|
||||
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: GetLocalRepositoryPath(),
|
||||
branchName: branchName,
|
||||
organizationName: codeRepository.organizationName,
|
||||
repositoryName: codeRepository.repositoryName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static async createPullRequest(data: {
|
||||
branchName: string;
|
||||
title: string;
|
||||
body: string;
|
||||
serviceRepository: ServiceRepository;
|
||||
}): Promise<PullRequest> {
|
||||
const branchName: string = this.getBranchName({
|
||||
branchName: data.branchName,
|
||||
serviceRepository: data.serviceRepository,
|
||||
});
|
||||
|
||||
const codeRepository: CodeRepositoryModel =
|
||||
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: CodeRepositoryModel;
|
||||
@@ -60,15 +307,16 @@ export default class CodeRepositoryUtil {
|
||||
}
|
||||
|
||||
const numberOfPullRequestForThisService: number =
|
||||
await new GitHubUtil({
|
||||
authToken: gitHuhbToken,
|
||||
}).getNumberOfPullRequestsExistForService({
|
||||
serviceRepository: service,
|
||||
pullRequestState: PullRequestState.Open,
|
||||
baseBranchName: data.codeRepository.mainBranchName,
|
||||
organizationName: data.codeRepository.organizationName,
|
||||
repositoryName: data.codeRepository.repositoryName,
|
||||
});
|
||||
await this.getGitHubUtil().getNumberOfPullRequestsExistForService(
|
||||
{
|
||||
serviceRepository: service,
|
||||
pullRequestState: PullRequestState.Open,
|
||||
baseBranchName: data.codeRepository.mainBranchName,
|
||||
organizationName:
|
||||
data.codeRepository.organizationName,
|
||||
repositoryName: data.codeRepository.repositoryName,
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
numberOfPullRequestForThisService <
|
||||
|
||||
165
Copilot/Utils/ServiceFileTypes.ts
Normal file
165
Copilot/Utils/ServiceFileTypes.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import ServiceLanguage from 'Common/Types/ServiceCatalog/ServiceLanguage';
|
||||
|
||||
export default class ServiceFileTypesUtil {
|
||||
private static getCommonDirectoriesToIgnore(): string[] {
|
||||
return [
|
||||
'node_modules',
|
||||
'.git',
|
||||
'build',
|
||||
'dist',
|
||||
'coverage',
|
||||
'logs',
|
||||
'tmp',
|
||||
'temp',
|
||||
'temporal',
|
||||
'tempfiles',
|
||||
'tempfiles',
|
||||
];
|
||||
}
|
||||
|
||||
private static getCommonFilesToIgnore(): string[] {
|
||||
return ['.DS_Store', 'Thumbs.db', '.gitignore', '.gitattributes'];
|
||||
}
|
||||
|
||||
public static getCommonFilesToIgnoreByServiceLanguage(
|
||||
serviceLanguage: ServiceLanguage
|
||||
): string[] {
|
||||
let filesToIgnore: string[] = [];
|
||||
|
||||
switch (serviceLanguage) {
|
||||
case ServiceLanguage.NodeJS:
|
||||
filesToIgnore = ['package-lock.json'];
|
||||
break;
|
||||
case ServiceLanguage.Python:
|
||||
filesToIgnore = ['__pycache__'];
|
||||
break;
|
||||
case ServiceLanguage.Ruby:
|
||||
filesToIgnore = ['Gemfile.lock'];
|
||||
break;
|
||||
case ServiceLanguage.Go:
|
||||
filesToIgnore = ['go.sum', 'go.mod'];
|
||||
break;
|
||||
case ServiceLanguage.Java:
|
||||
filesToIgnore = ['pom.xml'];
|
||||
break;
|
||||
case ServiceLanguage.PHP:
|
||||
filesToIgnore = ['composer.lock'];
|
||||
break;
|
||||
case ServiceLanguage.CSharp:
|
||||
filesToIgnore = ['packages', 'bin', 'obj'];
|
||||
break;
|
||||
case ServiceLanguage.CPlusPlus:
|
||||
filesToIgnore = [
|
||||
'build',
|
||||
'CMakeFiles',
|
||||
'CMakeCache.txt',
|
||||
'Makefile',
|
||||
];
|
||||
break;
|
||||
case ServiceLanguage.Rust:
|
||||
filesToIgnore = ['Cargo.lock'];
|
||||
break;
|
||||
case ServiceLanguage.Swift:
|
||||
filesToIgnore = ['Podfile.lock'];
|
||||
break;
|
||||
case ServiceLanguage.Kotlin:
|
||||
filesToIgnore = [
|
||||
'gradle',
|
||||
'build',
|
||||
'gradlew',
|
||||
'gradlew.bat',
|
||||
'gradle.properties',
|
||||
];
|
||||
break;
|
||||
case ServiceLanguage.TypeScript:
|
||||
filesToIgnore = ['node_modules', 'package-lock.json'];
|
||||
break;
|
||||
case ServiceLanguage.JavaScript:
|
||||
filesToIgnore = ['node_modules', 'package-lock.json'];
|
||||
break;
|
||||
case ServiceLanguage.Shell:
|
||||
filesToIgnore = [];
|
||||
break;
|
||||
case ServiceLanguage.React:
|
||||
filesToIgnore = ['node_modules', 'package-lock.json'];
|
||||
break;
|
||||
case ServiceLanguage.Other:
|
||||
filesToIgnore = [];
|
||||
break;
|
||||
default:
|
||||
filesToIgnore = [];
|
||||
}
|
||||
|
||||
return filesToIgnore
|
||||
.concat(this.getCommonFilesToIgnore())
|
||||
.concat(this.getCommonDirectoriesToIgnore());
|
||||
}
|
||||
|
||||
private static getCommonFilesExtentions(): string[] {
|
||||
// return markdown, dockerfile, etc.
|
||||
return ['.md', 'dockerfile', '.yml', '.yaml', '.sh', '.gitignore'];
|
||||
}
|
||||
|
||||
public static getFileExtentionsByServiceLanguage(
|
||||
serviceLanguage: ServiceLanguage
|
||||
): string[] {
|
||||
let fileExtentions: Array<string> = [];
|
||||
|
||||
switch (serviceLanguage) {
|
||||
case ServiceLanguage.NodeJS:
|
||||
fileExtentions = ['.js', '.ts', '.json', '.mjs'];
|
||||
break;
|
||||
case ServiceLanguage.Python:
|
||||
fileExtentions = ['.py'];
|
||||
break;
|
||||
case ServiceLanguage.Ruby:
|
||||
fileExtentions = ['.rb'];
|
||||
break;
|
||||
case ServiceLanguage.Go:
|
||||
fileExtentions = ['.go'];
|
||||
break;
|
||||
case ServiceLanguage.Java:
|
||||
fileExtentions = ['.java'];
|
||||
break;
|
||||
case ServiceLanguage.PHP:
|
||||
fileExtentions = ['.php'];
|
||||
break;
|
||||
case ServiceLanguage.CSharp:
|
||||
fileExtentions = ['.cs'];
|
||||
break;
|
||||
case ServiceLanguage.CPlusPlus:
|
||||
fileExtentions = ['.cpp', '.c'];
|
||||
break;
|
||||
case ServiceLanguage.Rust:
|
||||
fileExtentions = ['.rs'];
|
||||
break;
|
||||
case ServiceLanguage.Swift:
|
||||
fileExtentions = ['.swift'];
|
||||
break;
|
||||
case ServiceLanguage.Kotlin:
|
||||
fileExtentions = ['.kt', '.kts'];
|
||||
break;
|
||||
case ServiceLanguage.TypeScript:
|
||||
fileExtentions = ['.ts', '.tsx'];
|
||||
break;
|
||||
case ServiceLanguage.JavaScript:
|
||||
fileExtentions = ['.js', '.jsx'];
|
||||
break;
|
||||
case ServiceLanguage.Shell:
|
||||
fileExtentions = ['.sh'];
|
||||
break;
|
||||
case ServiceLanguage.React:
|
||||
fileExtentions = ['.js', '.ts', '.jsx', '.tsx'];
|
||||
break;
|
||||
case ServiceLanguage.Other:
|
||||
fileExtentions = [];
|
||||
break;
|
||||
default:
|
||||
fileExtentions = [];
|
||||
}
|
||||
|
||||
// add common files extentions
|
||||
|
||||
return fileExtentions.concat(this.getCommonFilesExtentions());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { GetLocalRepositoryPath } from '../Config';
|
||||
import ServiceFileTypesUtil from './ServiceFileTypes';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import CodeRepositoryCommonServerUtil from 'CommonServer/Utils/CodeRepository/CodeRepository';
|
||||
import CodeRepositoryFile from 'CommonServer/Utils/CodeRepository/CodeRepositoryFile';
|
||||
import ServiceRepository from 'Model/Models/ServiceRepository';
|
||||
@@ -10,10 +12,24 @@ export default class ServiceRepositoryUtil {
|
||||
}): Promise<Dictionary<CodeRepositoryFile>> {
|
||||
const { serviceRepository } = data;
|
||||
|
||||
if (!serviceRepository.serviceCatalog?.serviceLanguage) {
|
||||
throw new BadDataException(
|
||||
'Service language is not defined in the service catalog'
|
||||
);
|
||||
}
|
||||
|
||||
const allFiles: Dictionary<CodeRepositoryFile> =
|
||||
await CodeRepositoryCommonServerUtil.getFilesInDirectoryRecursive({
|
||||
repoPath: GetLocalRepositoryPath(),
|
||||
directoryPath: serviceRepository.servicePathInRepository || '.',
|
||||
acceptedFileExtensions:
|
||||
ServiceFileTypesUtil.getFileExtentionsByServiceLanguage(
|
||||
serviceRepository.serviceCatalog!.serviceLanguage!
|
||||
),
|
||||
ignoreFilesOrDirectories:
|
||||
ServiceFileTypesUtil.getCommonFilesToIgnoreByServiceLanguage(
|
||||
serviceRepository.serviceCatalog!.serviceLanguage!
|
||||
),
|
||||
});
|
||||
|
||||
return allFiles;
|
||||
|
||||
@@ -5,6 +5,7 @@ import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import PageComponentProps from '../PageComponentProps';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ServiceLanguage from 'Common/Types/ServiceCatalog/ServiceLanguage';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
@@ -12,7 +13,7 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Label from 'Model/Models/Label';
|
||||
import ServiceCatalog, { ServiceLanguage } from 'Model/Models/ServiceCatalog';
|
||||
import ServiceCatalog from 'Model/Models/ServiceCatalog';
|
||||
import React, { Fragment, FunctionComponent, ReactElement } from 'react';
|
||||
|
||||
const ServiceCatalogPage: FunctionComponent<PageComponentProps> = (
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import LabelsElement from '../../../Components/Label/Labels';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import ServiceLanguage from 'Common/Types/ServiceCatalog/ServiceLanguage';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Label from 'Model/Models/Label';
|
||||
import ServiceCatalog, { ServiceLanguage } from 'Model/Models/ServiceCatalog';
|
||||
import ServiceCatalog from 'Model/Models/ServiceCatalog';
|
||||
import React, { Fragment, FunctionComponent, ReactElement } from 'react';
|
||||
|
||||
const StatusPageView: FunctionComponent<PageComponentProps> = (
|
||||
|
||||
@@ -5,6 +5,7 @@ import ServiceRepository from './ServiceRepository';
|
||||
import User from './User';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import CopilotEventStatus from 'Common/Types/Copilot/CopilotEventStatus';
|
||||
import CopilotEventType from 'Common/Types/Copilot/CopilotEventType';
|
||||
import ColumnAccessControl from 'Common/Types/Database/AccessControl/ColumnAccessControl';
|
||||
import TableAccessControl from 'Common/Types/Database/AccessControl/TableAccessControl';
|
||||
@@ -293,6 +294,7 @@ export default class CopilotEvent extends BaseModel {
|
||||
@TableColumn({
|
||||
type: TableColumnType.LongText,
|
||||
title: 'File Path in Code Repository',
|
||||
required: true,
|
||||
description:
|
||||
'File Path in Code Repository where this event was triggered',
|
||||
})
|
||||
@@ -461,4 +463,50 @@ export default class CopilotEvent extends BaseModel {
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public serviceRepositoryId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadCopilotEvent,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ShortText,
|
||||
required: false,
|
||||
isDefaultValueColumn: false,
|
||||
title: 'Pull Request ID',
|
||||
description:
|
||||
'ID of Pull Request in the repository where this event was executed and then PR was created.',
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
nullable: true,
|
||||
})
|
||||
public pullRequestId?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadCopilotEvent,
|
||||
],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ShortText,
|
||||
title: 'Copilot Event Status',
|
||||
description:
|
||||
'Status of Copilot Event that was triggered for this file in Code Repository',
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ShortText,
|
||||
nullable: false,
|
||||
})
|
||||
public copilotEventStatus?: CopilotEventStatus = undefined;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import UniqueColumnBy from 'Common/Types/Database/UniqueColumnBy';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Permission from 'Common/Types/Permission';
|
||||
import ServiceLanguage from 'Common/Types/ServiceCatalog/ServiceLanguage';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
@@ -33,24 +34,6 @@ import {
|
||||
ManyToOne,
|
||||
} from 'typeorm';
|
||||
|
||||
export enum ServiceLanguage {
|
||||
NodeJS = 'NodeJS',
|
||||
Python = 'Python',
|
||||
Ruby = 'Ruby',
|
||||
Go = 'Go',
|
||||
Java = 'Java',
|
||||
PHP = 'PHP',
|
||||
CSharp = 'C#',
|
||||
CPlusPlus = 'C++',
|
||||
Rust = 'Rust',
|
||||
Swift = 'Swift',
|
||||
Kotlin = 'Kotlin',
|
||||
TypeScript = 'TypeScript',
|
||||
JavaScript = 'JavaScript',
|
||||
Shell = 'Shell',
|
||||
Other = 'Other',
|
||||
}
|
||||
|
||||
@AccessControlColumn('labels')
|
||||
@EnableDocumentation()
|
||||
@TenantColumn('projectId')
|
||||
|
||||
Reference in New Issue
Block a user