mirror of
https://code.forgejo.org/actions/git-backporting.git
synced 2025-05-17 11:09:13 -04:00
feat(issue-54): backport pr commits without squash (#55)
* feat(issue-54): backport pr commits without squash fix https://github.com/kiegroup/git-backporting/issues/54 * feat(issue-54): fixed readme
This commit is contained in:
parent
a737aa7c4c
commit
c4dbb26c1d
29 changed files with 990 additions and 145 deletions
|
@ -38,6 +38,7 @@ export default abstract class ArgsParser {
|
|||
inheritReviewers: this.getOrDefault(args.inheritReviewers, true),
|
||||
labels: this.getOrDefault(args.labels, []),
|
||||
inheritLabels: this.getOrDefault(args.inheritLabels, false),
|
||||
squash: this.getOrDefault(args.squash, true),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -18,4 +18,5 @@ export interface Args {
|
|||
inheritReviewers?: boolean, // if true and reviewers == [] then inherit reviewers from original pr
|
||||
labels?: string[], // backport pr labels
|
||||
inheritLabels?: boolean, // if true inherit labels from original pr
|
||||
squash?: boolean, // if false use squashed/merged commit otherwise backport all commits as part of the pr
|
||||
}
|
|
@ -26,6 +26,7 @@ export default class CLIArgsParser extends ArgsParser {
|
|||
.option("--no-inherit-reviewers", "if provided and reviewers option is empty then inherit them from original pull request")
|
||||
.option("--labels <labels>", "comma separated list of labels to be assigned to the backported pull request", getAsCommaSeparatedList)
|
||||
.option("--inherit-labels", "if true the backported pull request will inherit labels from the original one")
|
||||
.option("--no-squash", "if provided the tool will backport all commits as part of the pull request")
|
||||
.option("-cf, --config-file <config-file>", "configuration file containing all valid options, the json must match Args interface");
|
||||
}
|
||||
|
||||
|
@ -56,6 +57,7 @@ export default class CLIArgsParser extends ArgsParser {
|
|||
inheritReviewers: opts.inheritReviewers,
|
||||
labels: opts.labels,
|
||||
inheritLabels: opts.inheritLabels,
|
||||
squash: opts.squash,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ export default class GHAArgsParser extends ArgsParser {
|
|||
inheritReviewers: !getAsBooleanOrDefault(getInput("no-inherit-reviewers")),
|
||||
labels: getAsCommaSeparatedList(getInput("labels")),
|
||||
inheritLabels: getAsBooleanOrDefault(getInput("inherit-labels")),
|
||||
squash: !getAsBooleanOrDefault(getInput("no-squash")),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
|||
public async parse(args: Args): Promise<Configs> {
|
||||
let pr: GitPullRequest;
|
||||
try {
|
||||
pr = await this.gitClient.getPullRequestFromUrl(args.pullRequest);
|
||||
pr = await this.gitClient.getPullRequestFromUrl(args.pullRequest, args.squash!);
|
||||
} catch(error) {
|
||||
this.logger.error("Something went wrong retrieving pull request");
|
||||
throw error;
|
||||
|
|
|
@ -17,16 +17,18 @@ import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
|||
* @param owner repository's owner
|
||||
* @param repo repository's name
|
||||
* @param prNumber pull request number
|
||||
* @param squash if true keep just one single commit, otherwise get the full list
|
||||
* @returns {Promise<PullRequest>}
|
||||
*/
|
||||
getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest>;
|
||||
getPullRequest(owner: string, repo: string, prNumber: number, squash: boolean): Promise<GitPullRequest>;
|
||||
|
||||
/**
|
||||
* Get a pull request object from the underneath git service
|
||||
* @param prUrl pull request html url
|
||||
* @param squash if true keep just one single commit, otherwise get the full list
|
||||
* @returns {Promise<PullRequest>}
|
||||
*/
|
||||
getPullRequestFromUrl(prUrl: string): Promise<GitPullRequest>;
|
||||
getPullRequestFromUrl(prUrl: string, squash: boolean): Promise<GitPullRequest>;
|
||||
|
||||
// WRITE
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ export default interface GitResponseMapper<PR, S> {
|
|||
|
||||
mapPullRequest(
|
||||
pr: PR,
|
||||
commits?: string[],
|
||||
): Promise<GitPullRequest>;
|
||||
|
||||
mapGitState(state: S): GitRepoState;
|
||||
|
|
|
@ -31,20 +31,36 @@ export default class GitHubClient implements GitClient {
|
|||
return "noreply@github.com";
|
||||
}
|
||||
|
||||
async getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest> {
|
||||
async getPullRequest(owner: string, repo: string, prNumber: number, squash = true): Promise<GitPullRequest> {
|
||||
this.logger.info(`Getting pull request ${owner}/${repo}/${prNumber}.`);
|
||||
const { data } = await this.octokit.rest.pulls.get({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: prNumber
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
return this.mapper.mapPullRequest(data as PullRequest);
|
||||
const commits: string[] = [];
|
||||
if (!squash) {
|
||||
// fetch all commits
|
||||
try {
|
||||
const { data } = await this.octokit.rest.pulls.listCommits({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
commits.push(...data.map(c => c.sha));
|
||||
} catch(error) {
|
||||
throw new Error(`Failed to retrieve commits for pull request n. ${prNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return this.mapper.mapPullRequest(data as PullRequest, commits);
|
||||
}
|
||||
|
||||
async getPullRequestFromUrl(prUrl: string): Promise<GitPullRequest> {
|
||||
async getPullRequestFromUrl(prUrl: string, squash = true): Promise<GitPullRequest> {
|
||||
const { owner, project, id } = this.extractPullRequestData(prUrl);
|
||||
return this.getPullRequest(owner, project, id);
|
||||
return this.getPullRequest(owner, project, id, squash);
|
||||
}
|
||||
|
||||
// WRITE
|
||||
|
|
|
@ -13,7 +13,7 @@ export default class GitHubMapper implements GitResponseMapper<PullRequest, "ope
|
|||
}
|
||||
}
|
||||
|
||||
async mapPullRequest(pr: PullRequest): Promise<GitPullRequest> {
|
||||
async mapPullRequest(pr: PullRequest, commits?: string[]): Promise<GitPullRequest> {
|
||||
return {
|
||||
number: pr.number,
|
||||
author: pr.user.login,
|
||||
|
@ -30,11 +30,16 @@ export default class GitHubMapper implements GitResponseMapper<PullRequest, "ope
|
|||
sourceRepo: await this.mapSourceRepo(pr),
|
||||
targetRepo: await this.mapTargetRepo(pr),
|
||||
nCommits: pr.commits,
|
||||
// if pr is open use latest commit sha otherwise use merge_commit_sha
|
||||
commits: pr.state === "open" ? [pr.head.sha] : [pr.merge_commit_sha as string]
|
||||
// if commits is provided use them, otherwise fetch the single sha representing the whole pr
|
||||
commits: (commits && commits.length > 0) ? commits : this.getSha(pr),
|
||||
};
|
||||
}
|
||||
|
||||
private getSha(pr: PullRequest) {
|
||||
// if pr is open use latest commit sha otherwise use merge_commit_sha
|
||||
return pr.state === "open" ? [pr.head.sha] : [pr.merge_commit_sha as string];
|
||||
}
|
||||
|
||||
async mapSourceRepo(pr: PullRequest): Promise<GitRepository> {
|
||||
return Promise.resolve({
|
||||
owner: pr.head.repo.full_name.split("/")[0],
|
||||
|
|
|
@ -2,7 +2,7 @@ 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 { CommitSchema, MergeRequestSchema, UserSchema } from "@gitbeaker/rest";
|
||||
import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper";
|
||||
import axios, { Axios } from "axios";
|
||||
import https from "https";
|
||||
|
@ -41,16 +41,29 @@ export default class GitLabClient implements GitClient {
|
|||
// READ
|
||||
|
||||
// example: <host>/api/v4/projects/<namespace>%2Fbackporting-example/merge_requests/1
|
||||
async getPullRequest(namespace: string, repo: string, mrNumber: number): Promise<GitPullRequest> {
|
||||
async getPullRequest(namespace: string, repo: string, mrNumber: number, squash = true): 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);
|
||||
const commits: string[] = [];
|
||||
if (!squash) {
|
||||
// fetch all commits
|
||||
try {
|
||||
const { data } = await this.client.get(`/projects/${projectId}/merge_requests/${mrNumber}/commits`);
|
||||
|
||||
// gitlab returns them in reverse order
|
||||
commits.push(...(data as CommitSchema[]).map(c => c.id).reverse());
|
||||
} catch(error) {
|
||||
throw new Error(`Failed to retrieve commits for merge request n. ${mrNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return this.mapper.mapPullRequest(data as MergeRequestSchema, commits);
|
||||
}
|
||||
|
||||
getPullRequestFromUrl(mrUrl: string): Promise<GitPullRequest> {
|
||||
getPullRequestFromUrl(mrUrl: string, squash = true): Promise<GitPullRequest> {
|
||||
const { namespace, project, id } = this.extractMergeRequestData(mrUrl);
|
||||
return this.getPullRequest(namespace, project, id);
|
||||
return this.getPullRequest(namespace, project, id, squash);
|
||||
}
|
||||
|
||||
// WRITE
|
||||
|
|
|
@ -24,7 +24,7 @@ export default class GitLabMapper implements GitResponseMapper<MergeRequestSchem
|
|||
}
|
||||
}
|
||||
|
||||
async mapPullRequest(mr: MergeRequestSchema): Promise<GitPullRequest> {
|
||||
async mapPullRequest(mr: MergeRequestSchema, commits?: string[]): Promise<GitPullRequest> {
|
||||
return {
|
||||
number: mr.iid,
|
||||
author: mr.author.username,
|
||||
|
@ -40,12 +40,17 @@ export default class GitLabMapper implements GitResponseMapper<MergeRequestSchem
|
|||
labels: mr.labels ?? [],
|
||||
sourceRepo: await this.mapSourceRepo(mr),
|
||||
targetRepo: await this.mapTargetRepo(mr),
|
||||
nCommits: 1, // info not present on mr
|
||||
// if mr is merged, use merge_commit_sha otherwise use sha
|
||||
// what is the difference between sha and diff_refs.head_sha?
|
||||
commits: this.isMerged(mr) ? [mr.squash_commit_sha ? mr.squash_commit_sha : mr.merge_commit_sha as string] : [mr.sha]
|
||||
// if commits list is provided use that as source
|
||||
nCommits: (commits && commits.length > 1) ? commits.length : 1,
|
||||
commits: (commits && commits.length > 1) ? commits : this.getSha(mr)
|
||||
};
|
||||
}
|
||||
|
||||
private getSha(mr: MergeRequestSchema) {
|
||||
// if mr is merged, use merge_commit_sha otherwise use sha
|
||||
// what is the difference between sha and diff_refs.head_sha?
|
||||
return this.isMerged(mr) ? [mr.squash_commit_sha ? mr.squash_commit_sha : mr.merge_commit_sha as string] : [mr.sha];
|
||||
}
|
||||
|
||||
async mapSourceRepo(mr: MergeRequestSchema): Promise<GitRepository> {
|
||||
const project: ProjectSchema = await this.getProject(mr.source_project_id);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue