1
0
Fork 0
mirror of https://code.forgejo.org/actions/git-backporting synced 2025-07-01 23:06:00 +02:00

feat: integrate tool with gitlab service (#39)

* feat: integrate tool with gitlab service

Fix https://github.com/lampajr/backporting/issues/30
This commit is contained in:
Andrea Lamparelli 2023-07-02 00:05:17 +02:00 committed by GitHub
parent 8a007941d1
commit 107f5e52d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 17821 additions and 1553 deletions

View file

@ -0,0 +1,157 @@
import LoggerService from "@bp/service/logger/logger-service";
import GitClient from "@bp/service/git/git-client";
import { GitPullRequest, BackportPullRequest } from "@bp/service/git/git.types";
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import { MergeRequestSchema, UserSchema } from "@gitbeaker/rest";
import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper";
import axios, { Axios } from "axios";
import https from "https";
export default class GitLabClient implements GitClient {
private readonly logger: LoggerService;
private readonly apiUrl: string;
private readonly mapper: GitLabMapper;
private readonly client: Axios;
constructor(token: string, apiUrl: string, rejectUnauthorized = false) {
this.logger = LoggerServiceFactory.getLogger();
this.apiUrl = apiUrl;
this.client = axios.create({
baseURL: this.apiUrl,
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "lampajr/backporting",
},
httpsAgent: new https.Agent({
rejectUnauthorized
})
});
this.mapper = new GitLabMapper(this.client);
}
// READ
// example: <host>/api/v4/projects/alampare%2Fbackporting-example/merge_requests/1
async getPullRequest(namespace: string, repo: string, mrNumber: number): Promise<GitPullRequest> {
const projectId = this.getProjectId(namespace, repo);
const { data } = await this.client.get(`/projects/${projectId}/merge_requests/${mrNumber}`);
return this.mapper.mapPullRequest(data as MergeRequestSchema);
}
getPullRequestFromUrl(mrUrl: string): Promise<GitPullRequest> {
const { namespace, project, id } = this.extractMergeRequestData(mrUrl);
return this.getPullRequest(namespace, project, id);
}
// WRITE
async createPullRequest(backport: BackportPullRequest): Promise<string> {
this.logger.info(`Creating pull request ${backport.head} -> ${backport.base}.`);
this.logger.info(`${JSON.stringify(backport, null, 2)}`);
const projectId = this.getProjectId(backport.owner, backport.repo);
const { data } = await this.client.post(`/projects/${projectId}/merge_requests`, {
source_branch: backport.head,
target_branch: backport.base,
title: backport.title,
description: backport.body,
reviewer_ids: [],
assignee_ids: [],
});
const mr = data as MergeRequestSchema;
// reviewers
const reviewerIds: number[] = [];
for(const r of backport.reviewers) {
try {
this.logger.debug("Retrieving user: " + r);
const user = await this.getUser(r);
reviewerIds.push(user.id);
} catch(error) {
this.logger.warn(`Failed to retrieve reviewer ${r}`);
}
}
if (reviewerIds.length > 0) {
try {
this.logger.info("Setting reviewers: " + reviewerIds);
await this.client.put(`/projects/${projectId}/merge_requests/${mr.iid}`, {
reviewer_ids: reviewerIds.filter(r => r !== undefined),
});
} catch(error) {
this.logger.warn("Failure trying to update reviewers. " + error);
}
}
// assignees
const assigneeIds: number[] = [];
for(const a of backport.assignees) {
try {
this.logger.debug("Retrieving user: " + a);
const user = await this.getUser(a);
assigneeIds.push(user.id);
} catch(error) {
this.logger.warn(`Failed to retrieve assignee ${a}`);
}
}
if (assigneeIds.length > 0) {
try {
this.logger.info("Setting assignees: " + assigneeIds);
await this.client.put(`/projects/${projectId}/merge_requests/${mr.iid}`, {
assignee_ids: assigneeIds.filter(a => a !== undefined),
});
} catch(error) {
this.logger.warn("Failure trying to update assignees. " + error);
}
}
return mr.web_url;
}
/**
* Retrieve a gitlab user given its username
* @param username
* @returns UserSchema
*/
private async getUser(username: string): Promise<UserSchema> {
const { data } = await this.client.get(`/users?username=${username}`);
const users = data as UserSchema[];
if (users.length > 1) {
throw new Error("Too many users found with username=" + username);
}
if (users.length == 0) {
throw new Error("User " + username + " not found");
}
return users[0];
}
/**
* Extract repository namespace, project and mr number from the merge request url
* example: <host>/alampare/backporting-example/-/merge_requests/1
* note: "-/" could be omitted
* @param mrUrl merge request url
* @returns {{owner: string, project: string}}
*/
private extractMergeRequestData(mrUrl: string): {namespace: string, project: string, id: number} {
const elems: string[] = mrUrl.replace("/-/", "/").split("/");
return {
namespace: elems[elems.length - 4],
project: elems[elems.length - 3],
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
};
}
private getProjectId(namespace: string, repo: string) {
// e.g., <namespace>%2F<repo>
return encodeURIComponent(`${namespace}/${repo}`);
}
}