Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
204ebd4376 | ||
|
|
70da575afc | ||
|
|
aac73bf7c5 | ||
|
|
ed32d2275b | ||
|
|
e7c9b4795b | ||
|
|
2db9eb3ef2 | ||
|
|
4313be48e7 | ||
|
|
9f0fbc0b2f | ||
|
|
eecbff34b7 | ||
|
|
5fc72e127b |
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,5 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## [4.5.0](https://github.com/kiegroup/git-backporting/compare/v4.4.1...v4.5.0) (2023-12-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **gh-85:** take git tokens from environment ([#88](https://github.com/kiegroup/git-backporting/issues/88)) ([70da575](https://github.com/kiegroup/git-backporting/commit/70da575afce603190eafed927637922a37fbd087))
|
||||
|
||||
## [4.4.1](https://github.com/kiegroup/git-backporting/compare/v4.4.0...v4.4.1) (2023-12-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* namespace parsing in gitlab ([#84](https://github.com/kiegroup/git-backporting/issues/84)) ([ed32d22](https://github.com/kiegroup/git-backporting/commit/ed32d2275b6008d31e456c41beecd536eceb23dc))
|
||||
|
||||
## [4.4.0](https://github.com/kiegroup/git-backporting/compare/v4.3.0...v4.4.0) (2023-08-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* integrate with codeberg ([#80](https://github.com/kiegroup/git-backporting/issues/80)) ([9f0fbc0](https://github.com/kiegroup/git-backporting/commit/9f0fbc0b2fd8d449207660323be87f6d2fa8c017))
|
||||
* **issue-77:** handle multiple target branches ([#78](https://github.com/kiegroup/git-backporting/issues/78)) ([5fc72e1](https://github.com/kiegroup/git-backporting/commit/5fc72e127bedb3177f4e17ff1182827c78154ef1))
|
||||
|
||||
## [4.3.0](https://github.com/kiegroup/git-backporting/compare/v4.2.0...v4.3.0) (2023-07-27)
|
||||
|
||||
|
||||
|
||||
27
README.md
27
README.md
@@ -31,7 +31,7 @@ Table of content
|
||||
|
||||
## Who is this tool for?
|
||||
|
||||
`git-backporting` is a tool that provides capabilities to *backport* pull requests (on *GitHub*) and merge requests (on *GitLab*) in an automated way.
|
||||
`git-backporting` is a fully configurable tool that provides capabilities to *backport* pull requests (on *GitHub*) and merge requests (on *GitLab*) in an automated way.
|
||||
|
||||
> *What is backporting?* - backporting is an action aiming to move a change (usually a commit) from a branch (usually the main one) to another one, which is generally referring to a still maintained release branch. Keeping it simple: it is about to move a specific change or a set of them from one branch to another.
|
||||
|
||||
@@ -94,10 +94,10 @@ This tool comes with some inputs that allow users to override the default behavi
|
||||
|---------------|----------------------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
|
||||
| Version | -V, --version | - | Current version of the tool | |
|
||||
| Help | -h, --help | - | Display the help message | |
|
||||
| Target Branch | -tb, --target-branch | N | Branch where the changes must be backported to | |
|
||||
| Target Branches | -tb, --target-branch | N | Comma separated list of branches where the changes must be backported to | |
|
||||
| Pull Request | -pr, --pull-request | N | Original pull request url, the one that must be backported, e.g., https://github.com/kiegroup/git-backporting/pull/1 | |
|
||||
| Configuration File | -cf, --config-file | N | Configuration file, in JSON format, containing all options to be overridded, note that if provided all other CLI options will be ignored | |
|
||||
| Auth | -a, --auth | N | `GITHUB_TOKEN`, `GITLAB_TOKEN` or a `repo` scoped [Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) | "" |
|
||||
| Auth | -a, --auth | N | Git access/authorization token, if provided all token env variables will be ignored. See [auth token](#authorization-token) section for more details | "" |
|
||||
| Folder | -f, --folder | N | Local folder full name of the repository that will be checked out, e.g., /tmp/folder | {cwd}/bp |
|
||||
| Git User | -gu, --git-user | N | Local git user name | "GitHub" |
|
||||
| Git Email | -ge, --git-email | N | Local git user email | "noreply@github.com" |
|
||||
@@ -107,7 +107,7 @@ This tool comes with some inputs that allow users to override the default behavi
|
||||
| Reviewers | --reviewers | N | Backporting pull request comma-separated reviewers list | [] |
|
||||
| Assignees | --assignes | N | Backporting pull request comma-separated assignees list | [] |
|
||||
| No Reviewers Inheritance | --no-inherit-reviewers | N | Considered only if reviewers is empty, if true keep reviewers as empty list, otherwise inherit from original pull request | false |
|
||||
| Backport Branch Name | --bp-branch-name | N | Name of the backporting pull request branch, if it exceeds 250 chars it will be truncated | bp-{target-branch}-{sha1}...{shaN} |
|
||||
| Backport Branch Names | --bp-branch-name | N | Comma separated lists of the backporting pull request branch names, if they exceeds 250 chars they will be truncated | bp-{target-branch}-{sha1}...{shaN} |
|
||||
| Labels | --labels | N | Provide custom labels to be added to the backporting pull request | [] |
|
||||
| Inherit labels | --inherit-labels | N | If enabled inherit lables from the original pull request | false |
|
||||
| No squash | --no-squash | N | If provided the backporting will try to backport all pull request commits without squashing | false |
|
||||
@@ -118,6 +118,17 @@ This tool comes with some inputs that allow users to override the default behavi
|
||||
|
||||
> **NOTE**: `pull request` and `target branch` are *mandatory*, they must be provided as CLI options or as part of the configuration file (if used).
|
||||
|
||||
#### Authorization token
|
||||
|
||||
Since version `4.5.0` we introduced a new feature that allows user to provide the git access token through environment variables. These env variables are taken into consideration only if the `--auth/-a` is not provided as argument/input.
|
||||
Here the supported list of env variables:
|
||||
- `GITHUB_TOKEN`: this is checked only if backporting on Github platform.
|
||||
- `GITLAB_TOKEN`: this is checked only if backporting on Gitlab platform.
|
||||
- `CODEBERG_TOKEN`: this is checked only if backporting on Codeberg platform.
|
||||
- `GIT_TOKEN`: this is considered if none of the previous envs are set.
|
||||
|
||||
> **NOTE**: if `--auth` argument is provided, all env variables will be ignored even if not empty.
|
||||
|
||||
#### Configuration file example
|
||||
|
||||
This is an example of a configuration file that can be used.
|
||||
@@ -139,6 +150,8 @@ Right now **Git Backporting** supports the following git management services:
|
||||
|
||||
* ***GITLAB***: This has been introduced since version `3.0.0`, it works for both public and private *GitLab* servers. The interaction with this service is performed using plain [*axios*](https://axios-http.com) requests. The *gitlab* api version that is used to make requests is `v4`, at the moment there is no possibility to override it.
|
||||
|
||||
* ***CODEBERG***: Introduced since version `4.4.0`, it works for public [codeberg.org](https://codeberg.org/) platform. Thanks to the api compatibility with GitHub, the interaction with this service is performed using using [*octokit*](https://octokit.github.io/rest.js) client library.
|
||||
|
||||
> **NOTE**: by default, all gitlab requests are performed setting `rejectUnauthorized=false`, planning to make this configurable too.
|
||||
|
||||
## GitHub action
|
||||
@@ -192,6 +205,9 @@ on:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
backporting:
|
||||
name: "Backporting"
|
||||
@@ -214,7 +230,6 @@ jobs:
|
||||
with:
|
||||
target-branch: v1
|
||||
pull-request: ${{ github.event.pull_request.url }}
|
||||
auth: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
For a complete description of all inputs see [Inputs section](#inputs).
|
||||
@@ -306,4 +321,4 @@ Every change must be submitted through a *GitHub* pull request (PR). Backporting
|
||||
|
||||
## License
|
||||
|
||||
Backporting (BPer) open source project is licensed under the [MIT](./LICENSE) license.
|
||||
Git backporting open source project is licensed under the [MIT](./LICENSE) license.
|
||||
@@ -5,7 +5,7 @@ inputs:
|
||||
description: "URL of the pull request to backport, e.g., https://github.com/kiegroup/git-backporting/pull/1"
|
||||
required: false
|
||||
target-branch:
|
||||
description: "Branch where the pull request must be backported to"
|
||||
description: "Comma separated list of branches where the pull request must be backported to"
|
||||
required: false
|
||||
config-file:
|
||||
description: "Path to a file containing the json configuration for this tool, the object must match the Args interface"
|
||||
@@ -15,7 +15,7 @@ inputs:
|
||||
required: false
|
||||
default: "false"
|
||||
auth:
|
||||
description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)"
|
||||
description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT), if not provided will look for existing env variables like GITHUB_TOKEN"
|
||||
default: ${{ github.token }}
|
||||
required: false
|
||||
git-user:
|
||||
@@ -36,7 +36,7 @@ inputs:
|
||||
description: "Backporting PR body. Default is the original PR body"
|
||||
required: false
|
||||
bp-branch-name:
|
||||
description: "Backporting PR branch name. Default is auto-generated from commit"
|
||||
description: "Comma separated list of backporting PR branch names. Default is auto-generated from commit and target branches"
|
||||
required: false
|
||||
reviewers:
|
||||
description: "Comma separated list of reviewers for the backporting pull request"
|
||||
|
||||
400
dist/cli/index.js
vendored
400
dist/cli/index.js
vendored
@@ -40,8 +40,8 @@ class ArgsParser {
|
||||
parse() {
|
||||
const args = this.readArgs();
|
||||
// validate and fill with defaults
|
||||
if (!args.pullRequest || !args.targetBranch) {
|
||||
throw new Error("Missing option: pull request and target branch must be provided");
|
||||
if (!args.pullRequest || !args.targetBranch || args.targetBranch.trim().length == 0) {
|
||||
throw new Error("Missing option: pull request and target branches must be provided");
|
||||
}
|
||||
return {
|
||||
pullRequest: args.pullRequest,
|
||||
@@ -179,17 +179,17 @@ class CLIArgsParser extends args_parser_1.default {
|
||||
return new commander_1.Command(package_json_1.name)
|
||||
.version(package_json_1.version)
|
||||
.description(package_json_1.description)
|
||||
.option("-tb, --target-branch <branch>", "branch where changes must be backported to")
|
||||
.option("-tb, --target-branch <branches>", "comma separated list of branches where changes must be backported to")
|
||||
.option("-pr, --pull-request <pr-url>", "pull request url, e.g., https://github.com/kiegroup/git-backporting/pull/1")
|
||||
.option("-d, --dry-run", "if enabled the tool does not create any pull request nor push anything remotely")
|
||||
.option("-a, --auth <auth>", "git service authentication string, e.g., github token")
|
||||
.option("-a, --auth <auth>", "git authentication string, if not provided fallback by looking for existing env variables like GITHUB_TOKEN")
|
||||
.option("-gu, --git-user <git-user>", "local git user name, default is 'GitHub'")
|
||||
.option("-ge, --git-email <git-email>", "local git user email, default is 'noreply@github.com'")
|
||||
.option("-f, --folder <folder>", "local folder where the repo will be checked out, e.g., /tmp/folder")
|
||||
.option("--title <bp-title>", "backport pr title, default original pr title prefixed by target branch")
|
||||
.option("--body <bp-body>", "backport pr title, default original pr body prefixed by bodyPrefix")
|
||||
.option("--body-prefix <bp-body-prefix>", "backport pr body prefix, default `backport <original-pr-link>`")
|
||||
.option("--bp-branch-name <bp-branch-name>", "backport pr branch name, default auto-generated by the commit")
|
||||
.option("--bp-branch-name <bp-branch-names>", "comma separated list of backport pr branch names, default auto-generated by the commit and target branch")
|
||||
.option("--reviewers <reviewers>", "comma separated list of reviewers for the backporting pull request", args_utils_1.getAsCleanedCommaSeparatedList)
|
||||
.option("--assignees <assignees>", "comma separated list of assignees for the backporting pull request", args_utils_1.getAsCleanedCommaSeparatedList)
|
||||
.option("--no-inherit-reviewers", "if provided and reviewers option is empty then inherit them from original pull request")
|
||||
@@ -251,7 +251,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const configs_types_1 = __nccwpck_require__(4753);
|
||||
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
|
||||
const git_types_1 = __nccwpck_require__(750);
|
||||
/**
|
||||
* Abstract configuration parser class in charge to parse
|
||||
* Args and produces a common Configs object
|
||||
@@ -273,10 +275,68 @@ class ConfigsParser {
|
||||
}
|
||||
return Promise.resolve(configs);
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
* @param gitType
|
||||
* @returns tuple where
|
||||
* - the first element is the corresponding env value
|
||||
* - the second element is true if the value is not undefined nor empty
|
||||
*/
|
||||
getGitTokenFromEnv(gitType) {
|
||||
let [token] = this.getEnv(configs_types_1.AuthTokenId.GIT_TOKEN);
|
||||
let [specToken, specOk] = [undefined, false];
|
||||
if (git_types_1.GitClientType.GITHUB == gitType) {
|
||||
[specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITHUB_TOKEN);
|
||||
}
|
||||
else if (git_types_1.GitClientType.GITLAB == gitType) {
|
||||
[specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITLAB_TOKEN);
|
||||
}
|
||||
else if (git_types_1.GitClientType.CODEBERG == gitType) {
|
||||
[specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.CODEBERG_TOKEN);
|
||||
}
|
||||
if (specOk) {
|
||||
token = specToken;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
/**
|
||||
* Get process env variable given the input key string
|
||||
* @param key
|
||||
* @returns tuple where
|
||||
* - the first element is the corresponding env value
|
||||
* - the second element is true if the value is not undefined nor empty
|
||||
*/
|
||||
getEnv(key) {
|
||||
const val = process.env[key];
|
||||
return [val, val !== undefined && val !== ""];
|
||||
}
|
||||
}
|
||||
exports["default"] = ConfigsParser;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 4753:
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.AuthTokenId = void 0;
|
||||
var AuthTokenId;
|
||||
(function (AuthTokenId) {
|
||||
// github specific token
|
||||
AuthTokenId["GITHUB_TOKEN"] = "GITHUB_TOKEN";
|
||||
// gitlab specific token
|
||||
AuthTokenId["GITLAB_TOKEN"] = "GITLAB_TOKEN";
|
||||
// codeberg specific token
|
||||
AuthTokenId["CODEBERG_TOKEN"] = "CODEBERG_TOKEN";
|
||||
// generic git token
|
||||
AuthTokenId["GIT_TOKEN"] = "GIT_TOKEN";
|
||||
})(AuthTokenId = exports.AuthTokenId || (exports.AuthTokenId = {}));
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 6618:
|
||||
@@ -288,6 +348,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const args_utils_1 = __nccwpck_require__(8048);
|
||||
const configs_parser_1 = __importDefault(__nccwpck_require__(5799));
|
||||
const git_client_factory_1 = __importDefault(__nccwpck_require__(8550));
|
||||
class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
@@ -305,15 +366,28 @@ class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
throw error;
|
||||
}
|
||||
const folder = args.folder ?? this.getDefaultFolder();
|
||||
const targetBranches = [...new Set((0, args_utils_1.getAsCommaSeparatedList)(args.targetBranch))];
|
||||
const bpBranchNames = [...new Set(args.bpBranchName ? ((0, args_utils_1.getAsCleanedCommaSeparatedList)(args.bpBranchName) ?? []) : [])];
|
||||
if (bpBranchNames.length > 1 && bpBranchNames.length != targetBranches.length) {
|
||||
throw new Error(`The number of backport branch names, if provided, must match the number of target branches or just one, provided ${bpBranchNames.length} branch names instead`);
|
||||
}
|
||||
// setup the auth token
|
||||
let token = args.auth;
|
||||
if (token === undefined) {
|
||||
this.logger.info("Auth argument not provided, checking available tokens from env..");
|
||||
token = this.getGitTokenFromEnv(this.gitClient.getClientType());
|
||||
if (!token) {
|
||||
this.logger.info("Git token not set in the env");
|
||||
}
|
||||
}
|
||||
return {
|
||||
dryRun: args.dryRun,
|
||||
auth: args.auth,
|
||||
auth: token,
|
||||
folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`,
|
||||
targetBranch: args.targetBranch,
|
||||
mergeStrategy: args.strategy,
|
||||
mergeStrategyOption: args.strategyOption,
|
||||
originalPullRequest: pr,
|
||||
backportPullRequest: this.getDefaultBackportPullRequest(pr, args),
|
||||
backportPullRequests: this.generateBackportPullRequestsData(pr, args, targetBranches, bpBranchNames),
|
||||
git: {
|
||||
user: args.gitUser ?? this.gitClient.getDefaultGitUser(),
|
||||
email: args.gitEmail ?? this.gitClient.getDefaultGitEmail(),
|
||||
@@ -330,7 +404,7 @@ class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
* @param targetBranch target branch where the backport should be applied
|
||||
* @returns {GitPullRequest}
|
||||
*/
|
||||
getDefaultBackportPullRequest(originalPullRequest, args) {
|
||||
generateBackportPullRequestsData(originalPullRequest, args, targetBranches, bpBranchNames) {
|
||||
const reviewers = args.reviewers ?? [];
|
||||
if (reviewers.length == 0 && args.inheritReviewers) {
|
||||
// inherit only if args.reviewers is empty and args.inheritReviewers set to true
|
||||
@@ -345,29 +419,37 @@ class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
if (args.inheritLabels) {
|
||||
labels.push(...originalPullRequest.labels);
|
||||
}
|
||||
let backportBranch = args.bpBranchName;
|
||||
if (backportBranch === undefined || backportBranch.trim() === "") {
|
||||
// for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects
|
||||
const concatenatedCommits = originalPullRequest.commits.map(c => c.slice(0, 7)).join("-");
|
||||
backportBranch = `bp-${args.targetBranch}-${concatenatedCommits}`;
|
||||
}
|
||||
if (backportBranch.length > 250) {
|
||||
this.logger.warn(`Backport branch (length=${backportBranch.length}) exceeded the max length of 250 chars, branch name truncated!`);
|
||||
backportBranch = backportBranch.slice(0, 250);
|
||||
}
|
||||
return {
|
||||
owner: originalPullRequest.targetRepo.owner,
|
||||
repo: originalPullRequest.targetRepo.project,
|
||||
head: backportBranch,
|
||||
base: args.targetBranch,
|
||||
title: args.title ?? `[${args.targetBranch}] ${originalPullRequest.title}`,
|
||||
// preserve new line chars
|
||||
body: body.replace(/\\n/g, "\n").replace(/\\r/g, "\r"),
|
||||
reviewers: [...new Set(reviewers)],
|
||||
assignees: [...new Set(args.assignees)],
|
||||
labels: [...new Set(labels)],
|
||||
comments: args.comments?.map(c => c.replace(/\\n/g, "\n").replace(/\\r/g, "\r")) ?? [],
|
||||
};
|
||||
return targetBranches.map((tb, idx) => {
|
||||
// if there multiple branch names take the corresponding one, otherwise get the the first one if it exists
|
||||
let backportBranch = bpBranchNames.length > 1 ? bpBranchNames[idx] : bpBranchNames[0];
|
||||
if (backportBranch === undefined || backportBranch.trim() === "") {
|
||||
// for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects
|
||||
const concatenatedCommits = originalPullRequest.commits.map(c => c.slice(0, 7)).join("-");
|
||||
backportBranch = `bp-${tb}-${concatenatedCommits}`;
|
||||
}
|
||||
else if (bpBranchNames.length == 1 && targetBranches.length > 1) {
|
||||
// multiple targets and single custom backport branch name we need to differentiate branch names
|
||||
// so append "-${tb}" to the provided name
|
||||
backportBranch = backportBranch + `-${tb}`;
|
||||
}
|
||||
if (backportBranch.length > 250) {
|
||||
this.logger.warn(`Backport branch (length=${backportBranch.length}) exceeded the max length of 250 chars, branch name truncated!`);
|
||||
backportBranch = backportBranch.slice(0, 250);
|
||||
}
|
||||
return {
|
||||
owner: originalPullRequest.targetRepo.owner,
|
||||
repo: originalPullRequest.targetRepo.project,
|
||||
head: backportBranch,
|
||||
base: tb,
|
||||
title: args.title ?? `[${tb}] ${originalPullRequest.title}`,
|
||||
// preserve new line chars
|
||||
body: body.replace(/\\n/g, "\n").replace(/\\r/g, "\r"),
|
||||
reviewers: [...new Set(reviewers)],
|
||||
assignees: [...new Set(args.assignees)],
|
||||
labels: [...new Set(labels)],
|
||||
comments: args.comments?.map(c => c.replace(/\\n/g, "\n").replace(/\\r/g, "\r")) ?? [],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
exports["default"] = PullRequestConfigsParser;
|
||||
@@ -436,10 +518,12 @@ class GitCLIService {
|
||||
this.logger.info(`Cloning repository ${from} to ${to}`);
|
||||
if (!fs_1.default.existsSync(to)) {
|
||||
await (0, simple_git_1.default)().clone(this.remoteWithAuth(from), to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.logger.warn(`Folder ${to} already exist. Won't clone`);
|
||||
}
|
||||
this.logger.info(`Folder ${to} already exist. Won't clone`);
|
||||
// checkout to the proper branch
|
||||
this.logger.info(`Checking out branch ${branch}`);
|
||||
await this.git(to).checkout(branch);
|
||||
}
|
||||
/**
|
||||
* Create a new branch starting from the current one and checkout in it
|
||||
@@ -551,6 +635,9 @@ class GitClientFactory {
|
||||
case git_types_1.GitClientType.GITLAB:
|
||||
GitClientFactory.instance = new gitlab_client_1.default(authToken, apiUrl);
|
||||
break;
|
||||
case git_types_1.GitClientType.CODEBERG:
|
||||
GitClientFactory.instance = new github_client_1.default(authToken, apiUrl, true);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid git service type received: ${type}`);
|
||||
}
|
||||
@@ -592,6 +679,9 @@ const inferGitClient = (prUrl) => {
|
||||
else if (stdPrUrl.includes(git_types_1.GitClientType.GITLAB.toString())) {
|
||||
return git_types_1.GitClientType.GITLAB;
|
||||
}
|
||||
else if (stdPrUrl.includes(git_types_1.GitClientType.CODEBERG.toString())) {
|
||||
return git_types_1.GitClientType.CODEBERG;
|
||||
}
|
||||
throw new Error(`Remote git service not recognized from pr url: ${prUrl}`);
|
||||
};
|
||||
exports.inferGitClient = inferGitClient;
|
||||
@@ -625,6 +715,7 @@ var GitClientType;
|
||||
(function (GitClientType) {
|
||||
GitClientType["GITHUB"] = "github";
|
||||
GitClientType["GITLAB"] = "gitlab";
|
||||
GitClientType["CODEBERG"] = "codeberg";
|
||||
})(GitClientType = exports.GitClientType || (exports.GitClientType = {}));
|
||||
var GitRepoState;
|
||||
(function (GitRepoState) {
|
||||
@@ -646,19 +737,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const git_types_1 = __nccwpck_require__(750);
|
||||
const github_mapper_1 = __importDefault(__nccwpck_require__(5764));
|
||||
const octokit_factory_1 = __importDefault(__nccwpck_require__(4257));
|
||||
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
|
||||
class GitHubClient {
|
||||
constructor(token, apiUrl) {
|
||||
constructor(token, apiUrl, isForCodeberg = false) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.isForCodeberg = isForCodeberg;
|
||||
this.logger = logger_service_factory_1.default.getLogger();
|
||||
this.octokit = octokit_factory_1.default.getOctokit(token, this.apiUrl);
|
||||
this.mapper = new github_mapper_1.default();
|
||||
}
|
||||
getClientType() {
|
||||
return this.isForCodeberg ? git_types_1.GitClientType.CODEBERG : git_types_1.GitClientType.GITHUB;
|
||||
}
|
||||
// READ
|
||||
getDefaultGitUser() {
|
||||
return "GitHub";
|
||||
return this.apiUrl.includes(git_types_1.GitClientType.CODEBERG.toString()) ? "Codeberg" : "GitHub";
|
||||
}
|
||||
getDefaultGitEmail() {
|
||||
return "noreply@github.com";
|
||||
@@ -791,9 +887,9 @@ class GitHubMapper {
|
||||
state: this.mapGitState(pr.state),
|
||||
merged: pr.merged ?? false,
|
||||
mergedBy: pr.merged_by?.login,
|
||||
reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => r?.login)),
|
||||
assignees: pr.assignees.filter(r => "login" in r).map(r => r.login),
|
||||
labels: pr.labels.map(l => l.name),
|
||||
reviewers: pr.requested_reviewers?.filter(r => "login" in r).map((r => r?.login)) ?? [],
|
||||
assignees: pr.assignees?.filter(r => "login" in r).map(r => r.login) ?? [],
|
||||
labels: pr.labels?.map(l => l.name) ?? [],
|
||||
sourceRepo: await this.mapSourceRepo(pr),
|
||||
targetRepo: await this.mapTargetRepo(pr),
|
||||
nCommits: pr.commits,
|
||||
@@ -866,6 +962,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const git_types_1 = __nccwpck_require__(750);
|
||||
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
|
||||
const gitlab_mapper_1 = __importDefault(__nccwpck_require__(2675));
|
||||
const axios_1 = __importDefault(__nccwpck_require__(8757));
|
||||
@@ -886,6 +983,9 @@ class GitLabClient {
|
||||
});
|
||||
this.mapper = new gitlab_mapper_1.default(this.client);
|
||||
}
|
||||
getClientType() {
|
||||
return git_types_1.GitClientType.GITLAB;
|
||||
}
|
||||
getDefaultGitUser() {
|
||||
return "Gitlab";
|
||||
}
|
||||
@@ -1001,9 +1101,15 @@ class GitLabClient {
|
||||
* @returns {{owner: string, project: string}}
|
||||
*/
|
||||
extractMergeRequestData(mrUrl) {
|
||||
const elems = mrUrl.replace("/-/", "/").split("/");
|
||||
const { pathname } = new URL(mrUrl);
|
||||
const elems = pathname.substring(1).replace("/-/", "/").split("/");
|
||||
let namespace = "";
|
||||
for (let i = 0; i < elems.length - 3; i++) {
|
||||
namespace += elems[i] + "/";
|
||||
}
|
||||
namespace = namespace.substring(0, namespace.length - 1);
|
||||
return {
|
||||
namespace: elems[elems.length - 4],
|
||||
namespace: namespace,
|
||||
project: elems[elems.length - 3],
|
||||
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
|
||||
};
|
||||
@@ -1115,22 +1221,31 @@ class ConsoleLoggerService {
|
||||
this.logger = new logger_1.default();
|
||||
this.verbose = verbose;
|
||||
}
|
||||
setContext(newContext) {
|
||||
this.context = newContext;
|
||||
}
|
||||
clearContext() {
|
||||
this.context = undefined;
|
||||
}
|
||||
trace(message) {
|
||||
this.logger.log("TRACE", message);
|
||||
this.logger.log("TRACE", this.fromContext(message));
|
||||
}
|
||||
debug(message) {
|
||||
if (this.verbose) {
|
||||
this.logger.log("DEBUG", message);
|
||||
this.logger.log("DEBUG", this.fromContext(message));
|
||||
}
|
||||
}
|
||||
info(message) {
|
||||
this.logger.log("INFO", message);
|
||||
this.logger.log("INFO", this.fromContext(message));
|
||||
}
|
||||
warn(message) {
|
||||
this.logger.log("WARN", message);
|
||||
this.logger.log("WARN", this.fromContext(message));
|
||||
}
|
||||
error(message) {
|
||||
this.logger.log("ERROR", message);
|
||||
this.logger.log("ERROR", this.fromContext(message));
|
||||
}
|
||||
fromContext(msg) {
|
||||
return this.context ? `[${this.context}] ${msg}` : msg;
|
||||
}
|
||||
}
|
||||
exports["default"] = ConsoleLoggerService;
|
||||
@@ -1236,45 +1351,68 @@ class Runner {
|
||||
}
|
||||
// 2. init git service
|
||||
const gitClientType = (0, git_util_1.inferGitClient)(args.pullRequest);
|
||||
// right now the apiVersion is set to v4
|
||||
const apiUrl = (0, git_util_1.inferGitApiUrl)(args.pullRequest);
|
||||
// the api version is ignored in case of github
|
||||
const apiUrl = (0, git_util_1.inferGitApiUrl)(args.pullRequest, gitClientType === git_types_1.GitClientType.CODEBERG ? "v1" : undefined);
|
||||
const gitApi = git_client_factory_1.default.getOrCreate(gitClientType, args.auth, apiUrl);
|
||||
// 3. parse configs
|
||||
this.logger.debug("Parsing configs..");
|
||||
const configs = await new pr_configs_parser_1.default().parseAndValidate(args);
|
||||
const originalPR = configs.originalPullRequest;
|
||||
const backportPR = configs.backportPullRequest;
|
||||
const backportPRs = configs.backportPullRequests;
|
||||
// start local git operations
|
||||
const git = new git_cli_1.default(configs.auth, configs.git);
|
||||
const failures = [];
|
||||
// we need sequential backporting as they will operate on the same folder
|
||||
// avoid cloning the same repo multiple times
|
||||
for (const pr of backportPRs) {
|
||||
try {
|
||||
await this.executeBackport(configs, pr, {
|
||||
gitClientType: gitClientType,
|
||||
gitClientApi: gitApi,
|
||||
gitCli: git,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
|
||||
failures.push(error);
|
||||
}
|
||||
}
|
||||
if (failures.length > 0) {
|
||||
throw new Error(`Failure occurred during one of the backports: [${failures.join(" ; ")}]`);
|
||||
}
|
||||
}
|
||||
async executeBackport(configs, backportPR, git) {
|
||||
this.logger.setContext(backportPR.base);
|
||||
const originalPR = configs.originalPullRequest;
|
||||
// 4. clone the repository
|
||||
this.logger.debug("Cloning repo..");
|
||||
await git.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, configs.targetBranch);
|
||||
await git.gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
|
||||
// 5. create new branch from target one and checkout
|
||||
this.logger.debug("Creating local branch..");
|
||||
await git.createLocalBranch(configs.folder, backportPR.head);
|
||||
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
|
||||
// 6. fetch pull request remote if source owner != target owner or pull request still open
|
||||
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
|
||||
configs.originalPullRequest.state === "open") {
|
||||
this.logger.debug("Fetching pull request remote..");
|
||||
const prefix = gitClientType === git_types_1.GitClientType.GITHUB ? "pull" : "merge-requests"; // default is for gitlab
|
||||
await git.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||
const prefix = git.gitClientType === git_types_1.GitClientType.GITLAB ? "merge-requests" : "pull"; // default is for gitlab
|
||||
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||
}
|
||||
// 7. apply all changes to the new branch
|
||||
this.logger.debug("Cherry picking commits..");
|
||||
for (const sha of originalPR.commits) {
|
||||
await git.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption);
|
||||
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption);
|
||||
}
|
||||
if (!configs.dryRun) {
|
||||
// 8. push the new branch to origin
|
||||
await git.push(configs.folder, backportPR.head);
|
||||
await git.gitCli.push(configs.folder, backportPR.head);
|
||||
// 9. create pull request new branch -> target branch (using octokit)
|
||||
const prUrl = await gitApi.createPullRequest(backportPR);
|
||||
const prUrl = await git.gitClientApi.createPullRequest(backportPR);
|
||||
this.logger.info(`Pull request created: ${prUrl}`);
|
||||
}
|
||||
else {
|
||||
this.logger.warn("Pull request creation and remote push skipped");
|
||||
this.logger.info(`${JSON.stringify(backportPR, null, 2)}`);
|
||||
}
|
||||
this.logger.clearContext();
|
||||
}
|
||||
}
|
||||
exports["default"] = Runner;
|
||||
@@ -19198,7 +19336,7 @@ exports.suggestSimilar = suggestSimilar;
|
||||
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
// Axios v1.4.0 Copyright (c) 2023 Matt Zabriskie and contributors
|
||||
// Axios v1.6.0 Copyright (c) 2023 Matt Zabriskie and contributors
|
||||
|
||||
|
||||
const FormData$1 = __nccwpck_require__(4334);
|
||||
@@ -19768,8 +19906,9 @@ const reduceDescriptors = (obj, reducer) => {
|
||||
const reducedDescriptors = {};
|
||||
|
||||
forEach(descriptors, (descriptor, name) => {
|
||||
if (reducer(descriptor, name, obj) !== false) {
|
||||
reducedDescriptors[name] = descriptor;
|
||||
let ret;
|
||||
if ((ret = reducer(descriptor, name, obj)) !== false) {
|
||||
reducedDescriptors[name] = ret || descriptor;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20553,10 +20692,6 @@ function formDataToJSON(formData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const DEFAULT_CONTENT_TYPE = {
|
||||
'Content-Type': undefined
|
||||
};
|
||||
|
||||
/**
|
||||
* It takes a string, tries to parse it, and if it fails, it returns the stringified version
|
||||
* of the input
|
||||
@@ -20695,19 +20830,16 @@ const defaults = {
|
||||
|
||||
headers: {
|
||||
common: {
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': undefined
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
|
||||
utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch'], (method) => {
|
||||
defaults.headers[method] = {};
|
||||
});
|
||||
|
||||
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
|
||||
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
|
||||
});
|
||||
|
||||
const defaults$1 = defaults;
|
||||
|
||||
// RawAxiosHeaders whose duplicates are ignored by node
|
||||
@@ -21041,7 +21173,17 @@ class AxiosHeaders {
|
||||
|
||||
AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']);
|
||||
|
||||
utils.freezeMethods(AxiosHeaders.prototype);
|
||||
// reserved names hotfix
|
||||
utils.reduceDescriptors(AxiosHeaders.prototype, ({value}, key) => {
|
||||
let mapped = key[0].toUpperCase() + key.slice(1); // map `set` => `Set`
|
||||
return {
|
||||
get: () => value,
|
||||
set(headerValue) {
|
||||
this[mapped] = headerValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
utils.freezeMethods(AxiosHeaders);
|
||||
|
||||
const AxiosHeaders$1 = AxiosHeaders;
|
||||
@@ -21161,7 +21303,7 @@ function buildFullPath(baseURL, requestedURL) {
|
||||
return requestedURL;
|
||||
}
|
||||
|
||||
const VERSION = "1.4.0";
|
||||
const VERSION = "1.6.0";
|
||||
|
||||
function parseProtocol(url) {
|
||||
const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url);
|
||||
@@ -21765,6 +21907,18 @@ const wrapAsync = (asyncExecutor) => {
|
||||
})
|
||||
};
|
||||
|
||||
const resolveFamily = ({address, family}) => {
|
||||
if (!utils.isString(address)) {
|
||||
throw TypeError('address must be a string');
|
||||
}
|
||||
return ({
|
||||
address,
|
||||
family: family || (address.indexOf('.') < 0 ? 6 : 4)
|
||||
});
|
||||
};
|
||||
|
||||
const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : {address, family});
|
||||
|
||||
/*eslint consistent-return:0*/
|
||||
const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
|
||||
@@ -21775,15 +21929,16 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
let rejected = false;
|
||||
let req;
|
||||
|
||||
if (lookup && utils.isAsyncFn(lookup)) {
|
||||
lookup = callbackify$1(lookup, (entry) => {
|
||||
if(utils.isString(entry)) {
|
||||
entry = [entry, entry.indexOf('.') < 0 ? 6 : 4];
|
||||
} else if (!utils.isArray(entry)) {
|
||||
throw new TypeError('lookup async function must return an array [ip: string, family: number]]')
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
if (lookup) {
|
||||
const _lookup = callbackify$1(lookup, (value) => utils.isArray(value) ? value : [value]);
|
||||
// hotfix to support opt.all option which is required for node 20.x
|
||||
lookup = (hostname, opt, cb) => {
|
||||
_lookup(hostname, opt, (err, arg0, arg1) => {
|
||||
const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)];
|
||||
|
||||
opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// temporary internal emitter until the AxiosRequest class will be implemented
|
||||
@@ -22010,11 +22165,13 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
auth,
|
||||
protocol,
|
||||
family,
|
||||
lookup,
|
||||
beforeRedirect: dispatchBeforeRedirect,
|
||||
beforeRedirects: {}
|
||||
};
|
||||
|
||||
// cacheable-lookup integration hotfix
|
||||
!utils.isUndefined(lookup) && (options.lookup = lookup);
|
||||
|
||||
if (config.socketPath) {
|
||||
options.socketPath = config.socketPath;
|
||||
} else {
|
||||
@@ -22088,7 +22245,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
delete res.headers['content-encoding'];
|
||||
}
|
||||
|
||||
switch (res.headers['content-encoding']) {
|
||||
switch ((res.headers['content-encoding'] || '').toLowerCase()) {
|
||||
/*eslint default-case:0*/
|
||||
case 'gzip':
|
||||
case 'x-gzip':
|
||||
@@ -22184,7 +22341,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
}
|
||||
response.data = responseData;
|
||||
} catch (err) {
|
||||
reject(AxiosError.from(err, null, config, response.request, response));
|
||||
return reject(AxiosError.from(err, null, config, response.request, response));
|
||||
}
|
||||
settle(resolve, reject, response);
|
||||
});
|
||||
@@ -22221,7 +22378,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
// This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.
|
||||
const timeout = parseInt(config.timeout, 10);
|
||||
|
||||
if (isNaN(timeout)) {
|
||||
if (Number.isNaN(timeout)) {
|
||||
reject(new AxiosError(
|
||||
'error trying to parse `config.timeout` to int',
|
||||
AxiosError.ERR_BAD_OPTION_VALUE,
|
||||
@@ -22440,11 +22597,16 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
|
||||
}
|
||||
}
|
||||
|
||||
let contentType;
|
||||
|
||||
if (utils.isFormData(requestData)) {
|
||||
if (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv) {
|
||||
requestHeaders.setContentType(false); // Let the browser set it
|
||||
} else {
|
||||
requestHeaders.setContentType('multipart/form-data;', false); // mobile/desktop app frameworks
|
||||
} else if(!requestHeaders.getContentType(/^\s*multipart\/form-data/)){
|
||||
requestHeaders.setContentType('multipart/form-data'); // mobile/desktop app frameworks
|
||||
} else if(utils.isString(contentType = requestHeaders.getContentType())){
|
||||
// fix semicolon duplication issue for ReactNative FormData implementation
|
||||
requestHeaders.setContentType(contentType.replace(/^\s*(multipart\/form-data);+/, '$1'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22562,8 +22724,8 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
|
||||
// Specifically not if we're in a web worker, or react-native.
|
||||
if (platform.isStandardBrowserEnv) {
|
||||
// Add xsrf header
|
||||
const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath))
|
||||
&& config.xsrfCookieName && cookies.read(config.xsrfCookieName);
|
||||
// regarding CVE-2023-45857 config.withCredentials condition was removed temporarily
|
||||
const xsrfValue = isURLSameOrigin(fullPath) && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
|
||||
|
||||
if (xsrfValue) {
|
||||
requestHeaders.set(config.xsrfHeaderName, xsrfValue);
|
||||
@@ -22637,7 +22799,7 @@ const knownAdapters = {
|
||||
};
|
||||
|
||||
utils.forEach(knownAdapters, (fn, value) => {
|
||||
if(fn) {
|
||||
if (fn) {
|
||||
try {
|
||||
Object.defineProperty(fn, 'name', {value});
|
||||
} catch (e) {
|
||||
@@ -22647,6 +22809,10 @@ utils.forEach(knownAdapters, (fn, value) => {
|
||||
}
|
||||
});
|
||||
|
||||
const renderReason = (reason) => `- ${reason}`;
|
||||
|
||||
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
|
||||
|
||||
const adapters = {
|
||||
getAdapter: (adapters) => {
|
||||
adapters = utils.isArray(adapters) ? adapters : [adapters];
|
||||
@@ -22655,32 +22821,46 @@ const adapters = {
|
||||
let nameOrAdapter;
|
||||
let adapter;
|
||||
|
||||
const rejectedReasons = {};
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
nameOrAdapter = adapters[i];
|
||||
if((adapter = utils.isString(nameOrAdapter) ? knownAdapters[nameOrAdapter.toLowerCase()] : nameOrAdapter)) {
|
||||
let id;
|
||||
|
||||
adapter = nameOrAdapter;
|
||||
|
||||
if (!isResolvedHandle(nameOrAdapter)) {
|
||||
adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
|
||||
|
||||
if (adapter === undefined) {
|
||||
throw new AxiosError(`Unknown adapter '${id}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter) {
|
||||
break;
|
||||
}
|
||||
|
||||
rejectedReasons[id || '#' + i] = adapter;
|
||||
}
|
||||
|
||||
if (!adapter) {
|
||||
if (adapter === false) {
|
||||
throw new AxiosError(
|
||||
`Adapter ${nameOrAdapter} is not supported by the environment`,
|
||||
'ERR_NOT_SUPPORT'
|
||||
|
||||
const reasons = Object.entries(rejectedReasons)
|
||||
.map(([id, state]) => `adapter ${id} ` +
|
||||
(state === false ? 'is not supported by the environment' : 'is not available in the build')
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
utils.hasOwnProp(knownAdapters, nameOrAdapter) ?
|
||||
`Adapter '${nameOrAdapter}' is not available in the build` :
|
||||
`Unknown adapter '${nameOrAdapter}'`
|
||||
let s = length ?
|
||||
(reasons.length > 1 ? 'since :\n' + reasons.map(renderReason).join('\n') : ' ' + renderReason(reasons[0])) :
|
||||
'as no adapter specified';
|
||||
|
||||
throw new AxiosError(
|
||||
`There is no suitable adapter to dispatch the request ` + s,
|
||||
'ERR_NOT_SUPPORT'
|
||||
);
|
||||
}
|
||||
|
||||
if (!utils.isFunction(adapter)) {
|
||||
throw new TypeError('adapter is not a function');
|
||||
}
|
||||
|
||||
return adapter;
|
||||
},
|
||||
adapters: knownAdapters
|
||||
@@ -23011,15 +23191,13 @@ class Axios {
|
||||
// Set config.method
|
||||
config.method = (config.method || this.defaults.method || 'get').toLowerCase();
|
||||
|
||||
let contextHeaders;
|
||||
|
||||
// Flatten headers
|
||||
contextHeaders = headers && utils.merge(
|
||||
let contextHeaders = headers && utils.merge(
|
||||
headers.common,
|
||||
headers[config.method]
|
||||
);
|
||||
|
||||
contextHeaders && utils.forEach(
|
||||
headers && utils.forEach(
|
||||
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
|
||||
(method) => {
|
||||
delete headers[method];
|
||||
@@ -23429,6 +23607,8 @@ axios.AxiosHeaders = AxiosHeaders$1;
|
||||
|
||||
axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
|
||||
|
||||
axios.getAdapter = adapters.getAdapter;
|
||||
|
||||
axios.HttpStatusCode = HttpStatusCode$1;
|
||||
|
||||
axios.default = axios;
|
||||
@@ -23443,7 +23623,7 @@ module.exports = axios;
|
||||
/***/ ((module) => {
|
||||
|
||||
"use strict";
|
||||
module.exports = JSON.parse('{"name":"@kie/git-backporting","version":"4.3.0","description":"Git backporting is a tool to execute automatic pull request git backporting.","author":"","license":"MIT","private":false,"main":"./dist/gha/index.js","bin":{"git-backporting":"./dist/cli/index.js"},"files":["dist/cli/index.js"],"publishConfig":{"access":"public"},"scripts":{"prepare":"husky install","clean":"rm -rf ./build ./dist","compile":"tsc -p tsconfig.json && tsc-alias -p tsconfig.json","package":"npm run package:cli && npm run package:gha","package:cli":"ncc build ./build/src/bin/cli.js -o dist/cli","package:gha":"ncc build ./build/src/bin/gha.js -o dist/gha","build":"npm run clean && npm run compile && npm run package","test":"jest --silent","test:report":"npm test -- --coverage --testResultsProcessor=jest-sonar-reporter","lint":"eslint . --ext .ts","lint:fix":"npm run lint -- --fix","ts-node":"ts-node","postversion":"npm run build && git add dist && rm -rf build","release":"release-it","release:branch":"git checkout -b release/$(release-it --release-version) main","release:prepare":"release-it --no-npm.publish --no-github.release --no-git.push --no-git.tag --no-git.requireUpstream","release:prepare:all":"npm run release:branch && npm run release:prepare"},"repository":{"type":"git","url":"git+https://github.com/kiegroup/git-backporting.git"},"keywords":["backporting","pull-requests","merge-requests","github-action","cherry-pick"],"bugs":{"url":"https://github.com/kiegroup/git-backporting/issues"},"homepage":"https://github.com/kiegroup/git-backporting#readme","devDependencies":{"@commitlint/cli":"^17.4.0","@commitlint/config-conventional":"^17.4.0","@gitbeaker/rest":"^39.1.0","@kie/mock-github":"^1.1.0","@octokit/webhooks-types":"^6.8.0","@release-it/conventional-changelog":"^7.0.0","@types/fs-extra":"^9.0.13","@types/jest":"^29.2.4","@types/node":"^18.11.17","@typescript-eslint/eslint-plugin":"^5.47.0","@typescript-eslint/parser":"^5.47.0","@vercel/ncc":"^0.36.0","eslint":"^8.30.0","husky":"^8.0.2","jest":"^29.0.0","jest-sonar-reporter":"^2.0.0","release-it":"^16.1.3","semver":"^7.3.8","ts-jest":"^29.0.0","ts-node":"^10.8.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.0","typescript":"^4.9.3"},"dependencies":{"@actions/core":"^1.10.0","@octokit/rest":"^18.12.0","axios":"^1.4.0","commander":"^9.3.0","fs-extra":"^11.1.0","https":"^1.0.0","simple-git":"^3.15.1"}}');
|
||||
module.exports = JSON.parse('{"name":"@kie/git-backporting","version":"4.5.0","description":"Git backporting is a tool to execute automatic pull request git backporting.","author":"","license":"MIT","private":false,"main":"./dist/gha/index.js","bin":{"git-backporting":"./dist/cli/index.js"},"files":["dist/cli/index.js"],"publishConfig":{"access":"public"},"scripts":{"prepare":"husky install","clean":"rm -rf ./build ./dist","compile":"tsc -p tsconfig.json && tsc-alias -p tsconfig.json","package":"npm run package:cli && npm run package:gha","package:cli":"ncc build ./build/src/bin/cli.js -o dist/cli","package:gha":"ncc build ./build/src/bin/gha.js -o dist/gha","build":"npm run clean && npm run compile && npm run package","test":"jest --silent","test:report":"npm test -- --coverage --testResultsProcessor=jest-sonar-reporter","lint":"eslint . --ext .ts","lint:fix":"npm run lint -- --fix","ts-node":"ts-node","postversion":"npm run build && git add dist && rm -rf build","release":"release-it","release:branch":"git checkout -b release/$(release-it --release-version) main","release:prepare":"release-it --no-npm.publish --no-github.release --no-git.push --no-git.tag --no-git.requireUpstream","release:prepare:all":"npm run release:branch && npm run release:prepare"},"repository":{"type":"git","url":"git+https://github.com/kiegroup/git-backporting.git"},"keywords":["backporting","pull-requests","merge-requests","github-action","cherry-pick"],"bugs":{"url":"https://github.com/kiegroup/git-backporting/issues"},"homepage":"https://github.com/kiegroup/git-backporting#readme","devDependencies":{"@commitlint/cli":"^17.4.0","@commitlint/config-conventional":"^17.4.0","@gitbeaker/rest":"^39.1.0","@kie/mock-github":"^1.1.0","@octokit/webhooks-types":"^6.8.0","@release-it/conventional-changelog":"^7.0.0","@types/fs-extra":"^9.0.13","@types/jest":"^29.2.4","@types/node":"^18.11.17","@typescript-eslint/eslint-plugin":"^5.47.0","@typescript-eslint/parser":"^5.47.0","@vercel/ncc":"^0.36.0","eslint":"^8.30.0","husky":"^8.0.2","jest":"^29.0.0","jest-sonar-reporter":"^2.0.0","release-it":"^16.1.3","semver":"^7.3.8","ts-jest":"^29.0.0","ts-node":"^10.8.1","tsc-alias":"^1.8.2","tsconfig-paths":"^4.1.0","typescript":"^4.9.3"},"dependencies":{"@actions/core":"^1.10.0","@octokit/rest":"^18.12.0","axios":"^1.4.0","commander":"^9.3.0","fs-extra":"^11.1.0","https":"^1.0.0","simple-git":"^3.15.1"}}');
|
||||
|
||||
/***/ }),
|
||||
|
||||
|
||||
392
dist/gha/index.js
vendored
392
dist/gha/index.js
vendored
@@ -40,8 +40,8 @@ class ArgsParser {
|
||||
parse() {
|
||||
const args = this.readArgs();
|
||||
// validate and fill with defaults
|
||||
if (!args.pullRequest || !args.targetBranch) {
|
||||
throw new Error("Missing option: pull request and target branch must be provided");
|
||||
if (!args.pullRequest || !args.targetBranch || args.targetBranch.trim().length == 0) {
|
||||
throw new Error("Missing option: pull request and target branches must be provided");
|
||||
}
|
||||
return {
|
||||
pullRequest: args.pullRequest,
|
||||
@@ -221,7 +221,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const configs_types_1 = __nccwpck_require__(4753);
|
||||
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
|
||||
const git_types_1 = __nccwpck_require__(750);
|
||||
/**
|
||||
* Abstract configuration parser class in charge to parse
|
||||
* Args and produces a common Configs object
|
||||
@@ -243,10 +245,68 @@ class ConfigsParser {
|
||||
}
|
||||
return Promise.resolve(configs);
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
* @param gitType
|
||||
* @returns tuple where
|
||||
* - the first element is the corresponding env value
|
||||
* - the second element is true if the value is not undefined nor empty
|
||||
*/
|
||||
getGitTokenFromEnv(gitType) {
|
||||
let [token] = this.getEnv(configs_types_1.AuthTokenId.GIT_TOKEN);
|
||||
let [specToken, specOk] = [undefined, false];
|
||||
if (git_types_1.GitClientType.GITHUB == gitType) {
|
||||
[specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITHUB_TOKEN);
|
||||
}
|
||||
else if (git_types_1.GitClientType.GITLAB == gitType) {
|
||||
[specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITLAB_TOKEN);
|
||||
}
|
||||
else if (git_types_1.GitClientType.CODEBERG == gitType) {
|
||||
[specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.CODEBERG_TOKEN);
|
||||
}
|
||||
if (specOk) {
|
||||
token = specToken;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
/**
|
||||
* Get process env variable given the input key string
|
||||
* @param key
|
||||
* @returns tuple where
|
||||
* - the first element is the corresponding env value
|
||||
* - the second element is true if the value is not undefined nor empty
|
||||
*/
|
||||
getEnv(key) {
|
||||
const val = process.env[key];
|
||||
return [val, val !== undefined && val !== ""];
|
||||
}
|
||||
}
|
||||
exports["default"] = ConfigsParser;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 4753:
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.AuthTokenId = void 0;
|
||||
var AuthTokenId;
|
||||
(function (AuthTokenId) {
|
||||
// github specific token
|
||||
AuthTokenId["GITHUB_TOKEN"] = "GITHUB_TOKEN";
|
||||
// gitlab specific token
|
||||
AuthTokenId["GITLAB_TOKEN"] = "GITLAB_TOKEN";
|
||||
// codeberg specific token
|
||||
AuthTokenId["CODEBERG_TOKEN"] = "CODEBERG_TOKEN";
|
||||
// generic git token
|
||||
AuthTokenId["GIT_TOKEN"] = "GIT_TOKEN";
|
||||
})(AuthTokenId = exports.AuthTokenId || (exports.AuthTokenId = {}));
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 6618:
|
||||
@@ -258,6 +318,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const args_utils_1 = __nccwpck_require__(8048);
|
||||
const configs_parser_1 = __importDefault(__nccwpck_require__(5799));
|
||||
const git_client_factory_1 = __importDefault(__nccwpck_require__(8550));
|
||||
class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
@@ -275,15 +336,28 @@ class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
throw error;
|
||||
}
|
||||
const folder = args.folder ?? this.getDefaultFolder();
|
||||
const targetBranches = [...new Set((0, args_utils_1.getAsCommaSeparatedList)(args.targetBranch))];
|
||||
const bpBranchNames = [...new Set(args.bpBranchName ? ((0, args_utils_1.getAsCleanedCommaSeparatedList)(args.bpBranchName) ?? []) : [])];
|
||||
if (bpBranchNames.length > 1 && bpBranchNames.length != targetBranches.length) {
|
||||
throw new Error(`The number of backport branch names, if provided, must match the number of target branches or just one, provided ${bpBranchNames.length} branch names instead`);
|
||||
}
|
||||
// setup the auth token
|
||||
let token = args.auth;
|
||||
if (token === undefined) {
|
||||
this.logger.info("Auth argument not provided, checking available tokens from env..");
|
||||
token = this.getGitTokenFromEnv(this.gitClient.getClientType());
|
||||
if (!token) {
|
||||
this.logger.info("Git token not set in the env");
|
||||
}
|
||||
}
|
||||
return {
|
||||
dryRun: args.dryRun,
|
||||
auth: args.auth,
|
||||
auth: token,
|
||||
folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`,
|
||||
targetBranch: args.targetBranch,
|
||||
mergeStrategy: args.strategy,
|
||||
mergeStrategyOption: args.strategyOption,
|
||||
originalPullRequest: pr,
|
||||
backportPullRequest: this.getDefaultBackportPullRequest(pr, args),
|
||||
backportPullRequests: this.generateBackportPullRequestsData(pr, args, targetBranches, bpBranchNames),
|
||||
git: {
|
||||
user: args.gitUser ?? this.gitClient.getDefaultGitUser(),
|
||||
email: args.gitEmail ?? this.gitClient.getDefaultGitEmail(),
|
||||
@@ -300,7 +374,7 @@ class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
* @param targetBranch target branch where the backport should be applied
|
||||
* @returns {GitPullRequest}
|
||||
*/
|
||||
getDefaultBackportPullRequest(originalPullRequest, args) {
|
||||
generateBackportPullRequestsData(originalPullRequest, args, targetBranches, bpBranchNames) {
|
||||
const reviewers = args.reviewers ?? [];
|
||||
if (reviewers.length == 0 && args.inheritReviewers) {
|
||||
// inherit only if args.reviewers is empty and args.inheritReviewers set to true
|
||||
@@ -315,29 +389,37 @@ class PullRequestConfigsParser extends configs_parser_1.default {
|
||||
if (args.inheritLabels) {
|
||||
labels.push(...originalPullRequest.labels);
|
||||
}
|
||||
let backportBranch = args.bpBranchName;
|
||||
if (backportBranch === undefined || backportBranch.trim() === "") {
|
||||
// for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects
|
||||
const concatenatedCommits = originalPullRequest.commits.map(c => c.slice(0, 7)).join("-");
|
||||
backportBranch = `bp-${args.targetBranch}-${concatenatedCommits}`;
|
||||
}
|
||||
if (backportBranch.length > 250) {
|
||||
this.logger.warn(`Backport branch (length=${backportBranch.length}) exceeded the max length of 250 chars, branch name truncated!`);
|
||||
backportBranch = backportBranch.slice(0, 250);
|
||||
}
|
||||
return {
|
||||
owner: originalPullRequest.targetRepo.owner,
|
||||
repo: originalPullRequest.targetRepo.project,
|
||||
head: backportBranch,
|
||||
base: args.targetBranch,
|
||||
title: args.title ?? `[${args.targetBranch}] ${originalPullRequest.title}`,
|
||||
// preserve new line chars
|
||||
body: body.replace(/\\n/g, "\n").replace(/\\r/g, "\r"),
|
||||
reviewers: [...new Set(reviewers)],
|
||||
assignees: [...new Set(args.assignees)],
|
||||
labels: [...new Set(labels)],
|
||||
comments: args.comments?.map(c => c.replace(/\\n/g, "\n").replace(/\\r/g, "\r")) ?? [],
|
||||
};
|
||||
return targetBranches.map((tb, idx) => {
|
||||
// if there multiple branch names take the corresponding one, otherwise get the the first one if it exists
|
||||
let backportBranch = bpBranchNames.length > 1 ? bpBranchNames[idx] : bpBranchNames[0];
|
||||
if (backportBranch === undefined || backportBranch.trim() === "") {
|
||||
// for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects
|
||||
const concatenatedCommits = originalPullRequest.commits.map(c => c.slice(0, 7)).join("-");
|
||||
backportBranch = `bp-${tb}-${concatenatedCommits}`;
|
||||
}
|
||||
else if (bpBranchNames.length == 1 && targetBranches.length > 1) {
|
||||
// multiple targets and single custom backport branch name we need to differentiate branch names
|
||||
// so append "-${tb}" to the provided name
|
||||
backportBranch = backportBranch + `-${tb}`;
|
||||
}
|
||||
if (backportBranch.length > 250) {
|
||||
this.logger.warn(`Backport branch (length=${backportBranch.length}) exceeded the max length of 250 chars, branch name truncated!`);
|
||||
backportBranch = backportBranch.slice(0, 250);
|
||||
}
|
||||
return {
|
||||
owner: originalPullRequest.targetRepo.owner,
|
||||
repo: originalPullRequest.targetRepo.project,
|
||||
head: backportBranch,
|
||||
base: tb,
|
||||
title: args.title ?? `[${tb}] ${originalPullRequest.title}`,
|
||||
// preserve new line chars
|
||||
body: body.replace(/\\n/g, "\n").replace(/\\r/g, "\r"),
|
||||
reviewers: [...new Set(reviewers)],
|
||||
assignees: [...new Set(args.assignees)],
|
||||
labels: [...new Set(labels)],
|
||||
comments: args.comments?.map(c => c.replace(/\\n/g, "\n").replace(/\\r/g, "\r")) ?? [],
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
exports["default"] = PullRequestConfigsParser;
|
||||
@@ -406,10 +488,12 @@ class GitCLIService {
|
||||
this.logger.info(`Cloning repository ${from} to ${to}`);
|
||||
if (!fs_1.default.existsSync(to)) {
|
||||
await (0, simple_git_1.default)().clone(this.remoteWithAuth(from), to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.logger.warn(`Folder ${to} already exist. Won't clone`);
|
||||
}
|
||||
this.logger.info(`Folder ${to} already exist. Won't clone`);
|
||||
// checkout to the proper branch
|
||||
this.logger.info(`Checking out branch ${branch}`);
|
||||
await this.git(to).checkout(branch);
|
||||
}
|
||||
/**
|
||||
* Create a new branch starting from the current one and checkout in it
|
||||
@@ -521,6 +605,9 @@ class GitClientFactory {
|
||||
case git_types_1.GitClientType.GITLAB:
|
||||
GitClientFactory.instance = new gitlab_client_1.default(authToken, apiUrl);
|
||||
break;
|
||||
case git_types_1.GitClientType.CODEBERG:
|
||||
GitClientFactory.instance = new github_client_1.default(authToken, apiUrl, true);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid git service type received: ${type}`);
|
||||
}
|
||||
@@ -562,6 +649,9 @@ const inferGitClient = (prUrl) => {
|
||||
else if (stdPrUrl.includes(git_types_1.GitClientType.GITLAB.toString())) {
|
||||
return git_types_1.GitClientType.GITLAB;
|
||||
}
|
||||
else if (stdPrUrl.includes(git_types_1.GitClientType.CODEBERG.toString())) {
|
||||
return git_types_1.GitClientType.CODEBERG;
|
||||
}
|
||||
throw new Error(`Remote git service not recognized from pr url: ${prUrl}`);
|
||||
};
|
||||
exports.inferGitClient = inferGitClient;
|
||||
@@ -595,6 +685,7 @@ var GitClientType;
|
||||
(function (GitClientType) {
|
||||
GitClientType["GITHUB"] = "github";
|
||||
GitClientType["GITLAB"] = "gitlab";
|
||||
GitClientType["CODEBERG"] = "codeberg";
|
||||
})(GitClientType = exports.GitClientType || (exports.GitClientType = {}));
|
||||
var GitRepoState;
|
||||
(function (GitRepoState) {
|
||||
@@ -616,19 +707,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const git_types_1 = __nccwpck_require__(750);
|
||||
const github_mapper_1 = __importDefault(__nccwpck_require__(5764));
|
||||
const octokit_factory_1 = __importDefault(__nccwpck_require__(4257));
|
||||
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
|
||||
class GitHubClient {
|
||||
constructor(token, apiUrl) {
|
||||
constructor(token, apiUrl, isForCodeberg = false) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.isForCodeberg = isForCodeberg;
|
||||
this.logger = logger_service_factory_1.default.getLogger();
|
||||
this.octokit = octokit_factory_1.default.getOctokit(token, this.apiUrl);
|
||||
this.mapper = new github_mapper_1.default();
|
||||
}
|
||||
getClientType() {
|
||||
return this.isForCodeberg ? git_types_1.GitClientType.CODEBERG : git_types_1.GitClientType.GITHUB;
|
||||
}
|
||||
// READ
|
||||
getDefaultGitUser() {
|
||||
return "GitHub";
|
||||
return this.apiUrl.includes(git_types_1.GitClientType.CODEBERG.toString()) ? "Codeberg" : "GitHub";
|
||||
}
|
||||
getDefaultGitEmail() {
|
||||
return "noreply@github.com";
|
||||
@@ -761,9 +857,9 @@ class GitHubMapper {
|
||||
state: this.mapGitState(pr.state),
|
||||
merged: pr.merged ?? false,
|
||||
mergedBy: pr.merged_by?.login,
|
||||
reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => r?.login)),
|
||||
assignees: pr.assignees.filter(r => "login" in r).map(r => r.login),
|
||||
labels: pr.labels.map(l => l.name),
|
||||
reviewers: pr.requested_reviewers?.filter(r => "login" in r).map((r => r?.login)) ?? [],
|
||||
assignees: pr.assignees?.filter(r => "login" in r).map(r => r.login) ?? [],
|
||||
labels: pr.labels?.map(l => l.name) ?? [],
|
||||
sourceRepo: await this.mapSourceRepo(pr),
|
||||
targetRepo: await this.mapTargetRepo(pr),
|
||||
nCommits: pr.commits,
|
||||
@@ -836,6 +932,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const git_types_1 = __nccwpck_require__(750);
|
||||
const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936));
|
||||
const gitlab_mapper_1 = __importDefault(__nccwpck_require__(2675));
|
||||
const axios_1 = __importDefault(__nccwpck_require__(8757));
|
||||
@@ -856,6 +953,9 @@ class GitLabClient {
|
||||
});
|
||||
this.mapper = new gitlab_mapper_1.default(this.client);
|
||||
}
|
||||
getClientType() {
|
||||
return git_types_1.GitClientType.GITLAB;
|
||||
}
|
||||
getDefaultGitUser() {
|
||||
return "Gitlab";
|
||||
}
|
||||
@@ -971,9 +1071,15 @@ class GitLabClient {
|
||||
* @returns {{owner: string, project: string}}
|
||||
*/
|
||||
extractMergeRequestData(mrUrl) {
|
||||
const elems = mrUrl.replace("/-/", "/").split("/");
|
||||
const { pathname } = new URL(mrUrl);
|
||||
const elems = pathname.substring(1).replace("/-/", "/").split("/");
|
||||
let namespace = "";
|
||||
for (let i = 0; i < elems.length - 3; i++) {
|
||||
namespace += elems[i] + "/";
|
||||
}
|
||||
namespace = namespace.substring(0, namespace.length - 1);
|
||||
return {
|
||||
namespace: elems[elems.length - 4],
|
||||
namespace: namespace,
|
||||
project: elems[elems.length - 3],
|
||||
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
|
||||
};
|
||||
@@ -1085,22 +1191,31 @@ class ConsoleLoggerService {
|
||||
this.logger = new logger_1.default();
|
||||
this.verbose = verbose;
|
||||
}
|
||||
setContext(newContext) {
|
||||
this.context = newContext;
|
||||
}
|
||||
clearContext() {
|
||||
this.context = undefined;
|
||||
}
|
||||
trace(message) {
|
||||
this.logger.log("TRACE", message);
|
||||
this.logger.log("TRACE", this.fromContext(message));
|
||||
}
|
||||
debug(message) {
|
||||
if (this.verbose) {
|
||||
this.logger.log("DEBUG", message);
|
||||
this.logger.log("DEBUG", this.fromContext(message));
|
||||
}
|
||||
}
|
||||
info(message) {
|
||||
this.logger.log("INFO", message);
|
||||
this.logger.log("INFO", this.fromContext(message));
|
||||
}
|
||||
warn(message) {
|
||||
this.logger.log("WARN", message);
|
||||
this.logger.log("WARN", this.fromContext(message));
|
||||
}
|
||||
error(message) {
|
||||
this.logger.log("ERROR", message);
|
||||
this.logger.log("ERROR", this.fromContext(message));
|
||||
}
|
||||
fromContext(msg) {
|
||||
return this.context ? `[${this.context}] ${msg}` : msg;
|
||||
}
|
||||
}
|
||||
exports["default"] = ConsoleLoggerService;
|
||||
@@ -1206,45 +1321,68 @@ class Runner {
|
||||
}
|
||||
// 2. init git service
|
||||
const gitClientType = (0, git_util_1.inferGitClient)(args.pullRequest);
|
||||
// right now the apiVersion is set to v4
|
||||
const apiUrl = (0, git_util_1.inferGitApiUrl)(args.pullRequest);
|
||||
// the api version is ignored in case of github
|
||||
const apiUrl = (0, git_util_1.inferGitApiUrl)(args.pullRequest, gitClientType === git_types_1.GitClientType.CODEBERG ? "v1" : undefined);
|
||||
const gitApi = git_client_factory_1.default.getOrCreate(gitClientType, args.auth, apiUrl);
|
||||
// 3. parse configs
|
||||
this.logger.debug("Parsing configs..");
|
||||
const configs = await new pr_configs_parser_1.default().parseAndValidate(args);
|
||||
const originalPR = configs.originalPullRequest;
|
||||
const backportPR = configs.backportPullRequest;
|
||||
const backportPRs = configs.backportPullRequests;
|
||||
// start local git operations
|
||||
const git = new git_cli_1.default(configs.auth, configs.git);
|
||||
const failures = [];
|
||||
// we need sequential backporting as they will operate on the same folder
|
||||
// avoid cloning the same repo multiple times
|
||||
for (const pr of backportPRs) {
|
||||
try {
|
||||
await this.executeBackport(configs, pr, {
|
||||
gitClientType: gitClientType,
|
||||
gitClientApi: gitApi,
|
||||
gitCli: git,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
|
||||
failures.push(error);
|
||||
}
|
||||
}
|
||||
if (failures.length > 0) {
|
||||
throw new Error(`Failure occurred during one of the backports: [${failures.join(" ; ")}]`);
|
||||
}
|
||||
}
|
||||
async executeBackport(configs, backportPR, git) {
|
||||
this.logger.setContext(backportPR.base);
|
||||
const originalPR = configs.originalPullRequest;
|
||||
// 4. clone the repository
|
||||
this.logger.debug("Cloning repo..");
|
||||
await git.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, configs.targetBranch);
|
||||
await git.gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
|
||||
// 5. create new branch from target one and checkout
|
||||
this.logger.debug("Creating local branch..");
|
||||
await git.createLocalBranch(configs.folder, backportPR.head);
|
||||
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
|
||||
// 6. fetch pull request remote if source owner != target owner or pull request still open
|
||||
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
|
||||
configs.originalPullRequest.state === "open") {
|
||||
this.logger.debug("Fetching pull request remote..");
|
||||
const prefix = gitClientType === git_types_1.GitClientType.GITHUB ? "pull" : "merge-requests"; // default is for gitlab
|
||||
await git.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||
const prefix = git.gitClientType === git_types_1.GitClientType.GITLAB ? "merge-requests" : "pull"; // default is for gitlab
|
||||
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||
}
|
||||
// 7. apply all changes to the new branch
|
||||
this.logger.debug("Cherry picking commits..");
|
||||
for (const sha of originalPR.commits) {
|
||||
await git.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption);
|
||||
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption);
|
||||
}
|
||||
if (!configs.dryRun) {
|
||||
// 8. push the new branch to origin
|
||||
await git.push(configs.folder, backportPR.head);
|
||||
await git.gitCli.push(configs.folder, backportPR.head);
|
||||
// 9. create pull request new branch -> target branch (using octokit)
|
||||
const prUrl = await gitApi.createPullRequest(backportPR);
|
||||
const prUrl = await git.gitClientApi.createPullRequest(backportPR);
|
||||
this.logger.info(`Pull request created: ${prUrl}`);
|
||||
}
|
||||
else {
|
||||
this.logger.warn("Pull request creation and remote push skipped");
|
||||
this.logger.info(`${JSON.stringify(backportPR, null, 2)}`);
|
||||
}
|
||||
this.logger.clearContext();
|
||||
}
|
||||
}
|
||||
exports["default"] = Runner;
|
||||
@@ -18611,7 +18749,7 @@ module.exports = require("zlib");
|
||||
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
// Axios v1.4.0 Copyright (c) 2023 Matt Zabriskie and contributors
|
||||
// Axios v1.6.0 Copyright (c) 2023 Matt Zabriskie and contributors
|
||||
|
||||
|
||||
const FormData$1 = __nccwpck_require__(4334);
|
||||
@@ -19181,8 +19319,9 @@ const reduceDescriptors = (obj, reducer) => {
|
||||
const reducedDescriptors = {};
|
||||
|
||||
forEach(descriptors, (descriptor, name) => {
|
||||
if (reducer(descriptor, name, obj) !== false) {
|
||||
reducedDescriptors[name] = descriptor;
|
||||
let ret;
|
||||
if ((ret = reducer(descriptor, name, obj)) !== false) {
|
||||
reducedDescriptors[name] = ret || descriptor;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -19966,10 +20105,6 @@ function formDataToJSON(formData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const DEFAULT_CONTENT_TYPE = {
|
||||
'Content-Type': undefined
|
||||
};
|
||||
|
||||
/**
|
||||
* It takes a string, tries to parse it, and if it fails, it returns the stringified version
|
||||
* of the input
|
||||
@@ -20108,19 +20243,16 @@ const defaults = {
|
||||
|
||||
headers: {
|
||||
common: {
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': undefined
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
|
||||
utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch'], (method) => {
|
||||
defaults.headers[method] = {};
|
||||
});
|
||||
|
||||
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
|
||||
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
|
||||
});
|
||||
|
||||
const defaults$1 = defaults;
|
||||
|
||||
// RawAxiosHeaders whose duplicates are ignored by node
|
||||
@@ -20454,7 +20586,17 @@ class AxiosHeaders {
|
||||
|
||||
AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']);
|
||||
|
||||
utils.freezeMethods(AxiosHeaders.prototype);
|
||||
// reserved names hotfix
|
||||
utils.reduceDescriptors(AxiosHeaders.prototype, ({value}, key) => {
|
||||
let mapped = key[0].toUpperCase() + key.slice(1); // map `set` => `Set`
|
||||
return {
|
||||
get: () => value,
|
||||
set(headerValue) {
|
||||
this[mapped] = headerValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
utils.freezeMethods(AxiosHeaders);
|
||||
|
||||
const AxiosHeaders$1 = AxiosHeaders;
|
||||
@@ -20574,7 +20716,7 @@ function buildFullPath(baseURL, requestedURL) {
|
||||
return requestedURL;
|
||||
}
|
||||
|
||||
const VERSION = "1.4.0";
|
||||
const VERSION = "1.6.0";
|
||||
|
||||
function parseProtocol(url) {
|
||||
const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url);
|
||||
@@ -21178,6 +21320,18 @@ const wrapAsync = (asyncExecutor) => {
|
||||
})
|
||||
};
|
||||
|
||||
const resolveFamily = ({address, family}) => {
|
||||
if (!utils.isString(address)) {
|
||||
throw TypeError('address must be a string');
|
||||
}
|
||||
return ({
|
||||
address,
|
||||
family: family || (address.indexOf('.') < 0 ? 6 : 4)
|
||||
});
|
||||
};
|
||||
|
||||
const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : {address, family});
|
||||
|
||||
/*eslint consistent-return:0*/
|
||||
const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
|
||||
@@ -21188,15 +21342,16 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
let rejected = false;
|
||||
let req;
|
||||
|
||||
if (lookup && utils.isAsyncFn(lookup)) {
|
||||
lookup = callbackify$1(lookup, (entry) => {
|
||||
if(utils.isString(entry)) {
|
||||
entry = [entry, entry.indexOf('.') < 0 ? 6 : 4];
|
||||
} else if (!utils.isArray(entry)) {
|
||||
throw new TypeError('lookup async function must return an array [ip: string, family: number]]')
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
if (lookup) {
|
||||
const _lookup = callbackify$1(lookup, (value) => utils.isArray(value) ? value : [value]);
|
||||
// hotfix to support opt.all option which is required for node 20.x
|
||||
lookup = (hostname, opt, cb) => {
|
||||
_lookup(hostname, opt, (err, arg0, arg1) => {
|
||||
const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)];
|
||||
|
||||
opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// temporary internal emitter until the AxiosRequest class will be implemented
|
||||
@@ -21423,11 +21578,13 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
auth,
|
||||
protocol,
|
||||
family,
|
||||
lookup,
|
||||
beforeRedirect: dispatchBeforeRedirect,
|
||||
beforeRedirects: {}
|
||||
};
|
||||
|
||||
// cacheable-lookup integration hotfix
|
||||
!utils.isUndefined(lookup) && (options.lookup = lookup);
|
||||
|
||||
if (config.socketPath) {
|
||||
options.socketPath = config.socketPath;
|
||||
} else {
|
||||
@@ -21501,7 +21658,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
delete res.headers['content-encoding'];
|
||||
}
|
||||
|
||||
switch (res.headers['content-encoding']) {
|
||||
switch ((res.headers['content-encoding'] || '').toLowerCase()) {
|
||||
/*eslint default-case:0*/
|
||||
case 'gzip':
|
||||
case 'x-gzip':
|
||||
@@ -21597,7 +21754,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
}
|
||||
response.data = responseData;
|
||||
} catch (err) {
|
||||
reject(AxiosError.from(err, null, config, response.request, response));
|
||||
return reject(AxiosError.from(err, null, config, response.request, response));
|
||||
}
|
||||
settle(resolve, reject, response);
|
||||
});
|
||||
@@ -21634,7 +21791,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
||||
// This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.
|
||||
const timeout = parseInt(config.timeout, 10);
|
||||
|
||||
if (isNaN(timeout)) {
|
||||
if (Number.isNaN(timeout)) {
|
||||
reject(new AxiosError(
|
||||
'error trying to parse `config.timeout` to int',
|
||||
AxiosError.ERR_BAD_OPTION_VALUE,
|
||||
@@ -21853,11 +22010,16 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
|
||||
}
|
||||
}
|
||||
|
||||
let contentType;
|
||||
|
||||
if (utils.isFormData(requestData)) {
|
||||
if (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv) {
|
||||
requestHeaders.setContentType(false); // Let the browser set it
|
||||
} else {
|
||||
requestHeaders.setContentType('multipart/form-data;', false); // mobile/desktop app frameworks
|
||||
} else if(!requestHeaders.getContentType(/^\s*multipart\/form-data/)){
|
||||
requestHeaders.setContentType('multipart/form-data'); // mobile/desktop app frameworks
|
||||
} else if(utils.isString(contentType = requestHeaders.getContentType())){
|
||||
// fix semicolon duplication issue for ReactNative FormData implementation
|
||||
requestHeaders.setContentType(contentType.replace(/^\s*(multipart\/form-data);+/, '$1'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21975,8 +22137,8 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
|
||||
// Specifically not if we're in a web worker, or react-native.
|
||||
if (platform.isStandardBrowserEnv) {
|
||||
// Add xsrf header
|
||||
const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath))
|
||||
&& config.xsrfCookieName && cookies.read(config.xsrfCookieName);
|
||||
// regarding CVE-2023-45857 config.withCredentials condition was removed temporarily
|
||||
const xsrfValue = isURLSameOrigin(fullPath) && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
|
||||
|
||||
if (xsrfValue) {
|
||||
requestHeaders.set(config.xsrfHeaderName, xsrfValue);
|
||||
@@ -22050,7 +22212,7 @@ const knownAdapters = {
|
||||
};
|
||||
|
||||
utils.forEach(knownAdapters, (fn, value) => {
|
||||
if(fn) {
|
||||
if (fn) {
|
||||
try {
|
||||
Object.defineProperty(fn, 'name', {value});
|
||||
} catch (e) {
|
||||
@@ -22060,6 +22222,10 @@ utils.forEach(knownAdapters, (fn, value) => {
|
||||
}
|
||||
});
|
||||
|
||||
const renderReason = (reason) => `- ${reason}`;
|
||||
|
||||
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
|
||||
|
||||
const adapters = {
|
||||
getAdapter: (adapters) => {
|
||||
adapters = utils.isArray(adapters) ? adapters : [adapters];
|
||||
@@ -22068,32 +22234,46 @@ const adapters = {
|
||||
let nameOrAdapter;
|
||||
let adapter;
|
||||
|
||||
const rejectedReasons = {};
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
nameOrAdapter = adapters[i];
|
||||
if((adapter = utils.isString(nameOrAdapter) ? knownAdapters[nameOrAdapter.toLowerCase()] : nameOrAdapter)) {
|
||||
let id;
|
||||
|
||||
adapter = nameOrAdapter;
|
||||
|
||||
if (!isResolvedHandle(nameOrAdapter)) {
|
||||
adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
|
||||
|
||||
if (adapter === undefined) {
|
||||
throw new AxiosError(`Unknown adapter '${id}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter) {
|
||||
break;
|
||||
}
|
||||
|
||||
rejectedReasons[id || '#' + i] = adapter;
|
||||
}
|
||||
|
||||
if (!adapter) {
|
||||
if (adapter === false) {
|
||||
throw new AxiosError(
|
||||
`Adapter ${nameOrAdapter} is not supported by the environment`,
|
||||
'ERR_NOT_SUPPORT'
|
||||
|
||||
const reasons = Object.entries(rejectedReasons)
|
||||
.map(([id, state]) => `adapter ${id} ` +
|
||||
(state === false ? 'is not supported by the environment' : 'is not available in the build')
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
utils.hasOwnProp(knownAdapters, nameOrAdapter) ?
|
||||
`Adapter '${nameOrAdapter}' is not available in the build` :
|
||||
`Unknown adapter '${nameOrAdapter}'`
|
||||
let s = length ?
|
||||
(reasons.length > 1 ? 'since :\n' + reasons.map(renderReason).join('\n') : ' ' + renderReason(reasons[0])) :
|
||||
'as no adapter specified';
|
||||
|
||||
throw new AxiosError(
|
||||
`There is no suitable adapter to dispatch the request ` + s,
|
||||
'ERR_NOT_SUPPORT'
|
||||
);
|
||||
}
|
||||
|
||||
if (!utils.isFunction(adapter)) {
|
||||
throw new TypeError('adapter is not a function');
|
||||
}
|
||||
|
||||
return adapter;
|
||||
},
|
||||
adapters: knownAdapters
|
||||
@@ -22424,15 +22604,13 @@ class Axios {
|
||||
// Set config.method
|
||||
config.method = (config.method || this.defaults.method || 'get').toLowerCase();
|
||||
|
||||
let contextHeaders;
|
||||
|
||||
// Flatten headers
|
||||
contextHeaders = headers && utils.merge(
|
||||
let contextHeaders = headers && utils.merge(
|
||||
headers.common,
|
||||
headers[config.method]
|
||||
);
|
||||
|
||||
contextHeaders && utils.forEach(
|
||||
headers && utils.forEach(
|
||||
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
|
||||
(method) => {
|
||||
delete headers[method];
|
||||
@@ -22842,6 +23020,8 @@ axios.AxiosHeaders = AxiosHeaders$1;
|
||||
|
||||
axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
|
||||
|
||||
axios.getAdapter = adapters.getAdapter;
|
||||
|
||||
axios.HttpStatusCode = HttpStatusCode$1;
|
||||
|
||||
axios.default = axios;
|
||||
|
||||
347
package-lock.json
generated
347
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@kie/git-backporting",
|
||||
"version": "4.3.0",
|
||||
"version": "4.5.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@kie/git-backporting",
|
||||
"version": "4.3.0",
|
||||
"version": "4.5.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
@@ -86,17 +86,89 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
|
||||
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.22.5"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz",
|
||||
@@ -152,12 +224,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
|
||||
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@@ -210,22 +282,22 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -296,9 +368,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz",
|
||||
"integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -317,9 +389,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
|
||||
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -349,13 +421,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
|
||||
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -434,9 +506,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
|
||||
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -623,33 +695,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
|
||||
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
|
||||
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/generator": "^7.22.5",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -667,13 +739,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
|
||||
"integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2673,9 +2745,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -11747,12 +11819,71 @@
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
|
||||
"integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.22.5"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
@@ -11799,12 +11930,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
|
||||
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@@ -11847,19 +11978,19 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-hoist-variables": {
|
||||
@@ -11912,9 +12043,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz",
|
||||
"integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -11927,9 +12058,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
|
||||
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
@@ -11950,13 +12081,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
|
||||
"integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -12019,9 +12150,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
|
||||
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-syntax-async-generators": {
|
||||
@@ -12151,30 +12282,30 @@
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
|
||||
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
|
||||
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/generator": "^7.22.5",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -12188,13 +12319,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
|
||||
"integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
@@ -13796,9 +13927,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kie/git-backporting",
|
||||
"version": "4.3.0",
|
||||
"version": "4.5.0",
|
||||
"description": "Git backporting is a tool to execute automatic pull request git backporting.",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -17,8 +17,8 @@ export default abstract class ArgsParser {
|
||||
const args = this.readArgs();
|
||||
|
||||
// validate and fill with defaults
|
||||
if (!args.pullRequest || !args.targetBranch) {
|
||||
throw new Error("Missing option: pull request and target branch must be provided");
|
||||
if (!args.pullRequest || !args.targetBranch || args.targetBranch.trim().length == 0) {
|
||||
throw new Error("Missing option: pull request and target branches must be provided");
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Input arguments
|
||||
* Tool's input arguments interface
|
||||
*/
|
||||
export interface Args {
|
||||
targetBranch: string, // branch on the target repo where the change should be backported to
|
||||
// NOTE: keep targetBranch as singular and of type string for backward compatibilities
|
||||
targetBranch: string, // comma separated list of branches on the target repo where the change should be backported to
|
||||
pullRequest: string, // url of the pull request to backport
|
||||
dryRun?: boolean, // if enabled do not push anything remotely
|
||||
auth?: string, // git service auth, like github token
|
||||
@@ -12,7 +13,8 @@ export interface Args {
|
||||
title?: string, // backport pr title, default original pr title prefixed by target branch
|
||||
body?: string, // backport pr title, default original pr body prefixed by bodyPrefix
|
||||
bodyPrefix?: string, // backport pr body prefix, default `backport <original-pr-link>`
|
||||
bpBranchName?: string, // backport pr branch name, default computed from commit
|
||||
// NOTE: keep bpBranchName as singular and of type string for backward compatibilities
|
||||
bpBranchName?: string, // comma separated list of backport pr branch names, default computed from commit and target branches
|
||||
reviewers?: string[], // backport pr reviewers
|
||||
assignees?: string[], // backport pr assignees
|
||||
inheritReviewers?: boolean, // if true and reviewers == [] then inherit reviewers from original pr
|
||||
|
||||
@@ -10,17 +10,17 @@ export default class CLIArgsParser extends ArgsParser {
|
||||
return new Command(name)
|
||||
.version(version)
|
||||
.description(description)
|
||||
.option("-tb, --target-branch <branch>", "branch where changes must be backported to")
|
||||
.option("-tb, --target-branch <branches>", "comma separated list of branches where changes must be backported to")
|
||||
.option("-pr, --pull-request <pr-url>", "pull request url, e.g., https://github.com/kiegroup/git-backporting/pull/1")
|
||||
.option("-d, --dry-run", "if enabled the tool does not create any pull request nor push anything remotely")
|
||||
.option("-a, --auth <auth>", "git service authentication string, e.g., github token")
|
||||
.option("-a, --auth <auth>", "git authentication string, if not provided fallback by looking for existing env variables like GITHUB_TOKEN")
|
||||
.option("-gu, --git-user <git-user>", "local git user name, default is 'GitHub'")
|
||||
.option("-ge, --git-email <git-email>", "local git user email, default is 'noreply@github.com'")
|
||||
.option("-f, --folder <folder>", "local folder where the repo will be checked out, e.g., /tmp/folder")
|
||||
.option("--title <bp-title>", "backport pr title, default original pr title prefixed by target branch")
|
||||
.option("--body <bp-body>", "backport pr title, default original pr body prefixed by bodyPrefix")
|
||||
.option("--body-prefix <bp-body-prefix>", "backport pr body prefix, default `backport <original-pr-link>`")
|
||||
.option("--bp-branch-name <bp-branch-name>", "backport pr branch name, default auto-generated by the commit")
|
||||
.option("--bp-branch-name <bp-branch-names>", "comma separated list of backport pr branch names, default auto-generated by the commit and target branch")
|
||||
.option("--reviewers <reviewers>", "comma separated list of reviewers for the backporting pull request", getAsCleanedCommaSeparatedList)
|
||||
.option("--assignees <assignees>", "comma separated list of assignees for the backporting pull request", getAsCleanedCommaSeparatedList)
|
||||
.option("--no-inherit-reviewers", "if provided and reviewers option is empty then inherit them from original pull request")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Args } from "@bp/service/args/args.types";
|
||||
import { Configs } from "@bp/service/configs/configs.types";
|
||||
import { AuthTokenId, Configs } from "@bp/service/configs/configs.types";
|
||||
import LoggerService from "../logger/logger-service";
|
||||
import LoggerServiceFactory from "../logger/logger-service-factory";
|
||||
import { GitClientType } from "../git/git.types";
|
||||
|
||||
/**
|
||||
* Abstract configuration parser class in charge to parse
|
||||
@@ -34,4 +35,42 @@ import LoggerServiceFactory from "../logger/logger-service-factory";
|
||||
|
||||
return Promise.resolve(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param gitType
|
||||
* @returns tuple where
|
||||
* - the first element is the corresponding env value
|
||||
* - the second element is true if the value is not undefined nor empty
|
||||
*/
|
||||
public getGitTokenFromEnv(gitType: GitClientType): string | undefined {
|
||||
let [token] = this.getEnv(AuthTokenId.GIT_TOKEN);
|
||||
let [specToken, specOk]: [string | undefined, boolean] = [undefined, false];
|
||||
if (GitClientType.GITHUB == gitType) {
|
||||
[specToken, specOk] = this.getEnv(AuthTokenId.GITHUB_TOKEN);
|
||||
} else if (GitClientType.GITLAB == gitType) {
|
||||
[specToken, specOk] = this.getEnv(AuthTokenId.GITLAB_TOKEN);
|
||||
} else if (GitClientType.CODEBERG == gitType) {
|
||||
[specToken, specOk] = this.getEnv(AuthTokenId.CODEBERG_TOKEN);
|
||||
}
|
||||
|
||||
if (specOk) {
|
||||
token = specToken;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get process env variable given the input key string
|
||||
* @param key
|
||||
* @returns tuple where
|
||||
* - the first element is the corresponding env value
|
||||
* - the second element is true if the value is not undefined nor empty
|
||||
*/
|
||||
public getEnv(key: string): [string | undefined, boolean] {
|
||||
const val = process.env[key];
|
||||
return [val, val !== undefined && val !== ""];
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,19 @@ export interface Configs {
|
||||
auth?: string,
|
||||
git: LocalGit,
|
||||
folder: string,
|
||||
targetBranch: string,
|
||||
mergeStrategy?: string, // cherry-pick merge strategy
|
||||
mergeStrategyOption?: string, // cherry-pick merge strategy option
|
||||
originalPullRequest: GitPullRequest,
|
||||
backportPullRequest: BackportPullRequest,
|
||||
backportPullRequests: BackportPullRequest[],
|
||||
}
|
||||
|
||||
export enum AuthTokenId {
|
||||
// github specific token
|
||||
GITHUB_TOKEN = "GITHUB_TOKEN",
|
||||
// gitlab specific token
|
||||
GITLAB_TOKEN = "GITLAB_TOKEN",
|
||||
// codeberg specific token
|
||||
CODEBERG_TOKEN = "CODEBERG_TOKEN",
|
||||
// generic git token
|
||||
GIT_TOKEN = "GIT_TOKEN",
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getAsCleanedCommaSeparatedList, getAsCommaSeparatedList } from "@bp/service/args/args-utils";
|
||||
import { Args } from "@bp/service/args/args.types";
|
||||
import ConfigsParser from "@bp/service/configs/configs-parser";
|
||||
import { Configs } from "@bp/service/configs/configs.types";
|
||||
@@ -25,15 +26,31 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
||||
|
||||
const folder: string = args.folder ?? this.getDefaultFolder();
|
||||
|
||||
const targetBranches: string[] = [...new Set(getAsCommaSeparatedList(args.targetBranch)!)];
|
||||
const bpBranchNames: string[] = [...new Set(args.bpBranchName ? (getAsCleanedCommaSeparatedList(args.bpBranchName) ?? []) : [])];
|
||||
|
||||
if (bpBranchNames.length > 1 && bpBranchNames.length != targetBranches.length) {
|
||||
throw new Error(`The number of backport branch names, if provided, must match the number of target branches or just one, provided ${bpBranchNames.length} branch names instead`);
|
||||
}
|
||||
|
||||
// setup the auth token
|
||||
let token = args.auth;
|
||||
if (token === undefined) {
|
||||
this.logger.info("Auth argument not provided, checking available tokens from env..");
|
||||
token = this.getGitTokenFromEnv(this.gitClient.getClientType());
|
||||
if (!token) {
|
||||
this.logger.info("Git token not set in the env");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
dryRun: args.dryRun!,
|
||||
auth: args.auth,
|
||||
auth: token,
|
||||
folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`,
|
||||
targetBranch: args.targetBranch,
|
||||
mergeStrategy: args.strategy,
|
||||
mergeStrategyOption: args.strategyOption,
|
||||
originalPullRequest: pr,
|
||||
backportPullRequest: this.getDefaultBackportPullRequest(pr, args),
|
||||
backportPullRequests: this.generateBackportPullRequestsData(pr, args, targetBranches, bpBranchNames),
|
||||
git: {
|
||||
user: args.gitUser ?? this.gitClient.getDefaultGitUser(),
|
||||
email: args.gitEmail ?? this.gitClient.getDefaultGitEmail(),
|
||||
@@ -52,7 +69,13 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
||||
* @param targetBranch target branch where the backport should be applied
|
||||
* @returns {GitPullRequest}
|
||||
*/
|
||||
private getDefaultBackportPullRequest(originalPullRequest: GitPullRequest, args: Args): BackportPullRequest {
|
||||
private generateBackportPullRequestsData(
|
||||
originalPullRequest: GitPullRequest,
|
||||
args: Args,
|
||||
targetBranches: string[],
|
||||
bpBranchNames: string[]
|
||||
): BackportPullRequest[] {
|
||||
|
||||
const reviewers = args.reviewers ?? [];
|
||||
if (reviewers.length == 0 && args.inheritReviewers) {
|
||||
// inherit only if args.reviewers is empty and args.inheritReviewers set to true
|
||||
@@ -70,30 +93,38 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
||||
labels.push(...originalPullRequest.labels);
|
||||
}
|
||||
|
||||
let backportBranch = args.bpBranchName;
|
||||
if (backportBranch === undefined || backportBranch.trim() === "") {
|
||||
// for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects
|
||||
const concatenatedCommits: string = originalPullRequest.commits!.map(c => c.slice(0, 7)).join("-");
|
||||
backportBranch = `bp-${args.targetBranch}-${concatenatedCommits}`;
|
||||
}
|
||||
return targetBranches.map((tb, idx) => {
|
||||
|
||||
if (backportBranch.length > 250) {
|
||||
this.logger.warn(`Backport branch (length=${backportBranch.length}) exceeded the max length of 250 chars, branch name truncated!`);
|
||||
backportBranch = backportBranch.slice(0, 250);
|
||||
}
|
||||
// if there multiple branch names take the corresponding one, otherwise get the the first one if it exists
|
||||
let backportBranch = bpBranchNames.length > 1 ? bpBranchNames[idx] : bpBranchNames[0];
|
||||
if (backportBranch === undefined || backportBranch.trim() === "") {
|
||||
// for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects
|
||||
const concatenatedCommits: string = originalPullRequest.commits!.map(c => c.slice(0, 7)).join("-");
|
||||
backportBranch = `bp-${tb}-${concatenatedCommits}`;
|
||||
} else if (bpBranchNames.length == 1 && targetBranches.length > 1) {
|
||||
// multiple targets and single custom backport branch name we need to differentiate branch names
|
||||
// so append "-${tb}" to the provided name
|
||||
backportBranch = backportBranch + `-${tb}`;
|
||||
}
|
||||
|
||||
if (backportBranch.length > 250) {
|
||||
this.logger.warn(`Backport branch (length=${backportBranch.length}) exceeded the max length of 250 chars, branch name truncated!`);
|
||||
backportBranch = backportBranch.slice(0, 250);
|
||||
}
|
||||
|
||||
return {
|
||||
owner: originalPullRequest.targetRepo.owner,
|
||||
repo: originalPullRequest.targetRepo.project,
|
||||
head: backportBranch,
|
||||
base: args.targetBranch,
|
||||
title: args.title ?? `[${args.targetBranch}] ${originalPullRequest.title}`,
|
||||
// preserve new line chars
|
||||
body: body.replace(/\\n/g, "\n").replace(/\\r/g, "\r"),
|
||||
reviewers: [...new Set(reviewers)],
|
||||
assignees: [...new Set(args.assignees)],
|
||||
labels: [...new Set(labels)],
|
||||
comments: args.comments?.map(c => c.replace(/\\n/g, "\n").replace(/\\r/g, "\r")) ?? [],
|
||||
};
|
||||
return {
|
||||
owner: originalPullRequest.targetRepo.owner,
|
||||
repo: originalPullRequest.targetRepo.project,
|
||||
head: backportBranch,
|
||||
base: tb,
|
||||
title: args.title ?? `[${tb}] ${originalPullRequest.title}`,
|
||||
// preserve new line chars
|
||||
body: body.replace(/\\n/g, "\n").replace(/\\r/g, "\r"),
|
||||
reviewers: [...new Set(reviewers)],
|
||||
assignees: [...new Set(args.assignees)],
|
||||
labels: [...new Set(labels)],
|
||||
comments: args.comments?.map(c => c.replace(/\\n/g, "\n").replace(/\\r/g, "\r")) ?? [],
|
||||
};
|
||||
}) as BackportPullRequest[];
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,13 @@ export default class GitCLIService {
|
||||
this.logger.info(`Cloning repository ${from} to ${to}`);
|
||||
if (!fs.existsSync(to)) {
|
||||
await simpleGit().clone(this.remoteWithAuth(from), to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]);
|
||||
} else {
|
||||
this.logger.warn(`Folder ${to} already exist. Won't clone`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.info(`Folder ${to} already exist. Won't clone`);
|
||||
// checkout to the proper branch
|
||||
this.logger.info(`Checking out branch ${branch}`);
|
||||
await this.git(to).checkout(branch);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,9 @@ export default class GitClientFactory {
|
||||
case GitClientType.GITLAB:
|
||||
GitClientFactory.instance = new GitLabClient(authToken, apiUrl);
|
||||
break;
|
||||
case GitClientType.CODEBERG:
|
||||
GitClientFactory.instance = new GitHubService(authToken, apiUrl, true);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid git service type received: ${type}`);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
||||
import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/git/git.types";
|
||||
|
||||
/**
|
||||
* Git management service interface, which provides a common API for interacting
|
||||
@@ -6,6 +6,11 @@ import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
||||
*/
|
||||
export default interface GitClient {
|
||||
|
||||
/**
|
||||
* @returns {GitClientType} specific git client enum type
|
||||
*/
|
||||
getClientType(): GitClientType
|
||||
|
||||
// READ
|
||||
|
||||
getDefaultGitUser(): string;
|
||||
|
||||
@@ -16,6 +16,8 @@ export const inferGitClient = (prUrl: string): GitClientType => {
|
||||
return GitClientType.GITHUB;
|
||||
} else if (stdPrUrl.includes(GitClientType.GITLAB.toString())) {
|
||||
return GitClientType.GITLAB;
|
||||
} else if (stdPrUrl.includes(GitClientType.CODEBERG.toString())) {
|
||||
return GitClientType.CODEBERG;
|
||||
}
|
||||
|
||||
throw new Error(`Remote git service not recognized from pr url: ${prUrl}`);
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface BackportPullRequest {
|
||||
export enum GitClientType {
|
||||
GITHUB = "github",
|
||||
GITLAB = "gitlab",
|
||||
CODEBERG = "codeberg",
|
||||
}
|
||||
|
||||
export enum GitRepoState {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import GitClient from "@bp/service/git/git-client";
|
||||
import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
||||
import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/git/git.types";
|
||||
import GitHubMapper from "@bp/service/git/github/github-mapper";
|
||||
import OctokitFactory from "@bp/service/git/github/octokit-factory";
|
||||
import LoggerService from "@bp/service/logger/logger-service";
|
||||
@@ -11,20 +11,26 @@ export default class GitHubClient implements GitClient {
|
||||
|
||||
private logger: LoggerService;
|
||||
private apiUrl: string;
|
||||
private isForCodeberg: boolean;
|
||||
private octokit: Octokit;
|
||||
private mapper: GitHubMapper;
|
||||
|
||||
constructor(token: string | undefined, apiUrl: string) {
|
||||
constructor(token: string | undefined, apiUrl: string, isForCodeberg = false) {
|
||||
this.apiUrl = apiUrl;
|
||||
this.isForCodeberg = isForCodeberg;
|
||||
this.logger = LoggerServiceFactory.getLogger();
|
||||
this.octokit = OctokitFactory.getOctokit(token, this.apiUrl);
|
||||
this.mapper = new GitHubMapper();
|
||||
}
|
||||
|
||||
getClientType(): GitClientType {
|
||||
return this.isForCodeberg ? GitClientType.CODEBERG : GitClientType.GITHUB;
|
||||
}
|
||||
|
||||
// READ
|
||||
|
||||
getDefaultGitUser(): string {
|
||||
return "GitHub";
|
||||
return this.apiUrl.includes(GitClientType.CODEBERG.toString()) ? "Codeberg" : "GitHub";
|
||||
}
|
||||
|
||||
getDefaultGitEmail(): string {
|
||||
|
||||
@@ -24,9 +24,9 @@ export default class GitHubMapper implements GitResponseMapper<PullRequest, "ope
|
||||
state: this.mapGitState(pr.state), // TODO fix using custom mapper
|
||||
merged: pr.merged ?? false,
|
||||
mergedBy: pr.merged_by?.login,
|
||||
reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => (r as User)?.login)),
|
||||
assignees: pr.assignees.filter(r => "login" in r).map(r => r.login),
|
||||
labels: pr.labels.map(l => l.name),
|
||||
reviewers: pr.requested_reviewers?.filter(r => "login" in r).map((r => (r as User)?.login)) ?? [],
|
||||
assignees: pr.assignees?.filter(r => "login" in r).map(r => r.login) ?? [],
|
||||
labels: pr.labels?.map(l => l.name) ?? [],
|
||||
sourceRepo: await this.mapSourceRepo(pr),
|
||||
targetRepo: await this.mapTargetRepo(pr),
|
||||
nCommits: pr.commits,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { GitPullRequest, BackportPullRequest, GitClientType } from "@bp/service/git/git.types";
|
||||
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
||||
import { CommitSchema, MergeRequestSchema, UserSchema } from "@gitbeaker/rest";
|
||||
import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper";
|
||||
@@ -30,6 +30,10 @@ export default class GitLabClient implements GitClient {
|
||||
this.mapper = new GitLabMapper(this.client);
|
||||
}
|
||||
|
||||
getClientType(): GitClientType {
|
||||
return GitClientType.GITLAB;
|
||||
}
|
||||
|
||||
getDefaultGitUser(): string {
|
||||
return "Gitlab";
|
||||
}
|
||||
@@ -181,9 +185,18 @@ export default class GitLabClient implements GitClient {
|
||||
* @returns {{owner: string, project: string}}
|
||||
*/
|
||||
private extractMergeRequestData(mrUrl: string): {namespace: string, project: string, id: number} {
|
||||
const elems: string[] = mrUrl.replace("/-/", "/").split("/");
|
||||
const { pathname } = new URL(mrUrl);
|
||||
const elems: string[] = pathname.substring(1).replace("/-/", "/").split("/");
|
||||
let namespace = "";
|
||||
|
||||
for (let i = 0; i < elems.length - 3; i++) {
|
||||
namespace += elems[i] + "/";
|
||||
}
|
||||
|
||||
namespace = namespace.substring(0, namespace.length - 1);
|
||||
|
||||
return {
|
||||
namespace: elems[elems.length - 4],
|
||||
namespace: namespace,
|
||||
project: elems[elems.length - 3],
|
||||
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
|
||||
};
|
||||
|
||||
@@ -5,32 +5,44 @@ export default class ConsoleLoggerService implements LoggerService {
|
||||
|
||||
private readonly logger: Logger;
|
||||
private readonly verbose: boolean;
|
||||
private context?: string;
|
||||
|
||||
constructor(verbose = true) {
|
||||
this.logger = new Logger();
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
setContext(newContext: string) {
|
||||
this.context = newContext;
|
||||
}
|
||||
|
||||
clearContext() {
|
||||
this.context = undefined;
|
||||
}
|
||||
|
||||
trace(message: string): void {
|
||||
this.logger.log("TRACE", message);
|
||||
this.logger.log("TRACE", this.fromContext(message));
|
||||
}
|
||||
|
||||
debug(message: string): void {
|
||||
if (this.verbose) {
|
||||
this.logger.log("DEBUG", message);
|
||||
this.logger.log("DEBUG", this.fromContext(message));
|
||||
}
|
||||
}
|
||||
|
||||
info(message: string): void {
|
||||
this.logger.log("INFO", message);
|
||||
this.logger.log("INFO", this.fromContext(message));
|
||||
}
|
||||
|
||||
warn(message: string): void {
|
||||
this.logger.log("WARN", message);
|
||||
this.logger.log("WARN", this.fromContext(message));
|
||||
}
|
||||
|
||||
error(message: string): void {
|
||||
this.logger.log("ERROR", message);
|
||||
this.logger.log("ERROR", this.fromContext(message));
|
||||
}
|
||||
|
||||
private fromContext(msg: string): string {
|
||||
return this.context ? `[${this.context}] ${msg}` : msg;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
*/
|
||||
export default interface LoggerService {
|
||||
|
||||
setContext(newContext: string): void;
|
||||
|
||||
clearContext(): void;
|
||||
|
||||
trace(message: string): void;
|
||||
|
||||
debug(message: string): void;
|
||||
|
||||
@@ -10,6 +10,12 @@ import LoggerService from "@bp/service/logger/logger-service";
|
||||
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
||||
import { inferGitClient, inferGitApiUrl } from "@bp/service/git/git-util";
|
||||
|
||||
interface Git {
|
||||
gitClientType: GitClientType;
|
||||
gitClientApi: GitClient;
|
||||
gitCli: GitCLIService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main runner implementation, it implements the core logic flow
|
||||
*/
|
||||
@@ -55,54 +61,80 @@ export default class Runner {
|
||||
|
||||
// 2. init git service
|
||||
const gitClientType: GitClientType = inferGitClient(args.pullRequest);
|
||||
// right now the apiVersion is set to v4
|
||||
const apiUrl = inferGitApiUrl(args.pullRequest);
|
||||
// the api version is ignored in case of github
|
||||
const apiUrl = inferGitApiUrl(args.pullRequest, gitClientType === GitClientType.CODEBERG ? "v1" : undefined);
|
||||
const gitApi: GitClient = GitClientFactory.getOrCreate(gitClientType, args.auth, apiUrl);
|
||||
|
||||
// 3. parse configs
|
||||
this.logger.debug("Parsing configs..");
|
||||
const configs: Configs = await new PullRequestConfigsParser().parseAndValidate(args);
|
||||
const originalPR: GitPullRequest = configs.originalPullRequest;
|
||||
const backportPR: BackportPullRequest = configs.backportPullRequest;
|
||||
const backportPRs: BackportPullRequest[] = configs.backportPullRequests;
|
||||
|
||||
// start local git operations
|
||||
const git: GitCLIService = new GitCLIService(configs.auth, configs.git);
|
||||
|
||||
const failures: string[] = [];
|
||||
// we need sequential backporting as they will operate on the same folder
|
||||
// avoid cloning the same repo multiple times
|
||||
for(const pr of backportPRs) {
|
||||
try {
|
||||
await this.executeBackport(configs, pr, {
|
||||
gitClientType: gitClientType,
|
||||
gitClientApi: gitApi,
|
||||
gitCli: git,
|
||||
});
|
||||
} catch(error) {
|
||||
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
|
||||
failures.push(error as string);
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
throw new Error(`Failure occurred during one of the backports: [${failures.join(" ; ")}]`);
|
||||
}
|
||||
}
|
||||
|
||||
async executeBackport(configs: Configs, backportPR: BackportPullRequest, git: Git): Promise<void> {
|
||||
this.logger.setContext(backportPR.base);
|
||||
|
||||
const originalPR: GitPullRequest = configs.originalPullRequest;
|
||||
|
||||
// 4. clone the repository
|
||||
this.logger.debug("Cloning repo..");
|
||||
await git.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, configs.targetBranch);
|
||||
await git.gitCli.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, backportPR.base);
|
||||
|
||||
// 5. create new branch from target one and checkout
|
||||
this.logger.debug("Creating local branch..");
|
||||
|
||||
await git.createLocalBranch(configs.folder, backportPR.head);
|
||||
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
|
||||
|
||||
// 6. fetch pull request remote if source owner != target owner or pull request still open
|
||||
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
|
||||
configs.originalPullRequest.state === "open") {
|
||||
this.logger.debug("Fetching pull request remote..");
|
||||
const prefix = gitClientType === GitClientType.GITHUB ? "pull" : "merge-requests"; // default is for gitlab
|
||||
await git.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||
const prefix = git.gitClientType === GitClientType.GITLAB ? "merge-requests" : "pull" ; // default is for gitlab
|
||||
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||
}
|
||||
|
||||
// 7. apply all changes to the new branch
|
||||
this.logger.debug("Cherry picking commits..");
|
||||
for (const sha of originalPR.commits!) {
|
||||
await git.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption);
|
||||
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption);
|
||||
}
|
||||
|
||||
if (!configs.dryRun) {
|
||||
// 8. push the new branch to origin
|
||||
await git.push(configs.folder, backportPR.head);
|
||||
await git.gitCli.push(configs.folder, backportPR.head);
|
||||
|
||||
// 9. create pull request new branch -> target branch (using octokit)
|
||||
const prUrl = await gitApi.createPullRequest(backportPR);
|
||||
const prUrl = await git.gitClientApi.createPullRequest(backportPR);
|
||||
this.logger.info(`Pull request created: ${prUrl}`);
|
||||
|
||||
} else {
|
||||
this.logger.warn("Pull request creation and remote push skipped");
|
||||
this.logger.info(`${JSON.stringify(backportPR, null, 2)}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.clearContext();
|
||||
}
|
||||
}
|
||||
@@ -433,4 +433,63 @@ describe("cli args parser", () => {
|
||||
expect(args.squash).toEqual(true);
|
||||
expectArrayEqual(args.comments!,["first comment", "second comment"]);
|
||||
});
|
||||
|
||||
test("valid execution with multiple branches", () => {
|
||||
addProcessArgs([
|
||||
"-tb",
|
||||
"target, old",
|
||||
"-pr",
|
||||
"https://localhost/whatever/pulls/1"
|
||||
]);
|
||||
|
||||
const args: Args = parser.parse();
|
||||
expect(args.dryRun).toEqual(false);
|
||||
expect(args.auth).toEqual(undefined);
|
||||
expect(args.gitUser).toEqual(undefined);
|
||||
expect(args.gitEmail).toEqual(undefined);
|
||||
expect(args.folder).toEqual(undefined);
|
||||
expect(args.targetBranch).toEqual("target, old");
|
||||
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
|
||||
expect(args.title).toEqual(undefined);
|
||||
expect(args.body).toEqual(undefined);
|
||||
expect(args.bodyPrefix).toEqual(undefined);
|
||||
expect(args.bpBranchName).toEqual(undefined);
|
||||
expect(args.reviewers).toEqual([]);
|
||||
expect(args.assignees).toEqual([]);
|
||||
expect(args.inheritReviewers).toEqual(true);
|
||||
expect(args.labels).toEqual([]);
|
||||
expect(args.inheritLabels).toEqual(false);
|
||||
expect(args.squash).toEqual(true);
|
||||
expect(args.strategy).toEqual(undefined);
|
||||
expect(args.strategyOption).toEqual(undefined);
|
||||
});
|
||||
|
||||
test("invalid execution with empty target branch", () => {
|
||||
addProcessArgs([
|
||||
"-tb",
|
||||
" ",
|
||||
"-pr",
|
||||
"https://localhost/whatever/pulls/1"
|
||||
]);
|
||||
|
||||
expect(() => parser.parse()).toThrowError("Missing option: pull request and target branches must be provided");
|
||||
});
|
||||
|
||||
test("invalid execution with missing mandatory target branch", () => {
|
||||
addProcessArgs([
|
||||
"-pr",
|
||||
"https://localhost/whatever/pulls/1"
|
||||
]);
|
||||
|
||||
expect(() => parser.parse()).toThrowError("Missing option: pull request and target branches must be provided");
|
||||
});
|
||||
|
||||
test("invalid execution with missin mandatory pull request", () => {
|
||||
addProcessArgs([
|
||||
"-tb",
|
||||
"target",
|
||||
]);
|
||||
|
||||
expect(() => parser.parse()).toThrowError("Missing option: pull request and target branches must be provided");
|
||||
});
|
||||
});
|
||||
@@ -262,4 +262,56 @@ describe("gha args parser", () => {
|
||||
expect(args.squash).toEqual(true);
|
||||
expectArrayEqual(args.comments!,["first comment", "second comment"]);
|
||||
});
|
||||
|
||||
test("valid execution with multiple branches", () => {
|
||||
spyGetInput({
|
||||
"target-branch": "target,old",
|
||||
"pull-request": "https://localhost/whatever/pulls/1"
|
||||
});
|
||||
|
||||
const args: Args = parser.parse();
|
||||
expect(args.dryRun).toEqual(false);
|
||||
expect(args.auth).toEqual(undefined);
|
||||
expect(args.gitUser).toEqual(undefined);
|
||||
expect(args.gitEmail).toEqual(undefined);
|
||||
expect(args.folder).toEqual(undefined);
|
||||
expect(args.targetBranch).toEqual("target,old");
|
||||
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
|
||||
expect(args.title).toEqual(undefined);
|
||||
expect(args.body).toEqual(undefined);
|
||||
expect(args.reviewers).toEqual([]);
|
||||
expect(args.assignees).toEqual([]);
|
||||
expect(args.inheritReviewers).toEqual(true);
|
||||
expect(args.labels).toEqual([]);
|
||||
expect(args.inheritLabels).toEqual(false);
|
||||
expect(args.squash).toEqual(true);
|
||||
expect(args.strategy).toEqual(undefined);
|
||||
expect(args.strategyOption).toEqual(undefined);
|
||||
});
|
||||
|
||||
|
||||
test("invalid execution with empty target branch", () => {
|
||||
spyGetInput({
|
||||
"target-branch": " ",
|
||||
"pull-request": "https://localhost/whatever/pulls/1"
|
||||
});
|
||||
|
||||
expect(() => parser.parse()).toThrowError("Missing option: pull request and target branches must be provided");
|
||||
});
|
||||
|
||||
test("invalid execution with missing mandatory target branch", () => {
|
||||
spyGetInput({
|
||||
"pull-request": "https://localhost/whatever/pulls/1"
|
||||
});
|
||||
|
||||
expect(() => parser.parse()).toThrowError("Missing option: pull request and target branches must be provided");
|
||||
});
|
||||
|
||||
test("invalid execution with missin mandatory pull request", () => {
|
||||
spyGetInput({
|
||||
"target-branch": "target,old",
|
||||
});
|
||||
|
||||
expect(() => parser.parse()).toThrowError("Missing option: pull request and target branches must be provided");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,425 @@
|
||||
import { Args } from "@bp/service/args/args.types";
|
||||
import { Configs } from "@bp/service/configs/configs.types";
|
||||
import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs-parser";
|
||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||
import { GitClientType } from "@bp/service/git/git.types";
|
||||
import { mockGitHubClient } from "../../../support/mock/git-client-mock-support";
|
||||
import { resetEnvTokens, resetProcessArgs } from "../../../support/utils";
|
||||
import { MERGED_PR_FIXTURE, REPO, TARGET_OWNER, MULT_COMMITS_PR_FIXTURE } from "../../../support/mock/github-data";
|
||||
import GitHubMapper from "@bp/service/git/github/github-mapper";
|
||||
import GitHubClient from "@bp/service/git/github/github-client";
|
||||
|
||||
jest.spyOn(GitHubMapper.prototype, "mapPullRequest");
|
||||
jest.spyOn(GitHubClient.prototype, "getPullRequest");
|
||||
|
||||
describe("github pull request config parser", () => {
|
||||
|
||||
const mergedPRUrl = `https://github.com/${TARGET_OWNER}/${REPO}/pull/${MERGED_PR_FIXTURE.number}`;
|
||||
const multipleCommitsPRUrl = `https://github.com/${TARGET_OWNER}/${REPO}/pull/${MULT_COMMITS_PR_FIXTURE.number}`;
|
||||
|
||||
let configParser: PullRequestConfigsParser;
|
||||
|
||||
beforeAll(() => {
|
||||
GitClientFactory.reset();
|
||||
GitClientFactory.getOrCreate(GitClientType.GITHUB, "whatever", "http://localhost/api/v3");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// reset process.env variables
|
||||
resetProcessArgs();
|
||||
|
||||
// reset env tokens
|
||||
resetEnvTokens();
|
||||
|
||||
// mock octokit
|
||||
mockGitHubClient("http://localhost/api/v3");
|
||||
|
||||
// create a fresh new instance every time
|
||||
configParser = new PullRequestConfigsParser();
|
||||
});
|
||||
|
||||
test("multiple backports", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v1-28f63db",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v2-28f63db",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v3-28f63db",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports ignore duplicates", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v1-28f63db",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v2-28f63db",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v3-28f63db",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports with custom branch name", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
bpBranchName: "custom-branch",
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch-v1",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch-v2",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch-v3",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports with multiple custom branch names", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
bpBranchName: "custom-branch1, custom-branch2, custom-branch3",
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch1",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch2",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch3",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports with incorrect number of bp branch names", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
bpBranchName: "custom-branch1, custom-branch2",
|
||||
};
|
||||
|
||||
await expect(() => configParser.parseAndValidate(args)).rejects.toThrow("The number of backport branch names, if provided, must match the number of target branches or just one, provided 2 branch names instead");
|
||||
});
|
||||
|
||||
test("multiple backports and multiple commits", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: multipleCommitsPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "GitHub",
|
||||
gitEmail: "noreply@github.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
squash: false,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 8632, false);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), ["0404fb922ab75c3a8aecad5c97d9af388df04695", "11da4e38aa3e577ffde6d546f1c52e53b04d3151"]);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.git).toEqual({
|
||||
user: "GitHub",
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v1-0404fb9-11da4e3",
|
||||
base: "v1",
|
||||
title: "[v1] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/8632\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v2-0404fb9-11da4e3",
|
||||
base: "v2",
|
||||
title: "[v2] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/8632\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v3-0404fb9-11da4e3",
|
||||
base: "v3",
|
||||
title: "[v3] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/8632\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Args } from "@bp/service/args/args.types";
|
||||
import { Configs } from "@bp/service/configs/configs.types";
|
||||
import { AuthTokenId, Configs } from "@bp/service/configs/configs.types";
|
||||
import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs-parser";
|
||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||
import { GitClientType } from "@bp/service/git/git.types";
|
||||
import { mockGitHubClient } from "../../../support/mock/git-client-mock-support";
|
||||
import { addProcessArgs, createTestFile, removeTestFile, resetProcessArgs } from "../../../support/utils";
|
||||
import { mergedPullRequestFixture, openPullRequestFixture, notMergedPullRequestFixture, repo, targetOwner, multipleCommitsPullRequestFixture } from "../../../support/mock/github-data";
|
||||
import { addProcessArgs, createTestFile, removeTestFile, resetEnvTokens, resetProcessArgs } from "../../../support/utils";
|
||||
import { MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, REPO, TARGET_OWNER, MULT_COMMITS_PR_FIXTURE } from "../../../support/mock/github-data";
|
||||
import CLIArgsParser from "@bp/service/args/cli/cli-args-parser";
|
||||
import GitHubMapper from "@bp/service/git/github/github-mapper";
|
||||
import GitHubClient from "@bp/service/git/github/github-client";
|
||||
@@ -13,14 +13,14 @@ import GitHubClient from "@bp/service/git/github/github-client";
|
||||
const GITHUB_MERGED_PR_SIMPLE_CONFIG_FILE_CONTENT_PATHNAME = "./github-pr-configs-parser-simple-pr-merged.json";
|
||||
const GITHUB_MERGED_PR_SIMPLE_CONFIG_FILE_CONTENT = {
|
||||
"targetBranch": "prod",
|
||||
"pullRequest": `https://github.com/${targetOwner}/${repo}/pull/${mergedPullRequestFixture.number}`,
|
||||
"pullRequest": `https://github.com/${TARGET_OWNER}/${REPO}/pull/${MERGED_PR_FIXTURE.number}`,
|
||||
};
|
||||
|
||||
const GITHUB_MERGED_PR_COMPLEX_CONFIG_FILE_CONTENT_PATHNAME = "./github-pr-configs-parser-complex-pr-merged.json";
|
||||
const GITHUB_MERGED_PR_COMPLEX_CONFIG_FILE_CONTENT = {
|
||||
"dryRun": false,
|
||||
"auth": "my-auth-token",
|
||||
"pullRequest": `https://github.com/${targetOwner}/${repo}/pull/${mergedPullRequestFixture.number}`,
|
||||
"pullRequest": `https://github.com/${TARGET_OWNER}/${REPO}/pull/${MERGED_PR_FIXTURE.number}`,
|
||||
"targetBranch": "prod",
|
||||
"gitUser": "Me",
|
||||
"gitEmail": "me@email.com",
|
||||
@@ -39,10 +39,10 @@ jest.spyOn(GitHubClient.prototype, "getPullRequest");
|
||||
|
||||
describe("github pull request config parser", () => {
|
||||
|
||||
const mergedPRUrl = `https://github.com/${targetOwner}/${repo}/pull/${mergedPullRequestFixture.number}`;
|
||||
const openPRUrl = `https://github.com/${targetOwner}/${repo}/pull/${openPullRequestFixture.number}`;
|
||||
const notMergedPRUrl = `https://github.com/${targetOwner}/${repo}/pull/${notMergedPullRequestFixture.number}`;
|
||||
const multipleCommitsPRUrl = `https://github.com/${targetOwner}/${repo}/pull/${multipleCommitsPullRequestFixture.number}`;
|
||||
const mergedPRUrl = `https://github.com/${TARGET_OWNER}/${REPO}/pull/${MERGED_PR_FIXTURE.number}`;
|
||||
const openPRUrl = `https://github.com/${TARGET_OWNER}/${REPO}/pull/${OPEN_PR_FIXTURE.number}`;
|
||||
const notMergedPRUrl = `https://github.com/${TARGET_OWNER}/${REPO}/pull/${NOT_MERGED_PR_FIXTURE.number}`;
|
||||
const multipleCommitsPRUrl = `https://github.com/${TARGET_OWNER}/${REPO}/pull/${MULT_COMMITS_PR_FIXTURE.number}`;
|
||||
|
||||
let argsParser: CLIArgsParser;
|
||||
let configParser: PullRequestConfigsParser;
|
||||
@@ -66,6 +66,9 @@ describe("github pull request config parser", () => {
|
||||
// reset process.env variables
|
||||
resetProcessArgs();
|
||||
|
||||
// reset env tokens
|
||||
resetEnvTokens();
|
||||
|
||||
// mock octokit
|
||||
mockGitHubClient("http://localhost/api/v3");
|
||||
|
||||
@@ -77,7 +80,6 @@ describe("github pull request config parser", () => {
|
||||
test("parse configs from pull request", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
gitUser: "GitHub",
|
||||
@@ -99,8 +101,7 @@ describe("github pull request config parser", () => {
|
||||
user: "GitHub",
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.auth).toEqual(undefined);
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -128,7 +129,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -160,7 +162,6 @@ describe("github pull request config parser", () => {
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "GitHub",
|
||||
@@ -190,7 +191,6 @@ describe("github pull request config parser", () => {
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.git).toEqual({
|
||||
user: "GitHub",
|
||||
email: "noreply@github.com"
|
||||
@@ -271,7 +271,6 @@ describe("github pull request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -300,7 +299,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"],
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-branch",
|
||||
@@ -314,6 +314,48 @@ describe("github pull request config parser", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("override backport with empty bp branch name", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
bpBranchName: " "
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
base: "prod",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
});
|
||||
|
||||
test("override backport pr reviewers and assignees", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
@@ -343,7 +385,6 @@ describe("github pull request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -372,7 +413,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"],
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -415,7 +457,6 @@ describe("github pull request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -444,7 +485,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"],
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -489,7 +531,6 @@ describe("github pull request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -518,7 +559,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"],
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -552,7 +594,6 @@ describe("github pull request config parser", () => {
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
expect(configs.auth).toEqual(undefined);
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -580,7 +621,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -614,7 +656,6 @@ describe("github pull request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("my-auth-token");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -643,7 +684,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"],
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -684,7 +726,6 @@ describe("github pull request config parser", () => {
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 8632,
|
||||
@@ -712,7 +753,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["0404fb922ab75c3a8aecad5c97d9af388df04695", "11da4e38aa3e577ffde6d546f1c52e53b04d3151"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-0404fb9-11da4e3",
|
||||
@@ -758,7 +800,6 @@ describe("github pull request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 2368,
|
||||
@@ -787,7 +828,8 @@ describe("github pull request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"],
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-prod-28f63db",
|
||||
@@ -800,4 +842,82 @@ describe("github pull request config parser", () => {
|
||||
comments: ["First comment", "Second comment"],
|
||||
});
|
||||
});
|
||||
|
||||
test("override token using auth arg", async () => {
|
||||
process.env[AuthTokenId.GITHUB_TOKEN] = "mygithubtoken";
|
||||
const args: Args = {
|
||||
dryRun: true,
|
||||
auth: "whatever",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
folder: "/tmp/test",
|
||||
gitUser: "GitHub",
|
||||
gitEmail: "noreply@github.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "GitHub",
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
});
|
||||
|
||||
test("auth using GITHUB_TOKEN has precedence over GIT_TOKEN env variable", async () => {
|
||||
process.env[AuthTokenId.GIT_TOKEN] = "mygittoken";
|
||||
process.env[AuthTokenId.GITHUB_TOKEN] = "mygithubtoken";
|
||||
const args: Args = {
|
||||
dryRun: true,
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
folder: "/tmp/test",
|
||||
gitUser: "GitHub",
|
||||
gitEmail: "noreply@github.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("mygithubtoken");
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "GitHub",
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
});
|
||||
|
||||
test("ignore env variables related to other git platforms", async () => {
|
||||
process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken";
|
||||
process.env[AuthTokenId.CODEBERG_TOKEN] = "mycodebergtoken";
|
||||
const args: Args = {
|
||||
dryRun: true,
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
folder: "/tmp/test",
|
||||
gitUser: "GitHub",
|
||||
gitEmail: "noreply@github.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual(undefined);
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "GitHub",
|
||||
email: "noreply@github.com"
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,353 @@
|
||||
import { Args } from "@bp/service/args/args.types";
|
||||
import { Configs } from "@bp/service/configs/configs.types";
|
||||
import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs-parser";
|
||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||
import { GitClientType } from "@bp/service/git/git.types";
|
||||
import { getAxiosMocked } from "../../../support/mock/git-client-mock-support";
|
||||
import { MERGED_SQUASHED_MR } from "../../../support/mock/gitlab-data";
|
||||
import GitLabClient from "@bp/service/git/gitlab/gitlab-client";
|
||||
import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper";
|
||||
import { resetEnvTokens } from "../../../support/utils";
|
||||
|
||||
jest.spyOn(GitLabMapper.prototype, "mapPullRequest");
|
||||
jest.spyOn(GitLabClient.prototype, "getPullRequest");
|
||||
|
||||
jest.mock("axios", () => {
|
||||
return {
|
||||
create: jest.fn(() => ({
|
||||
get: getAxiosMocked,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
describe("gitlab merge request config parser", () => {
|
||||
|
||||
const mergedPRUrl = `https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/${MERGED_SQUASHED_MR.iid}`;
|
||||
|
||||
let configParser: PullRequestConfigsParser;
|
||||
|
||||
beforeAll(() => {
|
||||
GitClientFactory.reset();
|
||||
GitClientFactory.getOrCreate(GitClientType.GITLAB, "whatever", "my.gitlab.host.com");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// reset env tokens
|
||||
resetEnvTokens();
|
||||
|
||||
configParser = new PullRequestConfigsParser();
|
||||
});
|
||||
|
||||
test("multiple backports", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-v1-ebb1eca",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-v2-ebb1eca",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-v3-ebb1eca",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
}
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports ignore duplicates", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3, v1",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-v1-ebb1eca",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-v2-ebb1eca",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-v3-ebb1eca",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
}
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports with custom branch name", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
bpBranchName: "custom-branch"
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "custom-branch-v1",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "custom-branch-v2",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "custom-branch-v3",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
}
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports with multiple custom branch names", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
bpBranchName: "custom1, custom2, custom3"
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(false);
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.backportPullRequests.length).toEqual(3);
|
||||
expect(configs.backportPullRequests).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "custom1",
|
||||
base: "v1",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "custom2",
|
||||
base: "v2",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
},
|
||||
{
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "custom3",
|
||||
base: "v3",
|
||||
title: "New Title",
|
||||
body: "New Body Prefix -New Body",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
labels: [],
|
||||
comments: [],
|
||||
}
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("multiple backports with incorrect number of bp branch names", async () => {
|
||||
const args: Args = {
|
||||
dryRun: false,
|
||||
auth: "",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "v1, v2, v3",
|
||||
gitUser: "Me",
|
||||
gitEmail: "me@email.com",
|
||||
title: "New Title",
|
||||
body: "New Body",
|
||||
bodyPrefix: "New Body Prefix -",
|
||||
reviewers: [],
|
||||
assignees: ["user3", "user4"],
|
||||
inheritReviewers: false,
|
||||
labels: [],
|
||||
inheritLabels: false,
|
||||
comments: [],
|
||||
bpBranchName: "custom-branch1, custom-branch2, custom-branch2, custom-branch3, custom-branch4",
|
||||
};
|
||||
|
||||
await expect(() => configParser.parseAndValidate(args)).rejects.toThrow("The number of backport branch names, if provided, must match the number of target branches or just one, provided 4 branch names instead");
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Args } from "@bp/service/args/args.types";
|
||||
import { Configs } from "@bp/service/configs/configs.types";
|
||||
import { AuthTokenId, Configs } from "@bp/service/configs/configs.types";
|
||||
import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs-parser";
|
||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||
import { GitClientType } from "@bp/service/git/git.types";
|
||||
import { getAxiosMocked } from "../../../support/mock/git-client-mock-support";
|
||||
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, OPEN_MR } from "../../../support/mock/gitlab-data";
|
||||
import GHAArgsParser from "@bp/service/args/gha/gha-args-parser";
|
||||
import { createTestFile, removeTestFile, spyGetInput } from "../../../support/utils";
|
||||
import { createTestFile, removeTestFile, resetEnvTokens, spyGetInput } from "../../../support/utils";
|
||||
import GitLabClient from "@bp/service/git/gitlab/gitlab-client";
|
||||
import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper";
|
||||
|
||||
@@ -70,6 +70,9 @@ describe("gitlab merge request config parser", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// reset env tokens
|
||||
resetEnvTokens();
|
||||
|
||||
argsParser = new GHAArgsParser();
|
||||
configParser = new PullRequestConfigsParser();
|
||||
});
|
||||
@@ -100,7 +103,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "noreply@gitlab.com"
|
||||
});
|
||||
expect(configs.auth).toEqual(undefined);
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -128,7 +130,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -166,7 +169,6 @@ describe("gitlab merge request config parser", () => {
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "Gitlab",
|
||||
@@ -196,7 +198,6 @@ describe("gitlab merge request config parser", () => {
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.git).toEqual({
|
||||
user: "Gitlab",
|
||||
email: "noreply@gitlab.com"
|
||||
@@ -276,7 +277,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -304,7 +304,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -347,7 +348,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -375,7 +375,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -418,7 +419,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -446,7 +446,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -491,7 +492,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -519,7 +519,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -552,7 +553,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "noreply@gitlab.com"
|
||||
});
|
||||
expect(configs.auth).toEqual(undefined);
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -580,7 +580,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -613,7 +614,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("my-token");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -641,7 +641,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -678,7 +679,6 @@ describe("gitlab merge request config parser", () => {
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.git).toEqual({
|
||||
user: "Gitlab",
|
||||
email: "noreply@gitlab.com"
|
||||
@@ -710,7 +710,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 2,
|
||||
commits: ["e4dd336a4a20f394df6665994df382fb1d193a11", "974519f65c9e0ed65277cd71026657a09fca05e7"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-e4dd336-974519f",
|
||||
@@ -756,7 +757,6 @@ describe("gitlab merge request config parser", () => {
|
||||
email: "me@email.com"
|
||||
});
|
||||
expect(configs.auth).toEqual("");
|
||||
expect(configs.targetBranch).toEqual("prod");
|
||||
expect(configs.folder).toEqual(process.cwd() + "/bp");
|
||||
expect(configs.originalPullRequest).toEqual({
|
||||
number: 1,
|
||||
@@ -784,7 +784,8 @@ describe("gitlab merge request config parser", () => {
|
||||
nCommits: 1,
|
||||
commits: ["ebb1eca696c42fd067658bd9b5267709f78ef38e"]
|
||||
});
|
||||
expect(configs.backportPullRequest).toEqual({
|
||||
expect(configs.backportPullRequests.length).toEqual(1);
|
||||
expect(configs.backportPullRequests[0]).toEqual({
|
||||
owner: "superuser",
|
||||
repo: "backporting-example",
|
||||
head: "bp-prod-ebb1eca",
|
||||
@@ -797,4 +798,97 @@ describe("gitlab merge request config parser", () => {
|
||||
comments: ["First comment", "Second comment"],
|
||||
});
|
||||
});
|
||||
|
||||
test("override token using auth arg", async () => {
|
||||
process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken";
|
||||
const args: Args = {
|
||||
dryRun: true,
|
||||
auth: "whatever",
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
folder: "/tmp/test",
|
||||
gitUser: "Gitlab",
|
||||
gitEmail: "noreply@gitlab.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("whatever");
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "Gitlab",
|
||||
email: "noreply@gitlab.com"
|
||||
});
|
||||
});
|
||||
|
||||
test("auth using GITLAB_TOKEN has precedence over GIT_TOKEN env variable", async () => {
|
||||
process.env[AuthTokenId.GIT_TOKEN] = "mygittoken";
|
||||
process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken";
|
||||
const args: Args = {
|
||||
dryRun: true,
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
folder: "/tmp/test",
|
||||
gitUser: "Gitlab",
|
||||
gitEmail: "noreply@gitlab.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual("mygitlabtoken");
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "Gitlab",
|
||||
email: "noreply@gitlab.com"
|
||||
});
|
||||
});
|
||||
|
||||
test("ignore env variables related to other git platforms", async () => {
|
||||
process.env[AuthTokenId.CODEBERG_TOKEN] = "mycodebergtoken";
|
||||
process.env[AuthTokenId.GITHUB_TOKEN] = "mygithubtoken";
|
||||
const args: Args = {
|
||||
dryRun: true,
|
||||
pullRequest: mergedPRUrl,
|
||||
targetBranch: "prod",
|
||||
folder: "/tmp/test",
|
||||
gitUser: "Gitlab",
|
||||
gitEmail: "noreply@gitlab.com",
|
||||
reviewers: [],
|
||||
assignees: [],
|
||||
inheritReviewers: true,
|
||||
};
|
||||
|
||||
const configs: Configs = await configParser.parseAndValidate(args);
|
||||
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||
|
||||
expect(configs.dryRun).toEqual(true);
|
||||
expect(configs.auth).toEqual(undefined);
|
||||
expect(configs.folder).toEqual("/tmp/test");
|
||||
expect(configs.git).toEqual({
|
||||
user: "Gitlab",
|
||||
email: "noreply@gitlab.com"
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -114,4 +114,13 @@ describe("git cli service", () => {
|
||||
const output = spawnSync("git", ["cherry", "-v"], { cwd }).stdout.toString();
|
||||
expect(output.includes(expressionToTest)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
test("git clone on already created repo", async () => {
|
||||
await git.clone("remote", cwd, "tbranch");
|
||||
|
||||
// use rev-parse to double check the current branch is the expected one
|
||||
const post = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd }).stdout.toString().trim();
|
||||
expect(post).toEqual("tbranch");
|
||||
});
|
||||
});
|
||||
@@ -20,6 +20,11 @@ describe("git client factory test", () => {
|
||||
expect(client).toBeInstanceOf(GitLabClient);
|
||||
});
|
||||
|
||||
test("correctly create codeberg client", () => {
|
||||
const client = GitClientFactory.getOrCreate(GitClientType.CODEBERG, "auth", "apiurl");
|
||||
expect(client).toBeInstanceOf(GitHubClient);
|
||||
});
|
||||
|
||||
test("check get service github", () => {
|
||||
const create = GitClientFactory.getOrCreate(GitClientType.GITHUB, "auth", "apiurl");
|
||||
const get = GitClientFactory.getClient();
|
||||
@@ -31,4 +36,10 @@ describe("git client factory test", () => {
|
||||
const get = GitClientFactory.getClient();
|
||||
expect(create).toStrictEqual(get);
|
||||
});
|
||||
|
||||
test("check get service codeberg", () => {
|
||||
const create = GitClientFactory.getOrCreate(GitClientType.CODEBERG, "auth", "apiurl");
|
||||
const get = GitClientFactory.getClient();
|
||||
expect(create).toStrictEqual(get);
|
||||
});
|
||||
});
|
||||
@@ -23,6 +23,18 @@ describe("check git utilities", () => {
|
||||
expect(inferGitApiUrl("http://github.acme-inc.com/superuser/backporting-example/pull/4", "v3")).toStrictEqual("http://github.acme-inc.com/api/v3");
|
||||
});
|
||||
|
||||
test("check infer github api from github api url", ()=> {
|
||||
expect(inferGitApiUrl("https://api.github.com/repos/owner/repo/pulls/1")).toStrictEqual("https://api.github.com");
|
||||
});
|
||||
|
||||
test("check infer codeberg api", ()=> {
|
||||
expect(inferGitApiUrl("https://codeberg.org/lampajr/backporting-example/pulls/1", "v1")).toStrictEqual("https://codeberg.org/api/v1");
|
||||
});
|
||||
|
||||
test("check infer codeberg api", ()=> {
|
||||
expect(inferGitApiUrl("https://codeberg.org/lampajr/backporting-example/pulls/1", undefined)).toStrictEqual("https://codeberg.org/api/v4");
|
||||
});
|
||||
|
||||
test("check infer github client", ()=> {
|
||||
expect(inferGitClient("https://github.com/superuser/backporting-example/pull/4")).toStrictEqual(GitClientType.GITHUB);
|
||||
});
|
||||
@@ -39,7 +51,7 @@ describe("check git utilities", () => {
|
||||
expect(inferGitClient("https://api.github.com/repos/owner/repo/pulls/1")).toStrictEqual(GitClientType.GITHUB);
|
||||
});
|
||||
|
||||
test("check infer github api from github api url", ()=> {
|
||||
expect(inferGitApiUrl("https://api.github.com/repos/owner/repo/pulls/1")).toStrictEqual("https://api.github.com");
|
||||
test("check infer codeberg client", ()=> {
|
||||
expect(inferGitClient("https://codeberg.org/lampajr/backporting-example/pulls/1")).toStrictEqual(GitClientType.CODEBERG);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||
import { GitPullRequest, GitClientType } from "@bp/service/git/git.types";
|
||||
import GitHubClient from "@bp/service/git/github/github-client";
|
||||
import { mergedPullRequestFixture, repo, targetOwner } from "../../../support/mock/github-data";
|
||||
import { MERGED_PR_FIXTURE, REPO, TARGET_OWNER } from "../../../support/mock/github-data";
|
||||
import { mockGitHubClient } from "../../../support/mock/git-client-mock-support";
|
||||
|
||||
describe("github service", () => {
|
||||
@@ -22,7 +22,7 @@ describe("github service", () => {
|
||||
});
|
||||
|
||||
test("get pull request: success", async () => {
|
||||
const res: GitPullRequest = await gitClient.getPullRequest(targetOwner, repo, mergedPullRequestFixture.number);
|
||||
const res: GitPullRequest = await gitClient.getPullRequest(TARGET_OWNER, REPO, MERGED_PR_FIXTURE.number);
|
||||
expect(res.sourceRepo).toEqual({
|
||||
owner: "fork",
|
||||
project: "reponame",
|
||||
|
||||
@@ -323,4 +323,29 @@ describe("github service", () => {
|
||||
body: "this is second comment",
|
||||
});
|
||||
});
|
||||
|
||||
test("get pull request for nested namespaces", async () => {
|
||||
const res: GitPullRequest = await gitClient.getPullRequestFromUrl("https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/merge_requests/4");
|
||||
|
||||
// check content
|
||||
expect(res.sourceRepo).toEqual({
|
||||
owner: "superuser",
|
||||
project: "backporting-example",
|
||||
cloneUrl: "https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git"
|
||||
});
|
||||
expect(res.targetRepo).toEqual({
|
||||
owner: "superuser",
|
||||
project: "backporting-example",
|
||||
cloneUrl: "https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git"
|
||||
});
|
||||
expect(res.title).toBe("Update test.txt");
|
||||
expect(res.commits!.length).toBe(1);
|
||||
expect(res.commits).toEqual(["ebb1eca696c42fd067658bd9b5267709f78ef38e"]);
|
||||
|
||||
// check axios invocation
|
||||
expect(axiosInstanceSpy.get).toBeCalledTimes(3); // merge request and 2 repos
|
||||
expect(axiosInstanceSpy.get).toBeCalledWith("/projects/mysuperorg%2F6%2Fmysuperproduct%2Fmysuperunit%2Fbackporting-example/merge_requests/4");
|
||||
expect(axiosInstanceSpy.get).toBeCalledWith("/projects/1645");
|
||||
expect(axiosInstanceSpy.get).toBeCalledWith("/projects/1645");
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import CLIArgsParser from "@bp/service/args/cli/cli-args-parser";
|
||||
import { addProcessArgs, createTestFile, removeTestFile, resetProcessArgs } from "../../support/utils";
|
||||
import { mockGitHubClient } from "../../support/mock/git-client-mock-support";
|
||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||
import { GitClientType } from "@bp/service/git/git.types";
|
||||
import { BackportPullRequest, GitClientType } from "@bp/service/git/git.types";
|
||||
|
||||
const GITHUB_MERGED_PR_W_OVERRIDES_CONFIG_FILE_CONTENT_PATHNAME = "./cli-github-runner-pr-merged-with-overrides.json";
|
||||
const GITHUB_MERGED_PR_W_OVERRIDES_CONFIG_FILE_CONTENT = {
|
||||
@@ -236,6 +236,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("same owner", async () => {
|
||||
@@ -281,6 +282,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("closed and not merged pull request", async () => {
|
||||
@@ -338,6 +340,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("override backporting pr data", async () => {
|
||||
@@ -396,6 +399,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("set empty reviewers", async () => {
|
||||
@@ -453,6 +457,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("set custom labels with inheritance", async () => {
|
||||
@@ -502,6 +507,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("set custom labels without inheritance", async () => {
|
||||
@@ -550,6 +556,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("using config file with overrides", async () => {
|
||||
@@ -594,6 +601,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
// to check: https://github.com/kiegroup/git-backporting/issues/52
|
||||
@@ -641,6 +649,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("multiple commits pr", async () => {
|
||||
@@ -688,6 +697,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("too long bp branch name", async () => {
|
||||
@@ -741,6 +751,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("multiple commits pr with different strategy", async () => {
|
||||
@@ -792,6 +803,7 @@ describe("cli runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("additional pr comments", async () => {
|
||||
@@ -841,5 +853,255 @@ describe("cli runner", () => {
|
||||
comments: ["first comment", "second comment"],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("with multiple target branches", async () => {
|
||||
addProcessArgs([
|
||||
"-tb",
|
||||
"v1, v2, v3",
|
||||
"-pr",
|
||||
"https://github.com/owner/reponame/pull/2368",
|
||||
"-f",
|
||||
"/tmp/folder"
|
||||
]);
|
||||
|
||||
await runner.execute();
|
||||
|
||||
const cwd = "/tmp/folder";
|
||||
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
||||
|
||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
||||
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-v1-28f63db");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-v2-28f63db");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-v3-28f63db");
|
||||
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
||||
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
|
||||
expect(GitCLIService.prototype.push).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-v1-28f63db");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-v2-28f63db");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-v3-28f63db");
|
||||
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(3);
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v1-28f63db",
|
||||
base: "v1",
|
||||
title: "[v1] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v2-28f63db",
|
||||
base: "v2",
|
||||
title: "[v2] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v3-28f63db",
|
||||
base: "v3",
|
||||
title: "[v3] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(3);
|
||||
});
|
||||
|
||||
test("with multiple target branches and multiple bp names", async () => {
|
||||
addProcessArgs([
|
||||
"-tb",
|
||||
"v1, v2, v3",
|
||||
"-pr",
|
||||
"https://github.com/owner/reponame/pull/2368",
|
||||
"-f",
|
||||
"/tmp/folder",
|
||||
"--bp-branch-name",
|
||||
"custom1, custom1, custom2, custom3",
|
||||
]);
|
||||
|
||||
await runner.execute();
|
||||
|
||||
const cwd = "/tmp/folder";
|
||||
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
||||
|
||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
||||
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom1");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom2");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom3");
|
||||
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
||||
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
|
||||
expect(GitCLIService.prototype.push).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom1");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom2");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom3");
|
||||
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(3);
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom1",
|
||||
base: "v1",
|
||||
title: "[v1] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom2",
|
||||
base: "v2",
|
||||
title: "[v2] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom3",
|
||||
base: "v3",
|
||||
title: "[v3] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(3);
|
||||
});
|
||||
|
||||
test("with multiple target branches and one failure", async () => {
|
||||
jest.spyOn(GitHubClient.prototype, "createPullRequest").mockImplementation((_backport: BackportPullRequest) => {
|
||||
|
||||
throw new Error("Mocked error");
|
||||
});
|
||||
|
||||
addProcessArgs([
|
||||
"-tb",
|
||||
"v1, v2, v3",
|
||||
"-pr",
|
||||
"https://github.com/owner/reponame/pull/2368",
|
||||
"-f",
|
||||
"/tmp/folder",
|
||||
"--bp-branch-name",
|
||||
"custom-failure-head",
|
||||
]);
|
||||
|
||||
await expect(() => runner.execute()).rejects.toThrowError("Failure occurred during one of the backports: [Error: Mocked error ; Error: Mocked error ; Error: Mocked error]");
|
||||
|
||||
const cwd = "/tmp/folder";
|
||||
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
||||
|
||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
||||
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v1");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v2");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v3");
|
||||
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
||||
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
|
||||
expect(GitCLIService.prototype.push).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-failure-head-v1");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-failure-head-v2");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-failure-head-v3");
|
||||
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(3);
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-failure-head-v1",
|
||||
base: "v1",
|
||||
title: "[v1] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-failure-head-v2",
|
||||
base: "v2",
|
||||
title: "[v2] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-failure-head-v3",
|
||||
base: "v3",
|
||||
title: "[v3] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toThrowError();
|
||||
});
|
||||
});
|
||||
@@ -128,6 +128,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("closed and not merged pull request", async () => {
|
||||
@@ -181,6 +182,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("override backporting pr data", async () => {
|
||||
@@ -231,6 +233,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("set empty reviewers", async () => {
|
||||
@@ -282,6 +285,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("set custom labels with inheritance", async () => {
|
||||
@@ -328,6 +332,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("set custom labels without inheritance", async () => {
|
||||
@@ -374,6 +379,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("using config file with overrides", async () => {
|
||||
@@ -417,6 +423,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
// to check: https://github.com/kiegroup/git-backporting/issues/52
|
||||
@@ -462,6 +469,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("multiple commits pr", async () => {
|
||||
@@ -507,6 +515,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("using github api url and different strategy", async () => {
|
||||
@@ -553,6 +562,7 @@ describe("gha runner", () => {
|
||||
comments: [],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("additional pr comments", async () => {
|
||||
@@ -598,5 +608,161 @@ describe("gha runner", () => {
|
||||
comments: ["first comment", "second comment"],
|
||||
}
|
||||
);
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||
});
|
||||
|
||||
test("with multiple target branches", async () => {
|
||||
spyGetInput({
|
||||
"target-branch": "v1, v2, v3",
|
||||
"pull-request": "https://github.com/owner/reponame/pull/2368",
|
||||
"folder": "/tmp/folder",
|
||||
});
|
||||
|
||||
await runner.execute();
|
||||
|
||||
const cwd = "/tmp/folder";
|
||||
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
||||
|
||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
||||
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-v1-28f63db");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-v2-28f63db");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-v3-28f63db");
|
||||
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
||||
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
|
||||
expect(GitCLIService.prototype.push).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-v1-28f63db");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-v2-28f63db");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-v3-28f63db");
|
||||
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(3);
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v1-28f63db",
|
||||
base: "v1",
|
||||
title: "[v1] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v2-28f63db",
|
||||
base: "v2",
|
||||
title: "[v2] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "bp-v3-28f63db",
|
||||
base: "v3",
|
||||
title: "[v3] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(3);
|
||||
});
|
||||
|
||||
test("with multiple target branches and single custom bp branch", async () => {
|
||||
spyGetInput({
|
||||
"target-branch": "v1, v2, v3",
|
||||
"pull-request": "https://github.com/owner/reponame/pull/2368",
|
||||
"folder": "/tmp/folder",
|
||||
"bp-branch-name": "custom"
|
||||
});
|
||||
|
||||
await runner.execute();
|
||||
|
||||
const cwd = "/tmp/folder";
|
||||
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
||||
|
||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
||||
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-v1");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-v2");
|
||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-v3");
|
||||
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
||||
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined);
|
||||
|
||||
expect(GitCLIService.prototype.push).toBeCalledTimes(3);
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-v1");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-v2");
|
||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-v3");
|
||||
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(3);
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-v1",
|
||||
base: "v1",
|
||||
title: "[v1] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-v2",
|
||||
base: "v2",
|
||||
title: "[v2] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
||||
owner: "owner",
|
||||
repo: "reponame",
|
||||
head: "custom-v3",
|
||||
base: "v3",
|
||||
title: "[v3] PR Title",
|
||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
||||
reviewers: ["gh-user", "that-s-a-user"],
|
||||
assignees: [],
|
||||
labels: [],
|
||||
comments: [],
|
||||
});
|
||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(3);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,11 @@
|
||||
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
||||
import { Moctokit } from "@kie/mock-github";
|
||||
import { targetOwner, repo, mergedPullRequestFixture, openPullRequestFixture, notMergedPullRequestFixture, notFoundPullRequestNumber, multipleCommitsPullRequestFixture, multipleCommitsPullRequestCommits } from "./github-data";
|
||||
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, SUPERUSER} from "./gitlab-data";
|
||||
import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER } from "./github-data";
|
||||
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER} from "./gitlab-data";
|
||||
|
||||
// high number, for each test we are not expecting
|
||||
// to send more than 3 reqs per api endpoint
|
||||
const REPEAT = 20;
|
||||
|
||||
const logger = LoggerServiceFactory.getLogger();
|
||||
|
||||
@@ -18,8 +22,12 @@ export const getAxiosMocked = (url: string) => {
|
||||
data = OPEN_MR;
|
||||
} else if (url.endsWith("merge_requests/3")) {
|
||||
data = CLOSED_NOT_MERGED_MR;
|
||||
} else if (url.endsWith("merge_requests/4")) {
|
||||
data = NESTED_NAMESPACE_MR;
|
||||
} else if (url.endsWith("projects/76316")) {
|
||||
data = PROJECT_EXAMPLE;
|
||||
} else if (url.endsWith("projects/1645")) {
|
||||
data = NESTED_PROJECT_EXAMPLE;
|
||||
} else if (url.endsWith("users?username=superuser")) {
|
||||
data = [SUPERUSER];
|
||||
} else if (url.endsWith("merge_requests/2/commits")) {
|
||||
@@ -94,76 +102,82 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||
// valid requests
|
||||
mock.rest.pulls
|
||||
.get({
|
||||
owner: targetOwner,
|
||||
repo: repo,
|
||||
pull_number: mergedPullRequestFixture.number
|
||||
owner: TARGET_OWNER,
|
||||
repo: REPO,
|
||||
pull_number: MERGED_PR_FIXTURE.number
|
||||
})
|
||||
.reply({
|
||||
status: 200,
|
||||
data: mergedPullRequestFixture
|
||||
data: MERGED_PR_FIXTURE
|
||||
});
|
||||
|
||||
mock.rest.pulls
|
||||
.get({
|
||||
owner: targetOwner,
|
||||
repo: repo,
|
||||
pull_number: multipleCommitsPullRequestFixture.number
|
||||
owner: TARGET_OWNER,
|
||||
repo: REPO,
|
||||
pull_number: MULT_COMMITS_PR_FIXTURE.number
|
||||
})
|
||||
.reply({
|
||||
status: 200,
|
||||
data: multipleCommitsPullRequestFixture
|
||||
data: MULT_COMMITS_PR_FIXTURE
|
||||
});
|
||||
|
||||
mock.rest.pulls
|
||||
.get({
|
||||
owner: targetOwner,
|
||||
repo: repo,
|
||||
pull_number: openPullRequestFixture.number
|
||||
owner: TARGET_OWNER,
|
||||
repo: REPO,
|
||||
pull_number: OPEN_PR_FIXTURE.number
|
||||
})
|
||||
.reply({
|
||||
status: 200,
|
||||
data: openPullRequestFixture
|
||||
data: OPEN_PR_FIXTURE
|
||||
});
|
||||
|
||||
mock.rest.pulls
|
||||
.get({
|
||||
owner: targetOwner,
|
||||
repo: repo,
|
||||
pull_number: notMergedPullRequestFixture.number
|
||||
owner: TARGET_OWNER,
|
||||
repo: REPO,
|
||||
pull_number: NOT_MERGED_PR_FIXTURE.number
|
||||
})
|
||||
.reply({
|
||||
status: 200,
|
||||
data: notMergedPullRequestFixture
|
||||
data: NOT_MERGED_PR_FIXTURE
|
||||
});
|
||||
|
||||
mock.rest.pulls
|
||||
.listCommits({
|
||||
owner: targetOwner,
|
||||
repo: repo,
|
||||
pull_number: multipleCommitsPullRequestFixture.number
|
||||
owner: TARGET_OWNER,
|
||||
repo: REPO,
|
||||
pull_number: MULT_COMMITS_PR_FIXTURE.number
|
||||
})
|
||||
.reply({
|
||||
status: 200,
|
||||
data: multipleCommitsPullRequestCommits
|
||||
data: MULT_COMMITS_PR_COMMITS
|
||||
});
|
||||
|
||||
mock.rest.pulls
|
||||
.create()
|
||||
.reply({
|
||||
repeat: REPEAT,
|
||||
status: 201,
|
||||
data: mergedPullRequestFixture
|
||||
data: {
|
||||
number: NEW_PR_NUMBER,
|
||||
html_url: NEW_PR_URL,
|
||||
}
|
||||
});
|
||||
|
||||
mock.rest.pulls
|
||||
.requestReviewers()
|
||||
.reply({
|
||||
repeat: REPEAT,
|
||||
status: 201,
|
||||
data: mergedPullRequestFixture
|
||||
data: MERGED_PR_FIXTURE
|
||||
});
|
||||
|
||||
mock.rest.issues
|
||||
.addAssignees()
|
||||
.reply({
|
||||
repeat: REPEAT,
|
||||
status: 201,
|
||||
data: {}
|
||||
});
|
||||
@@ -171,6 +185,7 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||
mock.rest.issues
|
||||
.addLabels()
|
||||
.reply({
|
||||
repeat: REPEAT,
|
||||
status: 200,
|
||||
data: {}
|
||||
});
|
||||
@@ -178,6 +193,7 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||
mock.rest.issues
|
||||
.createComment()
|
||||
.reply({
|
||||
repeat: REPEAT,
|
||||
status: 201,
|
||||
data: {}
|
||||
});
|
||||
@@ -185,16 +201,17 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||
// invalid requests
|
||||
mock.rest.pulls
|
||||
.get({
|
||||
owner: targetOwner,
|
||||
repo: repo,
|
||||
pull_number: notFoundPullRequestNumber
|
||||
owner: TARGET_OWNER,
|
||||
repo: REPO,
|
||||
pull_number: NOT_FOUND_PR_NUMBER
|
||||
})
|
||||
.reply({
|
||||
repeat: REPEAT,
|
||||
status: 404,
|
||||
data: {
|
||||
message: "Not found"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return mock;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export const targetOwner = "owner";
|
||||
export const sourceOwner = "fork";
|
||||
export const repo = "reponame";
|
||||
export const notFoundPullRequestNumber = 1;
|
||||
export const TARGET_OWNER = "owner";
|
||||
export const SOURCE_OWNER = "fork";
|
||||
export const REPO = "reponame";
|
||||
export const NOT_FOUND_PR_NUMBER = 1;
|
||||
export const NEW_PR_URL = "new_pr_url";
|
||||
export const NEW_PR_NUMBER = 9999;
|
||||
|
||||
export const mergedPullRequestFixture = {
|
||||
export const MERGED_PR_FIXTURE = {
|
||||
"url": "https://api.github.com/repos/owner/reponame/pulls/2368",
|
||||
"id": 1137188271,
|
||||
"node_id": "PR_kwDOABTq6s5DyB2v",
|
||||
@@ -474,7 +476,7 @@ export const mergedPullRequestFixture = {
|
||||
"changed_files": 2
|
||||
};
|
||||
|
||||
export const openPullRequestFixture = {
|
||||
export const OPEN_PR_FIXTURE = {
|
||||
"url": "https://api.github.com/repos/owner/reponame/pulls/4444",
|
||||
"id": 1137188271,
|
||||
"node_id": "PR_kwDOABTq6s5DyB2v",
|
||||
@@ -898,7 +900,7 @@ export const openPullRequestFixture = {
|
||||
"changed_files": 2
|
||||
};
|
||||
|
||||
export const notMergedPullRequestFixture = {
|
||||
export const NOT_MERGED_PR_FIXTURE = {
|
||||
"url": "https://api.github.com/repos/owner/reponame/pulls/6666",
|
||||
"id": 1137188271,
|
||||
"node_id": "PR_kwDOABTq6s5DyB2v",
|
||||
@@ -1341,7 +1343,7 @@ export const notMergedPullRequestFixture = {
|
||||
"changed_files": 2
|
||||
};
|
||||
|
||||
export const multipleCommitsPullRequestFixture = {
|
||||
export const MULT_COMMITS_PR_FIXTURE = {
|
||||
"url": "https://api.github.com/repos/owner/reponame/pulls/8632",
|
||||
"id": 1137188271,
|
||||
"node_id": "PR_kwDOABTq6s5DyB2v",
|
||||
@@ -1804,7 +1806,7 @@ export const multipleCommitsPullRequestFixture = {
|
||||
"changed_files": 2
|
||||
};
|
||||
|
||||
export const multipleCommitsPullRequestCommits = [
|
||||
export const MULT_COMMITS_PR_COMMITS = [
|
||||
{
|
||||
"sha": "0404fb922ab75c3a8aecad5c97d9af388df04695",
|
||||
"node_id": "C_kwDOImgs99oAKDA0MDRmYjkyMmFiNzVjM2E4YWVjYWQ1Yzk3ZDlhZjM4OGRmMDQ2OTU",
|
||||
|
||||
@@ -161,6 +161,166 @@ export const PROJECT_EXAMPLE = {
|
||||
}
|
||||
};
|
||||
|
||||
export const NESTED_PROJECT_EXAMPLE = {
|
||||
"id":1645,
|
||||
"description":null,
|
||||
"name":"Backporting Example",
|
||||
"name_with_namespace":"Super User / Backporting Example",
|
||||
"path":"backporting-example",
|
||||
"path_with_namespace":"mysuperorg/6/mysuperproduct/mysuperunit/backporting-example",
|
||||
"created_at":"2023-06-23T13:45:15.121Z",
|
||||
"default_branch":"main",
|
||||
"tag_list":[
|
||||
|
||||
],
|
||||
"topics":[
|
||||
|
||||
],
|
||||
"ssh_url_to_repo":"git@my.gitlab.host.com:mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git",
|
||||
"http_url_to_repo":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git",
|
||||
"web_url":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example",
|
||||
"readme_url":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/blob/main/README.md",
|
||||
"forks_count":0,
|
||||
"avatar_url":null,
|
||||
"star_count":0,
|
||||
"last_activity_at":"2023-06-28T14:05:42.596Z",
|
||||
"namespace":{
|
||||
"id":70747,
|
||||
"name":"Super User",
|
||||
"path":"superuser",
|
||||
"kind":"user",
|
||||
"full_path":"superuser",
|
||||
"parent_id":null,
|
||||
"avatar_url":"/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
"_links":{
|
||||
"self":"https://my.gitlab.host.com/api/v4/projects/1645",
|
||||
"issues":"https://my.gitlab.host.com/api/v4/projects/1645/issues",
|
||||
"merge_requests":"https://my.gitlab.host.com/api/v4/projects/1645/merge_requests",
|
||||
"repo_branches":"https://my.gitlab.host.com/api/v4/projects/1645/repository/branches",
|
||||
"labels":"https://my.gitlab.host.com/api/v4/projects/1645/labels",
|
||||
"events":"https://my.gitlab.host.com/api/v4/projects/1645/events",
|
||||
"members":"https://my.gitlab.host.com/api/v4/projects/1645/members",
|
||||
"cluster_agents":"https://my.gitlab.host.com/api/v4/projects/1645/cluster_agents"
|
||||
},
|
||||
"packages_enabled":true,
|
||||
"empty_repo":false,
|
||||
"archived":false,
|
||||
"visibility":"private",
|
||||
"owner":{
|
||||
"id":14041,
|
||||
"username":"superuser",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
"resolve_outdated_diff_discussions":false,
|
||||
"container_expiration_policy":{
|
||||
"cadence":"1d",
|
||||
"enabled":false,
|
||||
"keep_n":10,
|
||||
"older_than":"90d",
|
||||
"name_regex":".*",
|
||||
"name_regex_keep":null,
|
||||
"next_run_at":"2023-06-24T13:45:15.167Z"
|
||||
},
|
||||
"issues_enabled":true,
|
||||
"merge_requests_enabled":true,
|
||||
"wiki_enabled":true,
|
||||
"jobs_enabled":true,
|
||||
"snippets_enabled":true,
|
||||
"container_registry_enabled":true,
|
||||
"service_desk_enabled":false,
|
||||
"service_desk_address":null,
|
||||
"can_create_merge_request_in":true,
|
||||
"issues_access_level":"enabled",
|
||||
"repository_access_level":"enabled",
|
||||
"merge_requests_access_level":"enabled",
|
||||
"forking_access_level":"enabled",
|
||||
"wiki_access_level":"enabled",
|
||||
"builds_access_level":"enabled",
|
||||
"snippets_access_level":"enabled",
|
||||
"pages_access_level":"private",
|
||||
"analytics_access_level":"enabled",
|
||||
"container_registry_access_level":"enabled",
|
||||
"security_and_compliance_access_level":"private",
|
||||
"releases_access_level":"enabled",
|
||||
"environments_access_level":"enabled",
|
||||
"feature_flags_access_level":"enabled",
|
||||
"infrastructure_access_level":"enabled",
|
||||
"monitor_access_level":"enabled",
|
||||
"emails_disabled":null,
|
||||
"shared_runners_enabled":true,
|
||||
"lfs_enabled":true,
|
||||
"creator_id":14041,
|
||||
"import_url":null,
|
||||
"import_type":null,
|
||||
"import_status":"none",
|
||||
"import_error":null,
|
||||
"open_issues_count":0,
|
||||
"description_html":"",
|
||||
"updated_at":"2023-06-28T14:05:42.596Z",
|
||||
"ci_default_git_depth":20,
|
||||
"ci_forward_deployment_enabled":true,
|
||||
"ci_job_token_scope_enabled":false,
|
||||
"ci_separated_caches":true,
|
||||
"ci_allow_fork_pipelines_to_run_in_parent_project":true,
|
||||
"build_git_strategy":"fetch",
|
||||
"keep_latest_artifact":true,
|
||||
"restrict_user_defined_variables":false,
|
||||
"runners_token":"TOKEN",
|
||||
"runner_token_expiration_interval":null,
|
||||
"group_runners_enabled":true,
|
||||
"auto_cancel_pending_pipelines":"enabled",
|
||||
"build_timeout":3600,
|
||||
"auto_devops_enabled":false,
|
||||
"auto_devops_deploy_strategy":"continuous",
|
||||
"ci_config_path":"",
|
||||
"public_jobs":true,
|
||||
"shared_with_groups":[
|
||||
|
||||
],
|
||||
"only_allow_merge_if_pipeline_succeeds":false,
|
||||
"allow_merge_on_skipped_pipeline":null,
|
||||
"request_access_enabled":true,
|
||||
"only_allow_merge_if_all_discussions_are_resolved":false,
|
||||
"remove_source_branch_after_merge":true,
|
||||
"printing_merge_request_link_enabled":true,
|
||||
"merge_method":"merge",
|
||||
"squash_option":"default_off",
|
||||
"enforce_auth_checks_on_uploads":true,
|
||||
"suggestion_commit_message":null,
|
||||
"merge_commit_template":null,
|
||||
"squash_commit_template":null,
|
||||
"issue_branch_template":null,
|
||||
"autoclose_referenced_issues":true,
|
||||
"approvals_before_merge":0,
|
||||
"mirror":false,
|
||||
"external_authorization_classification_label":null,
|
||||
"marked_for_deletion_at":null,
|
||||
"marked_for_deletion_on":null,
|
||||
"requirements_enabled":false,
|
||||
"requirements_access_level":"enabled",
|
||||
"security_and_compliance_enabled":true,
|
||||
"compliance_frameworks":[
|
||||
|
||||
],
|
||||
"issues_template":null,
|
||||
"merge_requests_template":null,
|
||||
"merge_pipelines_enabled":false,
|
||||
"merge_trains_enabled":false,
|
||||
"allow_pipeline_trigger_approve_deployment":false,
|
||||
"permissions":{
|
||||
"project_access":{
|
||||
"access_level":50,
|
||||
"notification_level":3
|
||||
},
|
||||
"group_access":null
|
||||
}
|
||||
};
|
||||
|
||||
export const MERGED_SQUASHED_MR = {
|
||||
"id":807106,
|
||||
"iid":1,
|
||||
@@ -580,3 +740,138 @@ export const SUPERUSER = {
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
};
|
||||
|
||||
export const NESTED_NAMESPACE_MR = {
|
||||
"id":807106,
|
||||
"iid":1,
|
||||
"project_id":1645,
|
||||
"title":"Update test.txt",
|
||||
"description":"This is the body",
|
||||
"state":"merged",
|
||||
"created_at":"2023-06-28T14:32:40.943Z",
|
||||
"updated_at":"2023-06-28T14:37:12.108Z",
|
||||
"merged_by":{
|
||||
"id":14041,
|
||||
"username":"superuser",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
"merge_user":{
|
||||
"id":14041,
|
||||
"username":"superuser",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
"merged_at":"2023-06-28T14:37:11.667Z",
|
||||
"closed_by":null,
|
||||
"closed_at":null,
|
||||
"target_branch":"main",
|
||||
"source_branch":"feature",
|
||||
"user_notes_count":0,
|
||||
"upvotes":0,
|
||||
"downvotes":0,
|
||||
"author":{
|
||||
"id":14041,
|
||||
"username":"superuser",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
"assignees":[
|
||||
{
|
||||
"id":14041,
|
||||
"username":"superuser",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
}
|
||||
],
|
||||
"assignee":{
|
||||
"id":14041,
|
||||
"username":"superuser",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
"reviewers":[
|
||||
{
|
||||
"id":1404188,
|
||||
"username":"superuser1",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
},
|
||||
{
|
||||
"id":1404199,
|
||||
"username":"superuser2",
|
||||
"name":"Super User",
|
||||
"state":"active",
|
||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
||||
"web_url":"https://my.gitlab.host.com/superuser"
|
||||
}
|
||||
],
|
||||
"source_project_id":1645,
|
||||
"target_project_id":1645,
|
||||
"labels":[
|
||||
"gitlab-original-label"
|
||||
],
|
||||
"draft":false,
|
||||
"work_in_progress":false,
|
||||
"milestone":null,
|
||||
"merge_when_pipeline_succeeds":false,
|
||||
"merge_status":"can_be_merged",
|
||||
"detailed_merge_status":"not_open",
|
||||
"sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644",
|
||||
"merge_commit_sha":"4d369c3e9a8d1d5b7e56c892a8ab2a7666583ac3",
|
||||
"squash_commit_sha":"ebb1eca696c42fd067658bd9b5267709f78ef38e",
|
||||
"discussion_locked":null,
|
||||
"should_remove_source_branch":true,
|
||||
"force_remove_source_branch":true,
|
||||
"reference":"!2",
|
||||
"references":{
|
||||
"short":"!2",
|
||||
"relative":"!2",
|
||||
"full":"superuser/backporting-example!2"
|
||||
},
|
||||
"web_url":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/merge_requests/4",
|
||||
"time_stats":{
|
||||
"time_estimate":0,
|
||||
"total_time_spent":0,
|
||||
"human_time_estimate":null,
|
||||
"human_total_time_spent":null
|
||||
},
|
||||
"squash":true,
|
||||
"squash_on_merge":true,
|
||||
"task_completion_status":{
|
||||
"count":0,
|
||||
"completed_count":0
|
||||
},
|
||||
"has_conflicts":false,
|
||||
"blocking_discussions_resolved":true,
|
||||
"approvals_before_merge":null,
|
||||
"subscribed":true,
|
||||
"changes_count":"1",
|
||||
"latest_build_started_at":null,
|
||||
"latest_build_finished_at":null,
|
||||
"first_deployed_to_production_at":null,
|
||||
"pipeline":null,
|
||||
"head_pipeline":null,
|
||||
"diff_refs":{
|
||||
"base_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b",
|
||||
"head_sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644",
|
||||
"start_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b"
|
||||
},
|
||||
"merge_error":null,
|
||||
"first_contribution":false,
|
||||
"user":{
|
||||
"can_merge":true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as core from "@actions/core";
|
||||
import { AuthTokenId } from "@bp/service/configs/configs.types";
|
||||
import * as fs from "fs";
|
||||
|
||||
export const addProcessArgs = (args: string[]) => {
|
||||
@@ -9,6 +10,13 @@ export const resetProcessArgs = () => {
|
||||
process.argv = ["node", "backporting"];
|
||||
};
|
||||
|
||||
export const resetEnvTokens = () => {
|
||||
delete process.env[AuthTokenId.GITHUB_TOKEN];
|
||||
delete process.env[AuthTokenId.GITLAB_TOKEN];
|
||||
delete process.env[AuthTokenId.CODEBERG_TOKEN];
|
||||
delete process.env[AuthTokenId.GIT_TOKEN];
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const spyGetInput = (obj: any) => {
|
||||
const mock = jest.spyOn(core, "getInput");
|
||||
|
||||
Reference in New Issue
Block a user