feat: auto-detect the value of the no-squash option (#118)

The auto-no-squash option is added to:

* backport all the commits when the pull/merge request has been merged
* backport the squashed commit otherwise

It is equivalent to dynamically adjust the value of the no-squash
option, depending on the context.

The no-squash option is kept for backward compatibility for a single
use case: backporting the merged commit instead of backporting the
commits of the pull/merge request request.

Detecting if a pull/merge request was squashed or not depends on the
underlying forge:

* Forgejo / GitHub: use the API to count the number of parents
* GitLab: if the squash_commit_sha is set, the merge request was
  squashed

If the pull/merge request is open, always backport all the commits it
contains.

Fixes: https://github.com/kiegroup/git-backporting/issues/113

Co-authored-by: Andrea Lamparelli <a.lamparelli95@gmail.com>
This commit is contained in:
Earl Warren 2024-04-08 18:51:13 +02:00 committed by GitHub
parent fc5dba6703
commit 6042bcc40b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 324 additions and 54 deletions

65
dist/gha/index.js vendored
View file

@ -66,6 +66,7 @@ class ArgsParser {
labels: this.getOrDefault(args.labels, []),
inheritLabels: this.getOrDefault(args.inheritLabels, false),
squash: this.getOrDefault(args.squash, true),
autoNoSquash: this.getOrDefault(args.autoNoSquash, false),
strategy: this.getOrDefault(args.strategy),
strategyOption: this.getOrDefault(args.strategyOption),
cherryPickOptions: this.getOrDefault(args.cherryPickOptions),
@ -207,6 +208,7 @@ class GHAArgsParser extends args_parser_1.default {
labels: (0, args_utils_1.getAsCommaSeparatedList)((0, core_1.getInput)("labels")),
inheritLabels: (0, args_utils_1.getAsBooleanOrDefault)((0, core_1.getInput)("inherit-labels")),
squash: !(0, args_utils_1.getAsBooleanOrDefault)((0, core_1.getInput)("no-squash")),
autoNoSquash: (0, args_utils_1.getAsBooleanOrDefault)((0, core_1.getInput)("auto-no-squash")),
strategy: (0, args_utils_1.getOrUndefined)((0, core_1.getInput)("strategy")),
strategyOption: (0, args_utils_1.getOrUndefined)((0, core_1.getInput)("strategy-option")),
cherryPickOptions: (0, args_utils_1.getOrUndefined)((0, core_1.getInput)("cherry-pick-options")),
@ -299,6 +301,9 @@ class PullRequestConfigsParser extends configs_parser_1.default {
}
async parse(args) {
let pr;
if (args.autoNoSquash) {
args.squash = undefined;
}
try {
pr = await this.gitClient.getPullRequestFromUrl(args.pullRequest, args.squash);
}
@ -628,12 +633,16 @@ GitClientFactory.logger = logger_service_factory_1.default.getLogger();
/***/ }),
/***/ 9080:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getEnv = exports.getGitTokenFromEnv = exports.inferGitApiUrl = exports.inferGitClient = void 0;
exports.getEnv = exports.getGitTokenFromEnv = exports.inferSquash = exports.inferGitApiUrl = exports.inferGitClient = void 0;
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
const git_types_1 = __nccwpck_require__(750);
const configs_types_1 = __nccwpck_require__(4753);
const PUBLIC_GITHUB_URL = "https://github.com";
@ -673,6 +682,30 @@ const inferGitApiUrl = (prUrl, apiVersion = "v4") => {
return `${baseUrl}/api/${apiVersion}`;
};
exports.inferGitApiUrl = inferGitApiUrl;
/**
* Infer the value of the squash option
* @param open true if the pull/merge request is still open
* @param squash_commit undefined if the pull/merge request was merged, the sha of the squashed commit if it was squashed
* @returns true if a single commit must be cherry-picked, false if all merged commits must be cherry-picked
*/
const inferSquash = (open, squash_commit) => {
const logger = logger_service_factory_1.default.getLogger();
if (open) {
logger.debug("cherry-pick all commits because they have not been merged (or squashed) in the base branch yet");
return false;
}
else {
if (squash_commit !== undefined) {
logger.debug(`cherry-pick the squashed commit ${squash_commit}`);
return true;
}
else {
logger.debug("cherry-pick the merged commit(s)");
return false;
}
}
};
exports.inferSquash = inferSquash;
/**
* Retrieve the git token from env variable, the default is taken from GIT_TOKEN env.
* All specific git env variable have precedence and override the default one.
@ -748,6 +781,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const git_util_1 = __nccwpck_require__(9080);
const git_types_1 = __nccwpck_require__(750);
const github_mapper_1 = __importDefault(__nccwpck_require__(5764));
const octokit_factory_1 = __importDefault(__nccwpck_require__(4257));
@ -770,13 +804,28 @@ class GitHubClient {
getDefaultGitEmail() {
return "noreply@github.com";
}
async getPullRequest(owner, repo, prNumber, squash = true) {
async getPullRequest(owner, repo, prNumber, squash) {
this.logger.debug(`Fetching pull request ${owner}/${repo}/${prNumber}`);
const { data } = await this.octokit.rest.pulls.get({
owner: owner,
repo: repo,
pull_number: prNumber,
});
if (squash === undefined) {
let commit_sha = undefined;
const open = data.state == "open";
if (!open) {
const commit = await this.octokit.rest.git.getCommit({
owner: owner,
repo: repo,
commit_sha: data.merge_commit_sha,
});
if (commit.data.parents.length === 1) {
commit_sha = data.merge_commit_sha;
}
}
squash = (0, git_util_1.inferSquash)(open, commit_sha);
}
const commits = [];
if (!squash) {
// fetch all commits
@ -794,7 +843,7 @@ class GitHubClient {
}
return this.mapper.mapPullRequest(data, commits);
}
async getPullRequestFromUrl(prUrl, squash = true) {
async getPullRequestFromUrl(prUrl, squash) {
const { owner, project, id } = this.extractPullRequestData(prUrl);
return this.getPullRequest(owner, project, id, squash);
}
@ -973,6 +1022,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const git_util_1 = __nccwpck_require__(9080);
const git_types_1 = __nccwpck_require__(750);
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
const gitlab_mapper_1 = __importDefault(__nccwpck_require__(2675));
@ -1005,9 +1055,12 @@ class GitLabClient {
}
// READ
// example: <host>/api/v4/projects/<namespace>%2Fbackporting-example/merge_requests/1
async getPullRequest(namespace, repo, mrNumber, squash = true) {
async getPullRequest(namespace, repo, mrNumber, squash) {
const projectId = this.getProjectId(namespace, repo);
const { data } = await this.client.get(`/projects/${projectId}/merge_requests/${mrNumber}`);
if (squash === undefined) {
squash = (0, git_util_1.inferSquash)(data.state == "opened", data.squash_commit_sha);
}
const commits = [];
if (!squash) {
// fetch all commits
@ -1022,7 +1075,7 @@ class GitLabClient {
}
return this.mapper.mapPullRequest(data, commits);
}
getPullRequestFromUrl(mrUrl, squash = true) {
getPullRequestFromUrl(mrUrl, squash) {
const { namespace, project, id } = this.extractMergeRequestData(mrUrl);
return this.getPullRequest(namespace, project, id, squash);
}