mirror of
https://github.com/kiegroup/git-backporting.git
synced 2025-06-30 06:33:47 +00:00
project setup
This commit is contained in:
commit
05d156a5b0
39 changed files with 14823 additions and 0 deletions
110
src/service/git/git-cli.ts
Normal file
110
src/service/git/git-cli.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
import LoggerService from "@gb/service/logger/logger-service";
|
||||
import LoggerServiceFactory from "@gb/service/logger/logger-service-factory";
|
||||
import simpleGit, { SimpleGit } from "simple-git";
|
||||
import fs from "fs";
|
||||
|
||||
/**
|
||||
* Command line git commands executor service
|
||||
*/
|
||||
export default class GitCLIService {
|
||||
|
||||
private readonly logger: LoggerService;
|
||||
|
||||
constructor() {
|
||||
this.logger = LoggerServiceFactory.getLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pre-configured SimpleGit instance able to execute commands from current
|
||||
* directory or the provided one
|
||||
* @param cwd [optional] current working directory
|
||||
* @returns {SimpleGit}
|
||||
*/
|
||||
private git(cwd?: string): SimpleGit {
|
||||
const gitConfig = { ...(cwd ? { baseDir: cwd } : {})};
|
||||
return simpleGit(gitConfig).addConfig("user.name", "Github").addConfig("user.email", "noreply@github.com");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the git version
|
||||
* @returns {Promise<string | undefined>}
|
||||
*/
|
||||
async version(): Promise<string | undefined> {
|
||||
const rawOutput = await this.git().raw("version");
|
||||
const match = rawOutput.match(/(\d+\.\d+(\.\d+)?)/);
|
||||
return match ? match[1] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a git repository
|
||||
* @param from url or path from which the repository should be cloned from
|
||||
* @param to location at which the repository should be cloned at
|
||||
* @param branch branch which should be cloned
|
||||
*/
|
||||
async clone(from: string, to: string, branch: string): Promise<void> {
|
||||
this.logger.info(`Cloning repository ${from}..`);
|
||||
if (!fs.existsSync(to)) {
|
||||
await this.git().clone(from, to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]);
|
||||
} else {
|
||||
this.logger.warn(`Folder ${to} already exist. Won't clone`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new branch starting from the current one and checkout in it
|
||||
* @param cwd repository in which createBranch should be performed
|
||||
* @param newBranch new branch name
|
||||
*/
|
||||
async createLocalBranch(cwd: string, newBranch: string): Promise<void> {
|
||||
this.logger.info(`Creating branch ${newBranch}..`);
|
||||
await this.git(cwd).checkoutLocalBranch(newBranch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new remote to the current repository
|
||||
* @param cwd repository in which addRemote should be performed
|
||||
* @param remote remote git link
|
||||
* @param remoteName [optional] name of the remote, by default 'fork' is used
|
||||
*/
|
||||
async addRemote(cwd: string, remote: string, remoteName = "fork"): Promise<void> {
|
||||
this.logger.info(`Adding new remote ${remote}..`);
|
||||
await this.git(cwd).addRemote(remoteName, remote);
|
||||
}
|
||||
|
||||
/**
|
||||
* Git fetch from a particular branch
|
||||
* @param cwd repository in which fetch should be performed
|
||||
* @param branch fetch from the given branch
|
||||
* @param remote [optional] the remote to fetch, by default origin
|
||||
*/
|
||||
async fetch(cwd: string, branch: string, remote = "origin"): Promise<void> {
|
||||
await this.git(cwd).fetch(remote, branch, ["--quiet"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cherry-pick a specific sha
|
||||
* @param cwd repository in which the sha should be cherry picked to
|
||||
* @param sha commit sha
|
||||
*/
|
||||
async cherryPick(cwd: string, sha: string): Promise<void> {
|
||||
this.logger.info(`Cherry picking ${sha}..`);
|
||||
await this.git(cwd).raw(["cherry-pick", "--strategy=recursive", "-X", "theirs", sha]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a branch to a remote
|
||||
* @param cwd repository in which the push should be performed
|
||||
* @param branch branch to be pushed
|
||||
* @param remote [optional] remote to which the branch should be pushed to, by default 'origin'
|
||||
*/
|
||||
async push(cwd: string, branch: string, remote = "origin", force = false): Promise<void> {
|
||||
this.logger.info(`Pushing ${branch} to ${remote}..`);
|
||||
|
||||
const options = ["--quiet"];
|
||||
if (force) {
|
||||
options.push("--force-with-lease");
|
||||
}
|
||||
await this.git(cwd).push(remote, branch, options);
|
||||
}
|
||||
|
||||
}
|
39
src/service/git/git-service-factory.ts
Normal file
39
src/service/git/git-service-factory.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import GitService from "@gb/service/git/git-service";
|
||||
import { GitServiceType } from "@gb/service/git/git.types";
|
||||
import GitHubService from "@gb/service/git/github/github-service";
|
||||
|
||||
/**
|
||||
* Singleton git service factory class
|
||||
*/
|
||||
export default class GitServiceFactory {
|
||||
|
||||
private static instance?: GitService;
|
||||
|
||||
public static getService(): GitService {
|
||||
if (!GitServiceFactory.instance) {
|
||||
throw new Error("You must call `init` method first!");
|
||||
}
|
||||
|
||||
return GitServiceFactory.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the singleton git management service
|
||||
* @param type git management service type
|
||||
* @param auth authentication, like github token
|
||||
*/
|
||||
public static init(type: GitServiceType, auth: string): void {
|
||||
|
||||
if (GitServiceFactory.instance) {
|
||||
throw new Error("Git service already initialized!");
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case GitServiceType.GITHUB:
|
||||
GitServiceFactory.instance = new GitHubService(auth);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid git service type received: ${type}`);
|
||||
}
|
||||
}
|
||||
}
|
33
src/service/git/git-service.ts
Normal file
33
src/service/git/git-service.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { GitPullRequest } from "@gb/service/git/git.types";
|
||||
|
||||
/**
|
||||
* Git management service interface, which provides a common API for interacting
|
||||
* with several git management services like GitHub, Gitlab or Bitbucket.
|
||||
*/
|
||||
export default interface GitService {
|
||||
|
||||
// READ
|
||||
|
||||
/**
|
||||
* Get a pull request object from the underneath git service
|
||||
* @param owner repository's owner
|
||||
* @param repo repository's name
|
||||
* @param prNumber pull request number
|
||||
* @returns {Promise<PullRequest>}
|
||||
*/
|
||||
getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest>;
|
||||
|
||||
// WRITE
|
||||
|
||||
/**
|
||||
* Create a new pull request on the underneath git service
|
||||
* @param owner repository's owner
|
||||
* @param repo repository's name
|
||||
* @param head name of the source branch
|
||||
* @param base name of the target branch
|
||||
* @param title pr title
|
||||
* @param body pr body
|
||||
* @param reviewers pr list of reviewers
|
||||
*/
|
||||
createPullRequest(owner: string, repo: string, head: string, base: string, title: string, body: string, reviewers: string[]): Promise<void>;
|
||||
}
|
15
src/service/git/git.types.ts
Normal file
15
src/service/git/git.types.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export interface GitPullRequest {
|
||||
url: string,
|
||||
patchUrl: string,
|
||||
state: string,
|
||||
title: string,
|
||||
body: string,
|
||||
reviewers: string[],
|
||||
targetRepo: string,
|
||||
sourceRepo: string,
|
||||
commits: string[]
|
||||
}
|
||||
|
||||
export enum GitServiceType {
|
||||
GITHUB = "github"
|
||||
}
|
19
src/service/git/github/github-mapper.ts
Normal file
19
src/service/git/github/github-mapper.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { GitPullRequest } from "@gb/service/git/git.types";
|
||||
import { PullRequest, User } from "@octokit/webhooks-types";
|
||||
|
||||
export default class GitHubMapper {
|
||||
|
||||
mapPullRequest(pr: PullRequest): GitPullRequest {
|
||||
return {
|
||||
url: pr.url,
|
||||
title: pr.title,
|
||||
body: pr.body,
|
||||
patchUrl: pr.patch_url,
|
||||
state: pr.state,
|
||||
reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => (r as User)?.login)),
|
||||
sourceRepo: pr.head.repo.full_name,
|
||||
targetRepo: pr.base.repo.full_name,
|
||||
commits: [pr.merge_commit_sha]
|
||||
} as GitPullRequest;
|
||||
}
|
||||
}
|
38
src/service/git/github/github-service.ts
Normal file
38
src/service/git/github/github-service.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import GitService from "@gb/service/git/git-service";
|
||||
import { GitPullRequest } from "@gb/service/git/git.types";
|
||||
import GitHubMapper from "@gb/service/git/github/github-mapper";
|
||||
import OctokitFactory from "@gb/service/git/github/octokit-factory";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { PullRequest } from "@octokit/webhooks-types";
|
||||
|
||||
export default class GitHubService implements GitService {
|
||||
|
||||
private octokit: Octokit;
|
||||
private mapper: GitHubMapper;
|
||||
|
||||
constructor(token: string) {
|
||||
this.octokit = OctokitFactory.getOctokit(token);
|
||||
this.mapper = new GitHubMapper();
|
||||
}
|
||||
|
||||
// READ
|
||||
|
||||
async getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest> {
|
||||
const { data } = await this.octokit.rest.pulls.get({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
return this.mapper.mapPullRequest(data as PullRequest);
|
||||
}
|
||||
|
||||
// WRITE
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
createPullRequest(owner: string, repo: string, head: string, base: string, title: string, body: string, reviewers: string[]): Promise<void> {
|
||||
// throw new Error("Method not implemented.");
|
||||
// TODO implement
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
24
src/service/git/github/octokit-factory.ts
Normal file
24
src/service/git/github/octokit-factory.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import LoggerService from "@gb/service/logger/logger-service";
|
||||
import LoggerServiceFactory from "@gb/service/logger/logger-service-factory";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
|
||||
/**
|
||||
* Singleton factory class for {Octokit} instance
|
||||
*/
|
||||
export default class OctokitFactory {
|
||||
|
||||
private static logger: LoggerService = LoggerServiceFactory.getLogger();
|
||||
private static octokit?: Octokit;
|
||||
|
||||
public static getOctokit(token: string): Octokit {
|
||||
if (!OctokitFactory.octokit) {
|
||||
OctokitFactory.logger.info("Creating octokit instance..");
|
||||
OctokitFactory.octokit = new Octokit({
|
||||
auth: token,
|
||||
userAgent: "lampajr/backporting"
|
||||
});
|
||||
}
|
||||
|
||||
return OctokitFactory.octokit;
|
||||
}
|
||||
}
|
32
src/service/logger/console-logger-service.ts
Normal file
32
src/service/logger/console-logger-service.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import Logger from "@gb/service/logger/logger";
|
||||
import LoggerService from "@gb/service/logger/logger-service";
|
||||
|
||||
export default class ConsoleLoggerService implements LoggerService {
|
||||
|
||||
private readonly logger;
|
||||
|
||||
constructor() {
|
||||
this.logger = new Logger();
|
||||
}
|
||||
|
||||
trace(message: string): void {
|
||||
this.logger.log("[TRACE]", message);
|
||||
}
|
||||
|
||||
debug(message: string): void {
|
||||
this.logger.log("[DEBUG]", message);
|
||||
}
|
||||
|
||||
info(message: string): void {
|
||||
this.logger.log("[INFO]", message);
|
||||
}
|
||||
|
||||
warn(message: string): void {
|
||||
this.logger.log("[WARN]", message);
|
||||
}
|
||||
|
||||
error(message: string): void {
|
||||
this.logger.log("[ERROR]", message);
|
||||
}
|
||||
|
||||
}
|
18
src/service/logger/logger-service-factory.ts
Normal file
18
src/service/logger/logger-service-factory.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import ConsoleLoggerService from "@gb/service/logger/console-logger-service";
|
||||
import LoggerService from "@gb/service/logger/logger-service";
|
||||
|
||||
/**
|
||||
* Singleton factory class
|
||||
*/
|
||||
export default class LoggerServiceFactory {
|
||||
|
||||
private static instance?: LoggerService;
|
||||
|
||||
public static getLogger(): LoggerService {
|
||||
if (!LoggerServiceFactory.instance) {
|
||||
LoggerServiceFactory.instance = new ConsoleLoggerService();
|
||||
}
|
||||
|
||||
return LoggerServiceFactory.instance;
|
||||
}
|
||||
}
|
15
src/service/logger/logger-service.ts
Normal file
15
src/service/logger/logger-service.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Logger service interface providing the most commong logging functionalities
|
||||
*/
|
||||
export default interface LoggerService {
|
||||
|
||||
trace(message: string): void;
|
||||
|
||||
debug(message: string): void;
|
||||
|
||||
info(message: string): void;
|
||||
|
||||
warn(message: string): void;
|
||||
|
||||
error(message: string): void;
|
||||
}
|
15
src/service/logger/logger.ts
Normal file
15
src/service/logger/logger.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
/**
|
||||
* Common logger class based on the console.log functionality
|
||||
*/
|
||||
export default class Logger {
|
||||
|
||||
log(prefix: string, ...str: string[]) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log.apply(console, [prefix, ...str]);
|
||||
}
|
||||
|
||||
emptyLine() {
|
||||
this.log("", "");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue